# ZK review remediation — 10 September 2026

All three findings from the staging review have been addressed and committed/pushed to staging at the user's request. No circuits or cryptographic keys were changed, and no transactions, live configuration changes, or deployments were performed as part of this work.

Verified remote staging heads: privacy-frontend `ac2dc2a329c4587d55bf58744f1dd9324a792e85`; privacy-protocol `4860c121c9ad089df3ee7e3bd755b838d775ca8d`; stealthos-relayer `34d35df705e566886435ef07e2d0ce39cbcbefe6`. All three Git working trees were clean after pushing. These review reports live outside the Git repositories and remain local.

## ZK-01: durable swap-output recovery

The frontend now saves the output secret in the existing encrypted settlement journal before submission. Journal preparation checks the secret against the exact canonical privateSwap calldata, including its proof-bound output NPK/token, source nullifiers, and change commitment.

Settlement completes the output from the confirmed receipt it already holds. It validates the pool's PrivateSwap event against the recorded intent, finds the unique matching insertion, reads the token domain for that actual tree at the receipt block, and recomputes the commitment. It no longer re-fetches a receipt after finalization or depends on the pre-proof current-tree snapshot.

Crash recovery discovers the transaction by its nullifiers even if the browser closed before saving a transaction hash. It uses the same reconstruction routine, requires a canonical successful transaction/receipt and the confirmation-safe chain view, and retains ambiguous records. The journal cannot finalize a new swap without its recovered output. The recovered note and self-delivery metadata are appended in a guarded, idempotent update.

Submission failures retain the journal reference even if no transaction hash was returned. Replaced transactions must match the exact recorded intent. The UI preserves recovery references, correctly marks spent commitments, and routes both recovered swap output and change through the existing durable delivery path.

Primary files:

- swapOutput.ts (`privacy-frontend/src/joinsplit-v2/swapOutput.ts`)
- pendingJournal.ts (`privacy-frontend/src/joinsplit-v2/pendingJournal.ts`)
- journalReconciliation.ts (`privacy-frontend/src/joinsplit-v2/journalReconciliation.ts`)
- settlement.ts (`privacy-frontend/src/joinsplit-v2/settlement.ts`)

## ZK-02: Flat V2 relayer compatibility

The relayer now supports all seven Flat V2 selectors, with canonical decoding, batch/arity/range/duplicate checks, source/current-tree rules, token-domain checks, and proof-bound positive fees paid to the actual signer for withdrawal-like actions. Transfer and migration remain sponsored because their ABI has no relayer fee field.

An exact whole-transaction simulation is mandatory, including composed batches; SKIP_PREFLIGHT cannot disable that boundary. Value-bearing relay requests fail closed. Legacy behavior remains separate.

Related integration blockers were corrected: the dedicated pool route accepts the frontend's minimal request envelope, and startup can validate the V2 pool/router instead of requiring legacy getters.

Operator configuration remains necessary: select `flat-joinsplit-v2` with the appropriate `BASE_*_POOL_PROTOCOL`, configure the actual pool/router addresses, and ensure the pool fee recipient matches the signer. Existing mainnet Node-relayer restrictions remain in place. See relayer configuration (`stealthos-relayer/README.md`).

## ZK-03: mandatory canonical production release gate

The canonical production deployment script now invokes the existing complete signed-manifest validator afresh before recording broadcasts. This includes its hash-pinned semantic phase-2 lineage and trust-policy verification. The script checks the target chain and all ten signed deployment addresses/runtime code hashes during local script execution before Forge can broadcast them. Saved `valid: true` receipts are not accepted as authorization.

This implements the deployment-enforcement alternative from the review. It does not introduce a second phase-2 cryptographic verifier inside the exporter. Exporter/token documentation and generated banners now explicitly describe phase-1/IC binding as preliminary, not production approval. A related export/validation mismatch was fixed by using the same canonical production renderer on both sides of the strict byte-for-byte source check.

Production requires Forge `--ffi`, cached pinned tooling, and the documented release/trust-policy inputs. The exporter marker alone remains insufficient. See production release prerequisites (`privacy-protocol/JOINSPLIT_PRODUCTION_MANIFEST.md`) and deployment gate (`privacy-protocol/contracts/script/JoinSplitProductionReleaseGate.sol`).

## Verification

- Frontend: **74 tests passed across 10 JoinSplit V2 suites**, including 21 new tests. TypeScript `--noEmit` passed.
- Swap regressions cover encrypted crash/restart recovery without a saved transaction hash, missing receipts, rollover, substituted events/keys/commitments, repeated finalization/reconciliation, missing recovery material, storage failure before broadcast, no receipt re-fetch, lost submission responses, and replacement-intent mismatch.
- Relayer: full `npm test` passed, including **13 new V2 regression groups**; formatting passed. Locked dependencies were restored without changing the lockfile.
- Protocol: **26 deployment tests**, **38 release-validator tests**, and **3 verifier-rendering tests** passed. One real-ceremony opt-in validator test remains ignored.
- Both release CLI binaries passed `cargo check`. The ceremony dependency-boundary check and finalization shell-script syntax check passed.
- `git diff --check` passed in all three repositories.

These are local source and regression-test results, not a claim of production readiness. A genuine production ceremony, configured live relayer, and live-chain deployment were not exercised. Existing production manifests must be rebuilt and approved against the final committed sources and artifacts; these changes do not promote development artifacts. Secrets already lost before this fix cannot be reconstructed merely from public transaction data.
