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.
236 lines
8.7 KiB
Markdown
236 lines
8.7 KiB
Markdown
# 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.
|