The destination-bound browser prover is implemented and locally accepted in
the production claim flow. Hosted enablement is still controlled entirely by
the deployment manifest's proof.browser_proving descriptor; absent or
disabled descriptors fail closed and keep Proof Helper Desktop available.
The browser provider changes where the same destination proof is computed. It does not change the circuit, proof artifact, Cardano bytes, verifier key, or contract semantics.
cmd/wasm-prover: main Go WASM entrypoint. RegistersdiscoverCredentialPaths,proveDestination,preflightProofAssets, and__wasmProverReady.cmd/msmworker: Go WASM MSM kernel used by nested workers.internal/streampk: proving-key section index and file/HTTP range source.internal/streamprove: streaming Groth16 adapter.internal/msmengine: CPU fallback, sharded worker transport, authenticated section fetch, pinned point decoding, scheduling, and trace hooks.internal/proofassets: signed chunk-manifest generation and validation.apps/ownership-proof-web/public/proof-runtime/prover-worker.js: dedicated proof-worker orchestrator.apps/ownership-proof-web/public/proof-runtime/msm-worker.js: nested MSM worker bootstrap.apps/ownership-proof-web/lib/proving/capability.ts: cheap browser capability checks.apps/ownership-proof-web/lib/proving/browser-wasm.ts: claim-provider preflight, sequential batch proving, cancellation, result gates, and error redaction.experiments/wasm-prover: benchmark harness and diagnostics for the same production packages; it is not a second production implementation.
The claim UI checks runtime capability before reading the recovery phrase. The
prover worker loads wasm_exec.js and proof-destination.wasm, then performs
automatic local credential discovery for all distinct proof targets in one
pass — unless every proof request already carries an explicit CIP-1852 path,
in which case discovery is skipped and each path is verified against its
target credential inside proveDestination. Only after every target is found
(or paths are supplied) does the browser open or prefetch the large proof
assets. The Go preflight then validates:
- key-manifest schema, signature, key version, circuit ID, and VK identity;
- chunk-manifest signature and coherence with the key/deployment manifests;
- CCS, VK, PK index, runtime WASM, worker JS, and worker WASM hashes/sizes;
- deployment ID and final
vk_hashequality with the claim deployment.
Discovery derives the hardened purpose, coin, and account prefixes once, traverses the soft role/index subtree from extended public keys, and canonically re-derives every match privately before it is cached for the current worker. It searches roles 0, 1, and 2 in staged index-major order over accounts 0 through 9 and indexes 0 through 5000. Aggregate candidates, rate, and ETA may reach the UI; credentials and paths may not.
During proving, the main worker uses the cached local path and sends scalar material plus signed section/range descriptors to nested MSM workers. Workers fetch public PK chunks, verify SHA-256 and BLAKE2b-256, decode pinned on-curve points, compute partial MSMs, zero scalar buffers, and return only partial results and timing data. The generated proof is verified locally before the provider returns an artifact.
Proof requests in a claim batch run sequentially in one long-lived worker. Parallel proofs would exceed reasonable memory. Cancellation terminates the Go worker; discovery itself checks cancellation between candidates, while an individual WASM MSM is not cooperatively cancellable.
The phrase is normalized and converted to a 96-byte master XPrv in a local
derivation worker. Browser proving moves the XPrv into the dedicated prover
worker and never fetches a hosted API with it. Request JSON containing
master_xprv_hex must not be logged, thrown, persisted, included in progress,
or surfaced in diagnostics. The caller zeros its master byte array in
finally; the worker is terminated after use.
Only public, signed proof assets cross the network. Proof artifacts omit path metadata and must still pass the claim backend's recomputation and VK gates.
apps/ownership-proof-web/lib/proving/browser-wasm.ts defaults to the measured
production route:
worker_count: adaptive 8..16 when W5 is enabled and no explicit count is pinned
shard_count: base 8; raised to at least the applied worker count
range_fetch_concurrency: 2
pinned_decode: true
opt_w1/w2/w3/w5/w6/w7: true
GOGC: 50
GOMEMLIMIT: 3000MiB
The accepted Gate G1 signed-r8 run used streampk-sharded-groth16 with all
W1-W7 flags, 16 applied Workers, 16 shards, and range-fetch concurrency two.
It completed proof construction in 70.400 seconds, peaked at 1.4593 GiB main
WASM heap, verified locally and through the compiled contract, and passed the
complete tamper and then-current five-case fault suites. The production-host
confirmation completed in 115.770 seconds / 1.4627 GiB under substantially
heavier concurrent load; it confirms coherence rather than replacing the
accepted G1 performance result. The old 111.461-second / 2.316-GiB O4/O2 run
remains the ideal-host pre-optimization reference only.
Credential-discovery release evidence uses immutable release
proof-assets-ownership-destination-v2-preprod-9fac96b-g3a-2m-key-discovery-r1.
Its signed runtime/chunk/deployment manifests passed the local release verifier,
and a cold Chromium proof for account 3, role 2, index 0 completed in 90.534
seconds with 0.833 GiB peak main-WASM heap and verified_locally=true. Heavy
unrelated host work contaminated the timing sample, so it qualifies the full
discovery-to-proof path and artifact coherence, not a new performance record.
The bounded worker/chunk recovery change passed its final 2026-07-31
no-regression gate against a same-revision baseline. The counterbalanced gate
ran three clean, locally verified proofs per runtime for each cache mode with
16 workers and shards, range-fetch concurrency two, W1/W2/W3/W5/W6/W7,
GOGC=15, and GOMEMLIMIT=3200MiB. Cold-cache median proving time was
51.470 s for the recovery candidate versus 51.881 s baseline (-0.792%);
warm-cache median was 48.370 s versus 48.609 s (-0.492%). Median peak
heap was 0.110% lower in the cold gate and 0.111% lower in the warm gate.
Both passed the hard ceilings of 0.5% proving-time regression and 1.0%
peak-heap regression, with no accepted sample carrying a transient
contamination observation. This qualifies the healthy path as
performance-preserving; the small apparent improvements are benchmark noise,
not an optimization claim.
The HTTP-Range compatibility fix was held to the same ceiling before release.
An initial Go/WASM fallback was rejected because an otherwise-dormant code
change increased the guarded cold median from 39.101 s to 41.363 s
(+5.785%). The accepted design instead leaves both proving WASM binaries
byte-identical and intercepts only proving-key range requests in the outer
prover-worker.js. A healthy HTTP 206 response is returned untouched with one
native request; only an observed HTTP 200 cancels the full-object body and
switches that operation to the signed, digest-verified 2 MiB chunks.
The corrected production-worker A/B gate ran the pre-change and final workers
through their real message protocol, three clean locally verified proofs per
worker in each cache mode. Cold medians were 43.443 s baseline and 41.969 s
candidate (-3.393%); warm medians were 42.144 s and 40.216 s
(-4.575%). Median peak heap changed by +0.177% cold and +0.118% warm.
All samples passed the unchanged contamination guards and the 0.5% time /
1.0% heap ceilings. The favorable time deltas are treated as benchmark noise,
not as an optimization claim. Unit and browser fault gates additionally cover
the no-extra-request 206 path, sticky 200-to-chunk recovery, signature and
SHA-256 rejection, bounded retry recovery, abort, and chunk corruption without
CPU fallback.
These are browser-prover source defaults and proof-runtime measurements. They
do not change claim batching by themselves. Until the statement-bound V2
deployment is activated, the current Preprod V1 manifest remains authoritative
for its legacy claim policy; the selected V2 candidate separately uses
default/optimization/hard caps 6/6/7 with seven as explicit opt-in.
Pinned decode skips redundant subgroup checks only for digest-authenticated PK
chunks and still performs on-curve validation. Commitment Basis and
BasisExpSigma use section-backed MSMs; WASM GC/memory-limit enforcement is
temporarily suspended only around the solver to avoid the observed wasm32
bad-pointer failure, then restored. computeH and scalar preparation overlap
worker-bound stages without changing proof challenges.
Upstream gnark v0.15.0 does not expose all seams required by the reviewed
browser runtime. vendor/ must equal a clean go mod vendor plus the ordered
runtime patch set managed by these scripts:
scripts/bootstrap-vendor.sh
scripts/check-vendor-drift.shNever run plain go mod vendor and commit or build silently; it removes the
streaming seam. Moving to a reviewed fork/upstream change remains preferable
for a final production provenance story.
Build reproducible runtime files:
scripts/build-wasm-prover.shRun correctness checks:
go test ./internal/msmengine ./internal/streampk ./internal/streamprove ./internal/proofassets
PROOF_WASM=dist/proof-runtime/proof-destination.wasm \
WASM_EXEC_JS=dist/proof-runtime/wasm_exec.js \
node experiments/wasm-prover/tests/key-discovery.mjs
node experiments/wasm-prover/scripts/key-discovery-browser-benchmark.mjs
GOROOT="$(go env GOROOT)" N=2000 WORKERS=4 \
node experiments/wasm-prover/web/node-msm-check/run.mjs
node experiments/wasm-prover/scripts/verify-tamper.mjs \
experiments/wasm-prover/output/o4-o2-section-commitment-w8-s32-rf2-local7-artifact.jsonRun a guarded benchmark only on an otherwise idle host and compare uncontaminated summaries:
node experiments/wasm-prover/scripts/guarded-browser-benchmark.mjs \
--case local-check --workers 16 --shards 16 --rf 2 --cpu-list 0-15See browser-proving-asset-hosting.md for asset generation/staging and
vercel-preprod-browser-proving-deployment-plan.md for the still-open hosted
rollout.
The durable results/backlog is experiments/wasm-prover/optimization-backlog.md.
Do not accept a tuning change from compile-only or microbenchmark evidence: it
must produce a real destination proof, verify locally, pass tamper tests,
preserve artifact/Cardano bytes, record uncontaminated trace evidence, and stay
within the approved memory envelope.