arborist/docs/zk-frontier-bench.md
russell@unturf.com 0154191bd4
#000016 parked: ZK frontier-proof bench plan + wire protocol
Two arborist-side artifacts close the v7 § 16.1 hand-wave without
violating the language constraint (arborist stays pure-Python; ZK
toolchain lives in sibling repo arborist-zk-bench).

docs/zk-frontier-bench.md — bench plan + parked verdict
========================================================

Bench plan covers Plonky3 (or equivalent) circuit at three sizes
(256/1024/4096 affine), measurement targets (prover ms, proof
bytes, verify ms, peak memory), platform pair (Apple M3 + Linux
x86), acceptance thresholds (≤30s prover at 4096; ≤100KB proof;
≤100ms verify; ≤16GB peak).

Preliminary projection from published Plonky3/Halo2 numbers:

  size 256:   0.5–2 s prove  / 30–80 KB / 5–30 ms verify
  size 1024:  5–30 s prove   / 50–120 KB / 10–50 ms verify
  size 4096:  60–600 s prove / 100–300 KB / 30–200 ms verify
                              ↑ likely UNAFFORDABLE at frontier scale

Most-likely outcome: VIABLE at small scales (256/1024 — useful for
distillation models); UNAFFORDABLE at LLaMA-class hidden width
(4096).

Decision tree from real measurement:
  VIABLE       → commission ZK as v8 capability;
                 follow-up tickets for arborist [zk] extra,
                 governance_policy.frontier_proof_mode field,
                 hermes-side activation export.
  UNAFFORDABLE → park ZK; v7-Local stays v7's terminal
                 contribution; answer→model binding stays
                 out of scope.

docs/zk-wire-protocol.md — consumer-side schema
================================================

Specifies the wire format arborist consumes WHEN/IF ZK proofs
become available. The sibling repo emits proofs; arborist
validates schema + signature + delegates ZK verification to a
sibling-repo verifier binary. arborist itself NEVER runs the
prover or verifier — that's the language-constraint contract.

  - Artifact: arborist-zk-proof-v1 JSON with schema_version,
    proof_system, circuit_id, frontier_node, public_inputs,
    public_outputs, proof_bytes_b64, verifier_setup_id, issued_at,
    issuer_pubkey_ed25519, issuer_signature_ed25519.
  - Trigger: governance_policy.frontier_proof_mode ∈
    {"reveal" (default), "zk"}.
  - Binding: model_weights_zk_root + frontier_proof_circuit_id
    columns added to providence_cache (schema migration deferred
    to integration ticket).
  - Validation pipeline: schema check → recompute commitment
    sanity → Ed25519 signature check → delegate to sibling
    verifier → bind to audit chain.
  - Threat model: compromised prover, wire-format injection,
    proof replay, issuer-key compromise — each with its own
    mitigation.

Status closure
==============

Ticket flipped from "open · awaiting go/no-go" to "parked ·
bench-plan + wire-protocol landed 2026-05-09 (sibling-repo
measurement pending)." This is honest: arborist's side is done.
Sibling repo `arborist-zk-bench` (not yet built) produces the
real numbers; doc updates roll back here when measurements
arrive.

Result: the v7 § 16.1 hand-wave is replaced with explicit
thresholds + preliminary projection + ready consumer schema.
The hand-wave is closed even though the bench-question is
parked.
2026-05-09 15:05:08 -04:00

8.7 KiB
Raw Blame History

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):

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.52 s 3080 KB 530 ms Medium
1024 530 s 50120 KB 1050 ms LowMedium
4096 60600 s 100300 KB 30200 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.