Review pass 1
Stage 34–35, 2026-10-30. Scope: the whole repository, against the brief's §152 list (issuer and Treasury key risk, Stellar asset assumptions, trustlines, authorization, clawback, sequence races, retries, duplicates, wrong network, supply, oversell, double issuance or burn, backing, balance, precision, stale quotes, indexer gaps, RPC and hosted dependencies, disaster recovery, Docker, monitoring, Layer-2 coupling). Method: reading the code paths for each item, the scans (govulncheck, pnpm audit, gosec, Grype), the security, failure and concurrency tests added in this pass, and the container and Chrome runs.
Findings
| ID | Sev. | Finding | Evidence | Impact | Fix | Regression test | Status |
|---|---|---|---|---|---|---|---|
| R1-01 | CRITICAL | Next.js 16.3.8 in all four apps: two unauthenticated RCE advisories (image optimisation with SVG; use cache) and a DoS | pnpm audit, Grype | Remote code execution on the web servers | Next.js 16.4.3 | make vuln (pnpm audit clean) | Fixed (the Ethereum sibling still has it) |
| R1-02 | HIGH | Reachable Go standard-library and x/net vulnerabilities (HTTP/2, TLS, x509, html/template) | govulncheck: 12 reachable | DoS / parsing flaws on the API listeners | Go 1.27.3, x/net 0.62.0, go-jose 4.1.6 | make vuln (0 reachable) | Fixed |
| R1-03 | HIGH | No production signer backend: derived refuses production and Mainnet by design, and nothing else exists | signer.NewDerived | Mainnet cannot launch | Vault Transit backend behind signer.Signer (SIGNER_BACKEND=vault) | make vault-test (real OpenBao; a Vault-signed transaction applied on local Stellar) | Fixed (owner chose Vault, 2026-10-31) |
| R1-04 | HIGH | The data-lake backfill source never prepared its range: the first backfill read would fail, leaving the indexer stuck once it fell outside the RPC window; and backfill could not be configured at all | indexer.BackendSource, cmd/dara | Silent loss of indexing → reconciliation failing indexer_current indefinitely | Prepare an unbounded range, re-prepare on jumps; BACKFILL_DATASTORE_* configuration | TestBackendSourcePreparesBeforeReading | Fixed (not yet run against a real lake: Q-10) |
| R1-05 | MEDIUM | FR-22 daily BUY/SELL limits missing; the daily transfer count could be exceeded by concurrent requests (count then insert without a lock) | trading.checkTransferLimits | Limits bypassable by parallel requests | Daily limits per asset (migration 00011); advisory lock per customer/asset/side | TestDailyBuyLimitUnderConcurrency | Fixed |
| R1-06 | MEDIUM | Idempotent replay not byte-identical (jsonb reorders keys) | TestHTTPSecurity | Clients comparing bodies or signatures over them break | response_body as json (migration 00010) | TestHTTPSecurity | Fixed |
| R1-07 | MEDIUM | A single transient RPC error failed a read when one node was configured (captive core restarts answer errors briefly) | Flaky setup failure, startup race in the demo | Spurious failures, aborted startup | Retry the full read round with backoff | e2e suite green under load | Fixed |
| R1-08 | MEDIUM | custody:CUSTOMER is the only signer of every customer account | custody.OnboardSpec | Compromise moves customer balances among authorized accounts (detected by the indexer) | Accepted design (custodial); HSM, monitoring, clawback + re-issue path documented | Outsider scenarios in TestPlatformLifecycle | Accepted risk, documented (KEY_MANAGEMENT.md) |
| R1-09 | MEDIUM | Settlement capacity is one transaction per channel per ledger; 3 channels ≈ 0.5 settlements/s on Mainnet | Performance baseline | Orders queue at peak | Size channels from expected volume; batching later | Baseline in evidence | Open: needs Q-08 |
| R1-10 | MEDIUM | Web runtime images carried npm/corepack with vulnerable undici and brace-expansion | Grype | Unused but present code with advisories | Remove package managers from the runtime stage | Rescan: no Critical/High; 3 Medium busybox (no fix) | Fixed (rescan in evidence) |
| R1-11 | MEDIUM | Adding a later asset to COLD needs a human-signed ChangeTrust, which the proposal flow does not build | issuance purposes | Manual step for each new asset | Manual procedure documented | — | Open (documented in ISSUER_OPERATIONS.md §3) |
| R1-12 | LOW | FeePerOp converted an RPC-supplied uint64 to int64 before capping | gosec G115 | A wrapped value fell back to the minimum fee | Explicit cap before conversion | go test ./internal/stellar | Fixed |
| R1-13 | LOW | Dead Ethereum incident kinds (reorgs, nonces, reserve cap, finality) | incident.go | Confusing operators and docs | Removed; kinds listed in INCIDENT_RESPONSE.md | build | Fixed |
| R1-14 | LOW | Demo race: deciding an unfreeze before the freeze is recorded (the API correctly refused) | make demo failure | Demo only | Demo waits as an officer would | make demo | Fixed |
| R1-15 | LOW | The backend exports no traces although the monitoring stack expects them | docker-compose.observability.yml | Less debugging context; logs carry request IDs | Tracing env removed until implemented | — | Open |
| R1-16 | INFO | The company npm mirror returns 500 for Next.js 16.4.3; container DNS cannot reach the public registry | image builds | Image builds fail in this environment | Build with --network=host and the public registry | — | Environment, reported |
Items reviewed with no finding: wrong-network transactions (signer bound to one passphrase; startup checks the
manifest; TestDerivedRefusesMainnetAndProduction); double issuance (in-flight issuance counted against backing;
CAS settlement); double burn (CAS; hold); oversell (TestConcurrentBuysNeverOversellTheHotFloat); stale quotes
(atomic Consume with expiry); duplicate transactions (one intent per purpose and source; UNKNOWN never rebuilt);
sequence races (channel leases; foreign use quarantines); supply calculation (full formula incl. claimable balances,
pools, contract balances); AUTH key scope (weight 1 = low threshold only: cannot claw back or issue); hosted
dependencies (none in the request path); Docker (non-root, read-only, capabilities dropped, admin API unpublished);
Layer 2 (only CapacityProvider).