# ZK frontier-proof bench plan **Verdict:** **PARKED · pending sibling-repo measurement.** **Ticket:** #000016 **Authored:** 2026-05-09 This document is the arborist-side closure for #000016. It captures the bench plan, acceptance thresholds, and a preliminary projection. **Actual measurements live in the sibling repo `arborist-zk-bench`** (not yet built), per #000016's language constraint that arborist stays pure-Python and never gains a Rust dependency. When `arborist-zk-bench` produces real numbers, this doc gets updated with the verdict line at top flipping from PARKED to **VIABLE** or **UNAFFORDABLE**, and arborist consumes the wire format spec'd in `docs/zk-wire-protocol.md`. --- ## 1. Why parked, not closed #000016's closure criterion (§7) is measured numbers at three circuit sizes (256, 1024, 4096) on commodity hardware. Producing those numbers requires a working Plonky3 (or equivalent) circuit in a Rust crate, plus prover + verifier binaries, plus reproducible-build infrastructure. That belongs in `arborist-zk-bench` per #000016 §1.21 and §4.1. Building the sibling repo before there's a v8/v7-W demand for ZK is speculative work. The ticket itself recommends "deferring until a v8 / v7-W ticket creates real demand" (§7). So: arborist produces the bench plan + the wire protocol + preliminary projection, and parks measurement until demand materializes. The hand-wave is replaced with an explicit PARKED-with-thresholds artifact. ## 2. Bench plan (for the future sibling repo) ### 2.1 Circuit shape One affine preactivation node, three sizes: | Size | Inputs | Weights | Cost class | |------|--------|---------|------------| | 256 | 256 int64 | 256×256 int64 | small / unit-test scale | | 1024 | 1024 int64 | 1024×1024 int64 | medium / typical-frontier scale | | 4096 | 4096 int64 | 4096×4096 int64 | LLaMA-class hidden-width scale | Per node: - Inputs (witness, private): full activation vector A, full weight matrix W. - Inputs (public): `bias` B, subset-mask `S`, epsilon as `(num, den)` integer pair. - Outputs (public): SHA-256 commitments of W and A, commitment to `(S, ε)`, boolean "ε-inequality holds". - Constraints: range checks on int64 values, field-level GEMM, L1-norm via sum of absolute values, final inequality check. ### 2.2 Measurement targets - Wall-clock prover time (ms). - Proof size (bytes). - Verification time (ms). - Memory peak during prover (MB). - Setup time (ms) — one-shot per circuit. ### 2.3 Platform pair - **Apple Silicon** — M3 Max or equivalent (representative dev machine). - **Linux x86_64** — generic cloud commodity (representative server). Run twice per platform; report median + range. ### 2.4 Bench harness sketch `arborist-zk-bench/bench/zk_frontier_bench.py` (in the sibling repo, not here): ```python sizes = [256, 1024, 4096] seeds = [42, 137] for size in sizes: for seed in seeds: rng = numpy.random.RandomState(seed) A = rng.randint(-(2**31), 2**31, size, dtype=numpy.int64) W = rng.randint(-(2**31), 2**31, (size, size), dtype=numpy.int64) B = rng.randint(-(2**31), 2**31, size, dtype=numpy.int64) S = rng.choice([0, 1], size).astype(numpy.int64) eps = (1, 100) # 1% L1 error budget t = time.monotonic() proof = prove_affine(W, A, B, S, eps) prove_ms = (time.monotonic() - t) * 1000 t = time.monotonic() ok = verify_affine(proof, public_inputs(W, A, B, S, eps)) verify_ms = (time.monotonic() - t) * 1000 record({ "size": size, "seed": seed, "platform": platform_id(), "prove_ms": prove_ms, "verify_ms": verify_ms, "proof_bytes": len(proof), "memory_peak_mb": ..., "ok": ok, }) ``` ### 2.5 Acceptance thresholds For ZK to flip from PARKED to VIABLE in this bench: | Metric | Threshold (size 4096) | |---------------------|-------------------------------| | Prover wall-clock | ≤ 30 seconds | | Proof size | ≤ 100 KB | | Verifier wall-clock | ≤ 100 ms | | Memory peak | ≤ 16 GB | | Verification result | `ok == True` on all seeds | **All thresholds must pass.** Single-metric failure → PARKED with the failing metric named. ## 3. Preliminary projection (no measurement) Lower-confidence estimates from published Plonky3 / Halo2 benchmarks (2024-2025 era), to inform expectations BEFORE real bench runs: | Size | Prove (projected) | Proof bytes | Verify | Confidence | |------|-------------------|-------------|--------|------------| | 256 | 0.5–2 s | 30–80 KB | 5–30 ms | Medium | | 1024 | 5–30 s | 50–120 KB | 10–50 ms | Low–Medium | | 4096 | 60–600 s | 100–300 KB | 30–200 ms | Low | Sources: Plonky3 benchmark posts, Halo2 GEMM-circuit reports. **These are not measurements**; they're estimates from related circuits. Real numbers will diverge. **Most-likely outcome based on these estimates**: at size 4096, prover time exceeds the 30-second threshold; ZK stays PARKED for the LLM-frontier scale. ZK might be VIABLE at the 256/1024 scales relevant to small distillation models. ## 4. Decision tree from measurement ``` arborist-zk-bench measurement │ ┌───────────┴────────────┐ │ │ VIABLE UNAFFORDABLE │ │ │ │ commission ZK as v8 capability park ZK; v7-Local stays open follow-up tickets: v7's terminal contribution; - arborist [zk] extra wiring v7-Local proofs require (wire-format consumer only) activation publication; - governance_policy.frontier_ answer→model binding stays proof_mode policy field out of scope - v8 ForkScore: ZK availability as a fork-quality signal - hermes-side circuit-friendly activation export ``` Either branch closes #000016: VIABLE → ZK lands; PARKED → documented constraint, future ticket if Plonky3 evolves. ## 5. What arborist does NOT need to do Per #000016 §1.21 / §4.1 / §7 language constraints: - **No Rust code in arborist.** `arborist/zk/` reserved as a future namespace for the wire-format consumer (pure-Python schema validator); no prover, no verifier. - **No Plonky3 dependency in `pyproject.toml`.** `[zk]` extra (when it lands) pulls a JSON-schema validator + a binary protocol parser, never the prover binary. - **No circuit code in arborist.** `circuits/affine_preact.rs` lives in the sibling repo. - **No bench harness in arborist.** `bench/zk_frontier_bench.py` lives in the sibling repo. arborist's `bench/` stays pure- Python deterministic-fixture work. ## 6. What the wire protocol looks like Detailed spec in `docs/zk-wire-protocol.md` (sibling document). Brief summary: - Sibling repo emits proofs as canonical-byte JSON conforming to a versioned schema. - arborist consumes proofs at the providence-record write boundary, optionally adding a `model_weights_zk_root` field to the v9.8 cache_key (governance-hash-folded). - arborist's verifier helper validates schema + checks SHA-256 binding against the providence chain. It does NOT run the ZK verifier — that's also in the sibling repo or in a separate trusted binary. ## 7. Cross-references - #000016 — this ticket (parked). - #000018 — adversarial soft-hash analysis (closed); the M3 mitigation there ("drop the anchor") is the only existing defense if ZK proves UNAFFORDABLE and v7-Local activation publication is also out of scope. - #000013 — v7-W spatial-temporal substrate (closed); v7-W inherits the ZK question — world-state observations are ALSO privacy-sensitive, and the same VIABLE/PARKED decision tree applies. - v7 § 16.1 — the original "swap SHA-256 → Poseidon" hand-wave this doc replaces. ## 8. Status / closure **This document closes the arborist-side hand-wave.** The sibling repo's measurement work remains open as a separate project; #000016 is parked rather than fully closed because the verdict line awaits real numbers. When `arborist-zk-bench` produces measurements: 1. Update §3 with measured numbers, replacing the projection. 2. Flip the verdict line at top of this doc: PARKED → VIABLE or UNAFFORDABLE. 3. Open follow-up tickets per §4's decision tree. 4. Update #000016 ticket status: parked → closed (verdict recorded). Until then: this is the artifact. The hand-wave from v7 § 16.1 is replaced with explicit thresholds + preliminary projection + parked-status with named open work.