Skip to content

chore(infra): testnet configuration and secrets management - #106

Merged
TOMOKI977 merged 14 commits into
mainfrom
feat/30-testnet-config
Oct 7, 2026
Merged

TOMOKI977 merged 14 commits into
mainfrom
feat/30-testnet-config

Conversation

@TOMOKI977

Copy link
Copy Markdown
Contributor

Closes #30

Summary

Testnet configuration and secrets management for puls3.

  • .env.example lists every variable read by the server, the app (dart-defines) and the scripts, each with a comment. Testnet defaults are the public values from contracts/deployments/testnet.json. Switching to a local network only changes the PULS3_STELLAR_* values.
  • One config file, the root .env, serves the server (set -a; . ../.env; set +a), the app (--dart-define-from-file=../.env) and the scripts. The app gets a new PULS3_API_URL override, and a blank PULS3_ESCROW_CONTRACT falls back to the testnet escrow. Server secrets stay in the git-ignored puls3_server/config/passwords.yaml locally and in Serverpod Cloud when deployed. puls3_server/.env holds only the local Docker passwords.
  • docs/infra/secrets.md: a secret | purpose | where stored | who has access | how to rotate table covering every secret in code plus the planned ones (ADR-0003 agent wallets, server signer, LLM key). It also has the canonical "Run against testnet from a fresh clone" path, which CONTRIBUTING and the READMEs link to.
  • scripts/fund-testnet-accounts.sh <public-key|identity>...: Friendbot XLM plus the scripted test USDC asset (PUSDC) from spike: agent payment rail and wallet custody on Stellar #7, with timeouts and strict error handling. Circle USDC is faucet-only and cannot be scripted.
  • scripts/tests/env-inventory.test.sh keeps code, .env.example, secrets.md and testnet.json in sync.

Acceptance criteria

  • From a fresh clone, cp .env.example .env (testnet values prefilled) plus scripts/setup-local-secrets.sh is enough to run the server and the app against testnet. See "Run against testnet from a fresh clone" in docs/infra/secrets.md.
  • .gitignore covers .env, passwords.yaml and key files (output below).
  • The secret scan passes on the whole repo history.
  • Every secret used anywhere in the code appears in secrets.md (enforced by scripts/tests/env-inventory.test.sh).
  • The funding script funds a new account with XLM and test USDC (account and tx hashes below).
  • Switching from local to testnet needs only env changes, no code changes: the server reads PULS3_STELLAR_*, the app reads PULS3_API_URL / PULS3_ESCROW_CONTRACT.

Verification evidence

$ git check-ignore -v .env puls3_server/.env puls3_server/config/passwords.yaml deployer.key
.gitignore:17:**/.env	.env
.gitignore:17:**/.env	puls3_server/.env
puls3_server/.gitignore:15:config/passwords.yaml	puls3_server/config/passwords.yaml
.gitignore:21:*.key	deployer.key

$ gitleaks git --redact --config=.gitleaks.toml .
no leaks found

$ bash scripts/tests/fund-accounts.test.sh     # stubbed stellar, curl and timeout; no network
passed 86, failed 0
$ bash scripts/tests/env-inventory.test.sh
passed 64, failed 0
$ bash scripts/tests/escrow-deploy.test.sh
passed 144, failed 0
$ bash scripts/tests/seed-preflight.test.sh
passed 52, failed 0

$ cd puls3_flutter && flutter analyze && flutter test
No issues found!
All tests passed! (85)

Live funding run on testnet (scripts/fund-testnet-accounts.sh with a throwaway identity):

  • Account: GCJOMHOLKJJUMFCBC56ZIZTINBAFARFQIWNM4MA4GJ6RJZQI4KH3RIB3
  • Asset: PUSDC:GDJUWQHLXXSKF6AFLMWFZT37WRRIL4VRGW5P7SB3QPW2F4MWPNYCPHEU (SAC CCD5CN7HFEZMSSNL7JCRAMR53ANY2UQYBSCJ744QG3IQIRRT6HWIFFHO)
  • XLM (create account): f9c5e949540dd34981358c5eb3d1223233c14da5e7e7a5dcebf0cdc9909f83d4
  • XLM (top-up): 5acc971ef2f8afb7114905a23eb5f799fd61b237a66f494b996020db226933a3
  • Trustline: ceb062f6a5273c0aa76ac211bc6750ca5a660147ea6277e90725338c4f711434
  • 100 PUSDC: 4b8a653a375d3f3dc5ff424000b7d3fca25573cdcf23c45df1f1142aad5abaf8

Notes for reviewers

  • PUSDC is not on the escrow allow-list (Circle USDC only). It is for funding and direct payments. Escrow jobs on testnet still need Circle USDC from the faucet.
  • defaultEscrowContractAddress is no longer const (a const can't trim a blank define), so HirePaymentView now takes a nullable escrowContractAddress and shows the testnet default when it is null.
  • Never put secrets in the root .env: the app reads it with --dart-define-from-file. Today only PULS3_API_URL and PULS3_ESCROW_CONTRACT are compiled into the app, and both are public.
  • Out of scope: mainnet keys, HSM/KMS custody, automated rotation.

Follow-ups

  • The shell tests in scripts/tests/ are not run in CI.
  • env-inventory.test.sh only finds 'PULS3_*' Dart literals and ${NAME:-...} shell reads. Other read styles are missed, and a commented-out line in .env.example counts as "listed".
  • Tx-hash parsing (Signing transaction: <hash>) and the "SAC already exists" match were checked against stellar-cli 28.1, while CI pins 28.0. Re-check the output format when either version moves.
  • The PUSDC payment is not idempotent: each run sends another TEST_USDC_AMOUNT.
  • After a testnet reset, the puls3-test-usdc-issuer identity still exists locally but its account is gone. The script does not recover yet; for now, run stellar keys rm puls3-test-usdc-issuer and run it again.
  • chore: deploy server to Serverpod Cloud and Flutter web app #31 must pass PULS3_API_URL at Pages build time (--dart-define=PULS3_API_URL=<server>). The Pages build is not served by the server, so it will not get config.json from it.

…d test USDC asset

Adds scripts/fund-testnet-accounts.sh with offline tests (stubbed stellar and curl). Accounts are public keys or Stellar CLI identities; the issuer is a CLI identity, so no key material is read or written. Refs #30
…fine

The bundled assets/config.json stays the default; native and flutter run builds can now point at another server through --dart-define(-from-file) without code changes. Refs #30
…mple

Testnet defaults are the public values from contracts/deployments/testnet.json; a commented local block shows the env-only switch. The app reads the same file through --dart-define-from-file, so it stays secret-free; Serverpod secrets are documented commented out. Refs #30
…ventory check

docs/infra/secrets.md lists every secret (in code or planned per ADR-0003) with purpose, storage, access and rotation. scripts/tests/env-inventory.test.sh checks that .env.example covers every variable read in code, matches testnet.json, and that secrets.md covers every secret. Refs #30
tx new only prints 'Signing transaction: <hash>'; found on the first live testnet run. Refs #30
…rt failure

The response file is truncated before each call, and status 000 or an empty body is a failure. Refs #30
…deployed

Only the 'already deployed' error (Error(Storage, ExistingValue) / contract already exists, stellar-cli 28.x) is tolerated; any other failure stops before funding and shows the CLI log. Refs #30
…ript with timeouts

curl uses --connect-timeout 10 --max-time 60; each stellar call runs under GNU timeout 180 when available and unbounded otherwise. Refs #30
…ACT is blank

--dart-define-from-file=../.env defines the key as '' when it is empty. The raw define is now read without a default and resolved by resolveEscrowContractAddress; HirePaymentView takes a nullable address and shows the resolved default. Refs #30
The missing-identity hint now matches the documented command: stellar keys generate <name> --fund --network testnet. Refs #30
docs/infra/secrets.md holds the steps; CONTRIBUTING, README and puls3_server/README link to it. Fixes the stale 'app uses mock data' note, unifies the server start command (dart bin/main.dart --apply-migrations after loading ../.env), the identity generate command and STELLAR_ACCOUNT setup, explains root .env vs puls3_server/.env, the TEST_USDC_AMOUNT unit and a missing separator in contracts/AGENTS.md. Refs #30
@TOMOKI977 TOMOKI977 added this to the Serverpod (Oct 14) milestone Oct 7, 2026
@TOMOKI977 TOMOKI977 added area: infra CI/CD, deploy, testnet, secrets type: chore Maintenance and tooling labels Oct 7, 2026
@TOMOKI977 TOMOKI977 self-assigned this Oct 7, 2026
@TOMOKI977

Copy link
Copy Markdown
Contributor Author

@moises-cisneros @fercodes @Pericena @XxHugheadxX could you review and approve this one? It sets up the shared testnet config and secrets doc that #31 (deploy) builds on. Quickest path to check it: follow "Run against testnet from a fresh clone" in docs/infra/secrets.md and tell me where it breaks for you. Main point to look at: the root .env is shared by the server and the app, so it must never hold secrets (see Notes for reviewers).

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Deploying puls3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1747c54
Status: ✅  Deploy successful!
Preview URL: https://baae9705.puls3-4lw.pages.dev
Branch Preview URL: https://feat-30-testnet-config.puls3-4lw.pages.dev

View logs

@TOMOKI977

Copy link
Copy Markdown
Contributor Author

@moises-cisneros @fercodes @Pericena @XxHugheadxX friendly reminder: this one still needs a review. It's the base for the Serverpod Cloud deploy (#31), which is ready on a branch and waits on this to merge. CI is green; the quickest check is following "Run against testnet from a fresh clone" in docs/infra/secrets.md. Thanks!

@moises-cisneros moises-cisneros left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved after checking out the PR and running it.

Ran: the four shell suites (86 / 64 / 144 / 52 passed, 0 failed), flutter analyze (no issues) and flutter test (85 passed). --dart-define-from-file=../.env parses the quoted passphrase line.

Non-blocking:

  • stellar_explorer_test.dart "the define is unset in tests" fails when running flutter test --dart-define-from-file=../.env, because .env.example already sets PULS3_ESCROW_CONTRACT. CI is unaffected (no defines), but the test could be dropped or made conditional.
  • The shell tests (including env-inventory.test.sh) are not in CI, so the "enforced by" claim relies on someone running them. Worth a follow-up issue.

Not run: fund-testnet-accounts.sh against live testnet, gitleaks locally, a full web build.

@TOMOKI977
TOMOKI977 merged commit 9d129b8 into main Oct 7, 2026
8 checks passed
@TOMOKI977
TOMOKI977 deleted the feat/30-testnet-config branch October 7, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: infra CI/CD, deploy, testnet, secrets type: chore Maintenance and tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore: testnet configuration and secrets management

2 participants