Ethereum and Base production readiness
This is the working audit and remediation plan, started on 2026-10-14. It is an internal engineering review, not independent security acceptance. The guardian address is configured on Ethereum and Base; Base deployment remains outstanding. Production verifier and release gates remain mandatory.
Acceptance criteria
- Every finding has a documented resolution or a specific deployment blocker.
- Contract accounting, authorization, replay protection, callbacks and exits withstand adversarial unit, fuzz, invariant, real-proof and pinned fork tests.
- Circuit constraints bind every financial transition and public context, reject invalid witnesses and agree with SDK, relayer and Solidity encodings.
- Plaintext private witnesses and keys remain local. Disclosures to RPC, relay, archive, backup and analytics services are minimized and documented accurately.
- Other origins cannot frame the account UI, retain a usable opener reference, read private responses or trigger authenticated session actions. Browser tests exercise an actual hostile origin, including sibling origins.
- Ethereum and Base use the exact qualified artifacts, pinned dependencies and compiler settings. Production builds fail closed on missing release evidence.
- Recovery, reorganization handling, service persistence, fee accounting, gas bounds and deployment/monitoring procedures are validated.
Execution order
- Baseline and scope. Preserve existing working-tree changes; inventory entry points, deployments and trust boundaries. Run TypeScript, SDK, browser, Rust, contract and release checks and record existing failures.
- Browser and privacy. Review CSP and hosting headers, CORS and request provenance, OAuth, cookies, storage, workers, external scripts, links, logs, proof asset integrity and service metadata. Remove unnecessary disclosure and add adversarial browser and request tests.
- Contracts. Trace every value-moving entry point through assets/vaults, fees, proofs, swaps and margin. Inspect reentrancy, token behavior, deadlines, signatures, rounding, partial failures, permission changes and insolvency.
- Circuits and cryptography. Review range and boolean constraints, note conservation, Merkle membership, nullifiers, owner/domain/action binding, checkpoint continuity, witness generation and verifier/prover agreement.
- Relayer and recovery. Audit admission, quotes, encryption, idempotency, nonces, transaction simulation, persistence, delivery, expiry and restarts.
- Maintainability and gas. Simplify code where it reduces error-prone duplication; retain explicit security boundaries. Measure gas changes and deployment sizes before accepting optimizations.
- Release verification. Run the complete applicable checks, real proofs, Ethereum/Base fork scenarios and hosted browser checks against the final build. Record immutable artifact identities and outstanding external evidence.
- Delivery. Review the complete diff, commit tested changes and push to
mainas requested. Do not create PRs or external messages. Do not mark the protocol production-ready while independent acceptance or exact-release qualification remains outstanding.
Initial findings and evidence
| Area | Observation | Status |
|---|---|---|
| Release | At baseline, no qualified four-circuit release was present; production pins were zero. | Automated qualification now passes; acceptance remains outstanding. |
| Review | Independent review of the exact deployed circuits, prover, verifier, contracts and client is outstanding. | External acceptance required. |
| Baseline | TypeScript check passes. Repository tests: 69 pass, 2 fail (dependency compatibility and margin gas expectation). | Fixed; current repository suite passes. |
| Workspace | Existing UI performance/proof-cache changes were present before this audit. | Preserved, reviewed and covered by regression runs. |
| Browser | Hosted config lacks opener/resource isolation; local preview headers differ from hosted headers. | Fixed in both hosting roots, development and preview. |
| Privacy | App loads analytics JavaScript in the same execution context as private account data. | Removed from private app; public sites retain analytics. |
| Session gateway | Google session gateway delegates browser request-origin validation to an upstream service. | Added local fail-closed checks and hostile-origin tests. |
Remediation record
- Removed the private app's analytics component and unused app telemetry helper. Public landing/documentation analytics remain on their separate origins.
- Separated catalog loading from relayer discovery. The sandbox no longer contacts real relayers, and account loading/refresh discovers only the selected deployment. Network navigation remains immediate and ignores stale discovery results after a later selection. Unit and browser regressions verify the request boundary, Back/Forward navigation and storage-disabled reloads.
- Added COOP, CORP, origin-agent clustering and restricted browser permissions to
both root and
apps/webhosted configurations, development and preview responses. Hosted CSP now explicitly denies framing in addition to the existing document meta policy. Disabled development and preview CORS defaults. Tests use a real second origin on another loopback port, exercising the same-site boundary as well as cross-origin access. - Added local Google session request validation before forwarding cookies or authorization codes. Requests require the app's custom header and same-origin fetch metadata or an exact origin; sibling, opaque, malformed and insecure origins fail closed. Upstream services accept only HTTPS or loopback HTTP. Added the missing root-hosted API entry point so both hosting roots expose the same guarded session handler.
- Consolidated relayer response policy in one middleware, including early errors and preflights. Added resource isolation and origin-confusion regressions.
- Added a dedicated Linux CI job that runs hostile-origin and wallet/device-key browser checks in Chromium and WebKit on every push. It uses synthetic local wallet/RPC fixtures and requires no real funds or account credentials.
- Moved the full UI suite and extended invariant campaign into independent CI jobs after the combined job exhausted its 45-minute limit during extended invariants. All earlier checks, including the full UI and contract state suites, had passed. Every check, invariant run count, depth and seed is retained; the deeper campaign has its own time budget. A later main job passed all 519 state tests and rejected ten mutations before its old 45-minute limit expired; it now allows 60 minutes. The browser job allows 45 minutes after a 26-minute full run. These limits preserve headroom for hosted-runner variation.
- Fixed a startup race where clicking Deposit before saved-account decryption completed opened an unnecessary wallet-connection dialog. Account actions now wait for local access checks, with a visible loading status. A controlled slow-decryption regression covers disabled actions, safe navigation and opening Deposit afterward without another wallet connection.
- Split the all-pairs swap quote matrix into separate source-asset cases. Every ordered pair and quote assertion remains covered, while a slower CI runner no longer has to complete an entire chain's matrix within one one-minute test.
- Fixed the account-constraint runner's cached BN254 worker-pool leak. It previously printed successful results but kept the process alive, stalling CI. Cleanup now releases the pool and temporary witnesses on success and failure; the complete 11-valid/41-adversarial run exits normally.
- Restored the retained public SRS verification log referenced by the tracked receipt. A blanket log ignore rule had omitted it from fresh checkouts. Its SHA-256 matches the existing receipt; this preserves verification evidence and does not grant production acceptance.
- Removed unselected local scenario configurations from hosted builds, with a regression covering both publication cleanup and changes in selected catalogs.
- Fixed the stale Sepolia gas expectation and admitted the exact frontend-only IndexedDB test dependency into release compatibility checks. Integrity and dependency tampering still fail; no proof dependency was changed.
- Corrected the production toolchain policy to statement version 3 and all four circuit input counts (71/71/71/11), and added retired/missing-route rejection. Refreshed the unaccepted build snapshot; no production pins or acceptance records were fabricated.
- Corrected exact-release qualification to require both browser worker modes for every current circuit, deriving the proof count from the canonical circuit list. The old six-proof assertion rejected the four-circuit bundle. Standalone account evidence now labels initial and continued states instead of note counts.
- Updated the 6,000-user population fixture to include checkpoint initialization, continuation and successor leaves. Mock verifiers still mean this test covers state growth, not cryptographic ownership.
- Cached the asset inventory in the reentrancy-guarded yield-fee checkpoint loop. Its existing 128-asset warm-reserve benchmark fell from 3,045,582 to 2,992,467 gas (53,115 gas, approximately 1.7%). These are isolated test measurements, not complete mainnet transaction receipts.
The opener policy deliberately supports OAuth popups opened by the app while isolating foreign pages that open the app. See the browser vendors' descriptions of COOP and CORP. Optional Google Identity Services remains trusted executable code. CSP permits HTTPS connections for user-selected RPCs; it is not an egress allowlist for a compromised application. The privacy guide records these limits.
- Reproduced all four development verifier exports from the existing keys and refreshed only their stale exporter identity. Exact key and generated-source hashes are unchanged. The generation test now verifies all 37 vectors and enforces the standalone circuit's 11-field ABI.
- Updated browser fixtures for decorative workers, current wallet recovery navigation, progress accessibility and authenticated encrypted quote metadata. Updated the relayer rehearsal for deposit discovery and owner-bound private margin checkpoint signatures/proofs, including the four-argument ABI and deadline-bound standalone proof. Alternate public fork RPCs must pass the same chain and deployment checkpoint checks.
- Reused the exact decimal display helper for intent labels, removing a contract utility import from the public preview's initial dependency graph. Preview JavaScript fell from 157,021 to 133,697 gzip bytes; the existing 135,000-byte budget now passes without raising it.
- Updated the yanked transitive
yoke-derivedependency from 0.8.3 to 0.8.4. Rust tests, Clippy and the release build pass with the new lockfile.
Validation ledger
| Check | Result so far |
|---|---|
| TypeScript | Pass. |
| Repository policy tests | 73 pass. |
| Web unit tests | 1,005 pass; one fork-dependent test skipped. |
| SDK tests | 161 pass. |
| Browser isolation | Four hostile-origin tests pass in Chromium and WebKit; all four also pass against the built preview in Chromium. |
| Targeted browser regressions | 50 pass; four unavailable-WebGPU cases skipped. |
| Full UI suite | 239 pass in the full local run; five skips (four unavailable-WebGPU cases and one local-fork case). CI repeats the suite on each push. |
| Saved-account startup | 42 account-access and Google flows pass. Controlled decryption regression fails before the fix and passes afterward; deposit layouts pass repeated runs. |
| Cross-browser built preview | 159 pass across the full run and targeted fixture reruns; three WebKit wallet cases blocked by the native keychain wait described below. |
| Dedicated browser security suite | All 16 checks pass in Linux CI, including WebKit device-key persistence. The local macOS keychain limitation is recorded below. |
| Exact-candidate browser proofs | All eight pass: four circuits in normal and single-worker fallback modes, including CSPRNG failure and encoding rejection checks. |
| Exact-candidate native/EVM qualification | All four pass stock/optimized equivalence, fresh-randomness and browser proof verification. All 24 Solidity cases pass in two clean builds with identical runtimes. |
| Relayer end-to-end rehearsal | Pass: eight confirmed transactions with real proofs, restart recovery, encrypted earnings and deletion of confirmed queue entries. |
| Contract state suite | All 519 pass after the fixture and gas changes. |
| Account/multi-asset real proofs | All nine tests pass across two suites, with no skips, including multi-asset claims, delayed reclaim, swaps and vault-donation accounting. |
| Contract standards | Lint, formatting, 22 compatibility checks, diagnostics and entry-point checks pass after the gas change. |
| Gas regression | All 27 scenarios pass after the checkpoint optimization. |
| Verifier fuzzing | Four campaigns pass, each with 10,000 cases. |
| Circuit baseline | 27 valid, 205 invalid, 225 serialized mutations and 12 seeded relation cases pass. |
| Account circuit | 11 valid statements and 41 malicious witnesses checked against R1CS. |
| Deep circuit constraints and real proofs | 50 recomputed malicious witnesses rejected with calculator assertions disabled; three real proofs pass 71 public-input tampering checks each. |
| Rust | 97 normal tests pass; 21 database tests also pass in disposable TLS PostgreSQL. Clippy passes. |
| Dependency advisories | npm audit and cargo-audit report zero known vulnerabilities. Rust maintenance notices are recorded below. |
| Builds | Preview web, hosted testnet, landing and docs pass. Preview JavaScript budget passes. |
| Release deployment plumbing | Two tests pass using a local EVM and synthetic acceptance records. This grants no production approval. |
| Ethereum/Base margin forks | All 222 scenarios pass on the two pinned chains. |
Additional checks: all eight verifier-generation tests and four source reference reproductions pass. The invariant suite rejected all 11 directed accounting and lifecycle mutations; the verifier suite rejected all ten directed verifier mutations. The separate extended invariant campaign passed all ten invariants. Artifact checks pass (17 tests plus one separately enabled local-EVM case), as do seven relayer deployment-script tests. All eight selected real-proof Ethereum/Base fork scenarios pass across six suites.
The dependency-policy audit, two proof-encoding checks and all 12 Sepolia qualification/preparation checks also pass. The root-hosted Google API entry point type-checks with Node types. Source formatting and Rust formatting pass.
Exact candidate evidence
Two fresh builds in .local/release-2026-10-14-audit/candidate-a and
candidate-b produce the same SETUP.json SHA-256:
3feb12278fe58db9cb61c0cf217d6e05bce0a065db1e31094724af117604d5f8.
They include all four current circuits (71/71/71/11 public signals). Every
generated verifier source matches its committed production source byte for byte.
Both builds use the retained, hash-verified public SRS verification record.
All eight exact-bundle browser proofs pass in Chromium. Each circuit runs in normal and single-worker fallback modes, rejects missing secure randomness and nine invalid encodings, and checks its public signals against the fixture. This evidence uses synthetic public inputs and grants no production acceptance.
Native qualification completed on 2026-10-14. All four circuits match stock verification keys and complete fixed-blinding proofs in two cases each. Two fresh randomized proofs per circuit verify with stock snarkjs and have distinct commitments. The account-only cases cover initial and continued states. All eight browser proofs also verify with stock snarkjs.
All 24 generated Solidity cases pass in each of two clean builds. They replay valid proofs, mutate every public signal and proof word, and reject noncanonical field aliases against both optimized and reference verifiers. Runtime hashes are identical across builds and match the repository's compiled production artifacts. Runtime sizes are 19,060/19,058/19,060/7,453 bytes, below EIP-170.
The local review packet is .local/release-2026-10-14-audit/review-packet/.
It retains the exact compact browser bundle, setup manifest, verified SRS receipt
and log, dependency lock, all 25 hash-bound qualification files and an explicitly
local reproduction record. Large reproducible stock keys and synthetic witness
intermediates were removed after completion; their stock-key hashes, complete
proof vectors and replay evidence remain. No approval or accepted manifest was
created. The two candidate builds ran on the same machine and do not constitute
independent review.
- Qualification SHA-256:
45147d996b3d8add5c9c0f5f0c07940c9483eda3e748c56ee6c93d7db5c7be1d. - Candidate approval-binding SHA-256:
0adf53ce96275a8c946754382a575ef2a640b18d1736e2386d67de9a5dce282d.
These identities let an independent reviewer evaluate this exact candidate. Production pins remain zero pending acceptance and supported-device validation.
Reproducing the long-running checks
Use the pinned Foundry 1.5.1 and Rust 1.94.1 toolchains. This review used Node
24.21.0 on macOS. Unit and browser interaction commands are in package.json;
the following commands exercise actual proofs rather than mock verifiers:
SEAL_ACCOUNT_STATE_PROOFS=1 forge test --root seal-pool/packages/contracts --match-contract '^(AccountStateRealProofTest|MultiAssetRealProofTest)$' --ffi --threads 2
SEAL_ACCOUNT_STATE_PROOFS=1 forge test --root seal-pool/packages/contracts --match-path 'test/fork/*RealProofFork.t.sol' --match-test '^(testFork_RealProofPrivateOpenAndClose|testFork_RealProofLongRoundTrip|testFork_RealProofShortRoundTrip|testFork_UsdcToWeth)' --ffi --threads 2
RUSTUP_TOOLCHAIN=1.94.1 SEAL_TESTNET_RPC_URL=https://sepolia.gateway.tenderly.co npm run test:relayer:e2e
npm run build:e2e
npm run test:preview -w @seal/web
npm run test:security -w @seal/web
Run heavy proof suites separately on resource-constrained machines. These fork tests write only to local EVM state and use development proof artifacts. They do not qualify or approve a production release.
Browser qualification gap on this machine
Three iPhone/WebKit wallet cases stall while persisting the non-extractable
AES-GCM device key. An isolated page with no SEAL code reproduces the stall in
WebKit revision 2359; Chromium persists, restores and uses the same synthetic
key successfully. Native process samples place the WebContent thread in
SerializeAndWrapCryptoKey and the browser's WebCryptoMasterKey queue in
macOS SecItemCopyMatching / SecurityServer decryption. This is evidence of a
native keychain wait on this test machine, not proof of Safari compatibility.
The tests remain enabled in CI. Do not replace non-extractable keys with exported key bytes or waive the tests. A passing WebKit run on a functioning keychain/test environment and a supported Safari/iOS device remain required before release.
The RustSec database was checked at commit
6aed84668c23d7f535077a9fe918bf1fa0f6a5bc (2026-10-13). It retains maintenance
notices for paste, a transitive build-time macro used by Alloy/arkworks, and
derivative, which is in the lockfile but absent from this target's resolved
build. These are maintenance notices, not reported vulnerabilities; do not hide
them with blanket advisory exclusions or replace cryptographic dependencies
without compatibility review.
Static analysis and manual review
Slither 0.11.6 completed with 990 raw reports across generated verifiers, vendored libraries and first-party contracts. It also reported IR/parser failures for the transient reentrancy guard and inherited EIP-712 code, so this is not a clean or complete static-analysis result. Do not suppress all reports or treat the process exit status as security acceptance.
Reviewed high-risk report families include signature-authorized transferFrom,
transiently guarded pool/vault entry points, adapter-only margin execution,
one-shot Aave flash callbacks, canonical Uniswap callbacks, and deliberate
assembly returns inside external Poseidon/verifier code. Existing adversarial
tests cover token reentrancy, callback spoofing, signatures/nonces, failed payout
rollback and account/backing isolation. The exact generated verifier and custom
prover still require independent cryptographic review.
Private account ciphertext and remembered non-extractable device keys use origin-scoped IndexedDB. Google tokens are encrypted locally, with additional data binding the account identity. Proof caches contain public artifacts only and recheck hashes before use; witness data is sent only to the dedicated local proof worker. Same-origin script compromise, malicious extensions, misleading wallet signature requests, and service traffic correlation remain outside the cross-origin browser protections.
Test counts describe specific runs and do not establish absence of vulnerabilities. Automated exact-artifact qualification is complete. Independent acceptance and the supported-browser/device validation described above remain deployment blockers.
Local passkey account follow-up
The subsequent local-account change adds independent random account keys, WebAuthn PRF wrapping with mandatory user verification, local encrypted file export/import, a separate high-entropy recovery key and replacement-passkey enrollment. Passkey accounts cannot persist an unwrapped remembered device key or enter the Google backup path. Existing wallet and Google account formats remain supported. No contract, circuit, verifier or relayer change is required.
Regression coverage includes authenticated-header/ciphertext tampering, wrong recovery keys, multi-network restore, older-file merges, missing storage, cancelled creation, mandatory approval on reopening and recovery without the original credential. Browser tests exercise Chromium's actual WebAuthn API with a virtual USB PRF authenticator at desktop/mobile viewport sizes, including a fresh browser context and replacement credential. These are not physical-device compatibility results.
Before production, review this added client encryption surface and qualify create, reopen, file restore and replacement on actual Ubuntu/Linux browsers and compatible security keys/providers, macOS, iOS Safari and Android. Verify PRF across synchronized copies of the same credential where synchronization is advertised. Unsupported combinations must fail without creating an unprotected account. Use a stable private-app relying-party hostname with no untrusted subdomains. The earlier exact-candidate qualification does not independently accept this later UI code.
Password-protected portable export is implemented for random-key Google-created and passkey accounts. The self-contained local file restores all saved networks without Google or the original authenticator, using AES-256-GCM and WebCrypto PBKDF2-SHA256 with a fixed 600,000-iteration profile. Export validates the generated file before download; import authenticates its keys and complete history before writing account state. Tests cover wrong passwords, ciphertext changes, unsupported KDF parameters, fresh-device restoration and preservation of passkey unlock requirements.
Google Drive backup can be explicitly disabled without changing the account, address or available actions. Enabled backups still fail closed on upload or authorization failure. Funding wallets are separate from private identity; deposits bind the actual funding address and selected chain, and relayed swaps support independent accounts. The UI exposes all six overview actions, with deployment availability checked when selected.
These client changes need independent review and physical-device qualification before a production release. Production pins and acceptance gates remain unchanged.