Research on zero-knowledge proofs of LLM inference, building on FlashProve.
Every number below comes from a file on main (the one exception, a reviewer figure, is marked as commit-message only); follow the evidence link for its conditions and caveats. "unreplicated" means one lane measured it and no separate lane has reproduced it with a receipt in a file, so it is not a headline. Plain-language overview: docs/fork/EXPLAINER.md. The table and the claim ledger CLAIMS.md are generated from research/results.yaml, which also holds each figure as a machine-readable metric (value, unit, n, spread, hardware, date, commit, source file); regenerate with python3 scripts/update-results.py, check with python3 scripts/update-results.py --check and python3 scripts/check-claims.py.
| Result | Status | Key number | n + conditions | Evidence |
|---|---|---|---|---|
| CPU verification of upstream's stored synthetic proofs | reproduced | stored exact-match and clipped-gap proofs: YES / WITHIN LIMITS, about 1.57 s each; 10 of 10 altered sets rejected in each table |
one clean build at upstream 9e2bdd8f, synthetic weights, CPU only; plus an independent review in a separate clone that added 19 negative cases |
research/reproduction/ |
| Full confined CPU suite on the repaired integration (PR #4) | measured, unreplicated (grandfathered, not a headline) | aggregate exit 77: 89 rows passed, 2 in part, 0 failed, 1 not run (13 GPU rows left out by design) | n = 1, not pre-registered (predates the research standards), one run on a shared 16-CPU VM at PR #4 head 133da749; its 4,371 s is wall time, not a performance number |
research/cpu-full-suite/ |
| Matched upstream/fork CPU verifier baseline (PR #18) | negative, unreplicated | median 2.67 s upstream vs 2.68 s fork, identical binaries: no speedup | 5 alternating runs per arm, -j 1, one pinned CPU, shared VM |
research/performance/ |
| Accounting-reuse candidate, with a stage profile (PR #18 follow-up) | negative, unreplicated | 0.04 s median difference, inside run-to-run spread: no speedup claimed | alternating pairs, same setup as the baseline | research/performance/followup/ |
| One H100, upstream small synthetic example, proof checked on a separate CPU machine (PR #23) | reproduced | proof accepted (YES), 5 of 5 altered inputs rejected; proof 4,149,564 bytes; $1.19 all in |
n = 1 cold run, synthetic weights, -M 1, one request; timings from file timestamps (±1 s) |
research/gpu/ |
| Independent replication of that run (PR #26) | reproduced (agree) | cp_prove mean 2.35 s (SD 0.13); cp_commit mean 18.8 s (SD 2.2); 4 of 4 proofs accepted, 20 of 20 mutations rejected; same answer token on every run |
separate rental and lane; 1 cold + 3 warm runs, n = 3 timed under /usr/bin/time; one machine |
research/gpu/replication-1/ |
| Verifier speedup: inline limbs for exact planning arithmetic (PR #27) | measured, unreplicated | complete stored-proof verification about 18% faster single-thread (2.610 to 2.145 s median, -17.8%; -18.0% on the final source); verdicts and output byte-identical on 9 cases. A reviewer re-run recorded only in a commit message saw -15.4%; no file receipt, so not a replication | single thread (-j 1), one pinned CPU, 12 and 10 alternating rounds, non-overlapping ranges; at -j 4 the gain is -10.1% wall |
research/performance/real/; reviewer -15.4% only in the commit message of 29907e5f |
| KDA micro-slice: signed staged-floor fixed-point recurrence (PR #19) | measured, unreplicated | 11,840-byte proof, 56 product gates; 50 hostile proofs rejected; fixed point differs from scalar FP32 by up to 1.73 (numerical equivalence fails, as expected) | one token, one head, dimension 1, synthetic inputs, no weights, CPU | research/operators/glm_kda_fixedpoint/ |
| Adoption of top-k and operator PRs #14 to #16 onto the repaired integration (PR #20) | in progress (held) | targeted gaps closed: clang build exit 0, weight_sums 0 skipped, integrations unit 1961 passed / 3 skipped; not approved for adoption | CPU only, confined runner | research/integration-adoption/ |
| Host-path redaction of published evidence (PR #21) | done (hygiene) | no result number; adds scripts/check-no-host-paths.sh |
applies to all evidence trees | scripts/check-no-host-paths.sh |
| Clipped-gap limits vs attacked replies, small synthetic model, scored not proved (PR #45) | measured, unreplicated | clause 1 PASS: 12 grid points reject every gross attack class by median (7 at every seed), e.g. C=p99.9 theta=p100 cap=0 with median FRR 4.7%; clause 2 holds: no point stops a one-token second-choice swap at FRR <= 5% (best 4.7% accepted); 19 of 19 cp_verify binding spot checks NO |
5 prompt seeds x 64 held-out honest replies, Python score of the integer-model package (not cp_verify), CPU, synthetic weights; swap positions random, so rates are lower bounds; no limits recommended for any real model | research/acceptance/clipped-gap/ |
| Fiat-Shamir transcript binding probe on the stored proof (PR #41) | negative, unreplicated (fixed-ID rejection PASS) | 0 of 50 semantic mutations accepted at the published ID (55 of 55 baselines accepted); the registered universal early-binding clause FAILS: the next token y leaves the first challenge unchanged in 5 of 5 runs and is rejected later (reply-differs), as documented upstream; not a forgery |
one shipped fixture (examples/accepted-proof), 11 single-field cases x 5 paired runs, CPU; a binding probe, not a soundness proof or audit |
experiments/20261103-fiat-shamir-binding/RESULTS.md |
| Qwen3.8-27B port feasibility, paper study from config and shard headers (PR #28) | measured, unreplicated (PASS, with a caveat) | committed parameters 26.9 B, 0.067 of Maverick's committed set (threshold <= 0.25); 7 new operator families vs 11 for GLM-5.3-Flash (counting rule written after registration, GLM list from unmerged notes); per-token matrix MACs 1.59x Maverick's | deterministic count from the pinned revision's config and 18 safetensors headers, 5 byte-identical runs; no weights downloaded, no proof run, prove-cost figures are estimates | research/ports/qwen3.8-27b/FEASIBILITY.md |
| CPU reference commit/prove profile and one hashing candidate (PR #40) | negative, unreplicated | candidate (direct 8-byte hash feeding) cut the median reference generation time 6.5% (1.489 to 1.392 s) but wall ranges overlap: FAIL, not adopted; artifacts byte-identical on 6 seeds | 10 alternating AB/BA pairs per arm, CPU 13 pinned, shared loaded VM; CPU reference fixture, not the GPU cp_commit/cp_prove path | experiments/20261103-7-cpu-commit/RESULTS.md |
| Model identity under runtime steering, synthetic model, CPU score only (PR #46) | negative, unreplicated (killed) | KILLED: 0 fresh commitments and 0 fresh proofs (CPU cannot commit or prove); in the tested grid the strict and lenient scores rejected 56 of 56 changed steered replies and accepted the 4 unchanged ones | 5 prompt seeds x 12 nonzero doses (0.05 to 1.0, both signs), 4-token replies, one neuron, synthetic 8-layer model; Python score, not cp_verify | experiments/20261103-model-identity/RESULTS.md |
| Runtime steering under real cp_prove / cp_verify, small synthetic model (PR #50) | measured, unreplicated (strict PASS, lenient half holds) | exact match: 0 of 57 changed steered replies accepted (all refused by the prover), 13 of 13 unchanged accepted; shipped clipped-gap: 5 of 54 changed replies accepted WITHIN LIMITS, all at dose 0.05, passing on the theta mean bound (above-clip 0); Python score agreed with the real decision on 140 of 140 |
one neuron, synthetic 8-layer model, 5 prompt seeds x 7 doses x 2 reply lengths x 2 policies, one H100 of an unverified host; honest prover only, no cheating prover tested; S and G replies are not paired (different scales) | experiments/20261104-36-steering-real-proofs/RESULTS.md |
| Qwen Gated DeltaNet head at real shape, M1 (PR #47) | negative, unreplicated | FAIL (NO-GO for M2 as registered): 10 of 10 honest proofs accepted (prove median 51.4 s, verify 11.6 s, 50.4 MB) and 40 of 40 hostile proofs rejected, but under heavy-tailed values delta reaches 422,167 > 2^18 at F = 12 (0 of 5 seeds fit) | one head, 128 x 128 state, T = 4, F = 12, synthetic inputs, research test security settings (target 10 bits), CPU, shared loaded VM; range study 1,024 steps x 4 distributions x 5 seeds, integer reference only | research/operators/qwen_deltanet/ |
| Qwen Gated DeltaNet head with the RNG/LKP range check, M1b (PR #63) | measured, unreplicated | PASS vs prereg (GO for M2 design): RNG/LKP composed with the wide product oracle in one group; 10 of 10 honest proofs (prove median 39.2 s, verify 7.1 s, 25.4 MB, table 2^21), 44 of 44 hostile rejected incl. limb-recombination and out-of-range state, and 5 of 5 R3 windows where M1 overflowed proved | one head, 128 x 128 state, T = 4, F = 12, two-limb delta, exact remainder bits, synthetic inputs, research security (target 10 bits, N = 10 combinations), CPU 6 cores, shared loaded VM | experiments/20261105-32-qwen-deltanet-m1b-lookup/RESULTS.md |
| #45 attack classes through the honest cp_prove + cp_verify, small synthetic model, one H100 (PR #51) | measured, unreplicated | honest-prover path: Python score matched all 256 decisions (89 cp_verify accepts, 167 honest-prover refusals, 0 cp_verify rejections); one swap then greedy continuation proved and accepted 15/16 (fp2nd, 95% CI 0.70-1.00) and 13/16 (rand, 0.54-0.96) under the shipped limits; 4 swaps and random replies 0/16; cp_verify's limit check was not exercised on a forged proof | target_small_f2_gap, shipped limits and #45's fit point (both cap 1), one fresh commitment each; #45's seed 2026 held-out replies, 16 per class, one draw; synthetic weights; soundness flag vacuous for a cheating prover | experiments/20261104-38-swap-at-verifier/RESULTS.md |
| Signed model manifest checked next to cp_verify, stored small example (PR #59) | measured, unreplicated | 137 of 137 registered tampers refused, 10 of 10 untampered verifies accepted (cp_verify YES), verify about 1.41 s; 14 of 37 fields are the trusted signer's word: 11 always (10 as a one-field change, plus commitment.kind, which a matching seed file passes even with --public-weights-seed) and 3 (upstream, committed files, seed digest) unless the caller passes the rehash flag; small-order ed25519 keys and R refused; symlinks in rehashed dirs, deep nesting and non-true none refused | stored synthetic example, fixture tokenizer/template/system prompt, no real model; client-side detached manifest, pure-Python ed25519 (RFC 8032 3 of 3, 64 of 64 vs cryptography); CPU only, re-measured after PREREG amendment 3 | experiments/20261104-58-signed-manifest/RESULTS.md |
| Still open: integer-vs-BF16/FP8 fidelity (#8), KDA model-scale sweep (PR #42), cost teardown and larger-memory GPU test (PR #44) | in progress | none yet | open PRs, issues and lanes, not merged | docs/research-roadmap.md |
Last regenerated from research/results.yaml on 2026-11-05 (UTC), against main 43bfa9fc.
What this table does not show: no full-size (Maverick) run, no real model weights on GPU, no serving, no TEE, no production use, and no comparison with other verifiable-inference systems. The GPU rows are upstream's small example on made-up weights.
This is the active wakesync/flashprove research fork of Anthropic's FlashProve. We are investigating reproducible, useful verifiable inference: explicit proof coverage, lower measured overhead, characterized numerical fidelity and safe client/request integration.
The original v0.16.0 artifact is preserved below with its attribution, instructions, license and theorem boundaries. Its "not maintained, not accepting contributions" notice describes upstream, not this fork. Research contributions here should target a tracked evidence gate, not assume arbitrary model or production support.
- Reproduced, narrow scope: independent CPU verification of stored synthetic proofs, a 41-call mutation audit and toy proof-library checks in PR #3, building on PR #1. This is not GPU inference or checkpoint fidelity validation.
- Provisional: fail-closed extension admission in PR #2, focused integration repairs in PR #4, and narrowly independently reviewed public-coordinate/shared-wire experiments. Open PRs are not merged functionality; full CPU closure and public operator reproduction remain gated.
- Planned or unvalidated: full-size GPU replication (only upstream's small synthetic example has been run on a GPU, see the table above), measured proving/serving cost at full size, integer-versus-floating-point fidelity, request-bound receipts, longer contexts and conditional architecture ports. No full-size GPU validation, real TEE attestation, GLM/DeepSeek support or production readiness is claimed.
Priority order: P0 reproducibility and soundness/coverage; then measured overhead and cost, with fidelity and request integration in parallel; long-context scaling after profiling; model ports only after operator feasibility. Signatures authenticate messages, TEE evidence authenticates measured execution under hardware trust, and cryptographic proofs establish a specified relation. They are not interchangeable.
The upstream full-size relation uses a 1,024-token context and an integer approximation of Maverick. Its reported roughly 70-100x prefill overhead compares specific workloads and hardware/precision configurations, not universal end-to-end overhead. Integer greedy proofs do not establish BF16/FP8 equivalence or correct random sampling. Full versus selective coverage and clipped-gap policy must stay explicit.
Read next: Research roadmap and issue index | Evidence ledger and exact source caveats | Upstream known limits.
Goals are practical, measurable guarantees and honest negative results. Non-goals at this stage are universal Transformer support, floating-point bit equivalence, unqualified privacy/ZK, immediate long-context ports or promised speedups. The original formal results do not automatically cover new operators, dimensions, wrappers or batches.
The following introduction and operating instructions describe the upstream artifact, not newly reproduced fork results. Performance and model statements retain the qualifications in the linked upstream documentation.
Version 0.16.0 · A research artifact: not maintained, not accepting contributions. · Apache 2.0
FlashProve lets an AI provider prove which model answered a request, without revealing the model's weights.
- Commit. The provider publishes a commitment ID, which binds it to one set of weights. The weights themselves stay secret: every published value that depends on them is masked, except a declared list.
- Prove. For each reply, the provider's GPU produces a proof that the reply is the committed model's output.
- Verify. Anyone can check the proof on an ordinary CPU in seconds with
cp_verify, without the weights.
FlashProve runs Llama 4 Maverick at full size, with a 1,024-token context window, on a single NVIDIA H100. The prover reaches about 110 tokens/second of prefill at 24 requests per batch (Performance).
The protocol's soundness is proved in Lean 4. A provider that tries to pass off output that is not the committed model's is caught, except with probability about q/2^124 + 2^-101 after q hash queries. lean/THEOREM.md has the theorem and how to check it.
Building FlashProve, running it and checking its output take long builds and many exact comparisons. That work is meant for a coding agent. Point yours at this repository with:
Read README.md in the FlashProve repository, starting at "For agents", and AGENTS.md. Working from
the repository root as an ordinary (non-root) user, build the verifier, check the stored example
proof, and report the exit code and last line. Then tell me which other routes (toy proofs on CPU,
small model on an H100, full size) this machine can run, and ask me before starting any of them.
Run everything from the repository root as an ordinary user, never as root or under sudo. Only
installing system packages needs root, so ask your user to do that. If root is the only account,
create one first (how). Also read AGENTS.md for the
rules on reporting results.
| Route | What it does | Needs | Time |
|---|---|---|---|
| 1. Verify a stored proof | Builds cp_verify, checks an example proof |
Linux, bash, g++ 13+; under 2 GB RAM | build 1–2 min, check < 2 s |
| 2. Toy proofs | Self-test of the proof library on CPU | Route 1, plus make and python3; under 1.5 GB RAM | ~1 min |
| 3. Small model | Builds the prover, commits, proves and verifies an 8-layer synthetic model | GPU stack (below); 10 GB RAM | build ~29 min, run 23–30 s |
| 3, full size | Same at Llama 4 Maverick's shape, synthetic weights, 1,001 tokens | GPU stack; 280 GB RAM | ~42 min |
| 4. Llama 4 Maverick | Downloads a public 4-bit conversion (232 GB) and proves one token | GPU stack, curl, Python 3.13; 330 GB RAM, 245 GB disk; a calibration set that you make first | hours |
GPU stack (routes 3 and 4) is the only supported platform for the prover:
- x86-64 Linux 4.14 or later, on a CPU with the SHA extensions.
- One NVIDIA H100 80 GB, used by nothing else.
- CUDA 13.2 Update 2 (nvcc 13.2.86) on PATH. prover/docs/build.md prints the pip lines that install it.
- gcc and g++ 13 first on PATH; binutils, flock, python3.
- The repository and work directories on a local disk.
On minimal Ubuntu 24.04, route 1 and 2 dependencies are (needs root):
apt-get update && apt-get install -y --no-install-recommends g++ make python3 unzip
verifier/build.sh
It is silent for 1–2 minutes, then prints a line starting THE VERIFIER IS BUILT: and exits 0. A
line starting build.sh: note: is a warning: quote it in your report. If your tool calls time out,
run the build in the background:
echo "build started" >> verifier-build.log; nohup bash -c 'verifier/build.sh; echo "exit code $?"' >> verifier-build.log 2>&1 &
Then poll tail -n 2 verifier-build.log until the last line starts exit code. Don't start a
second build meanwhile.
bin/cp_verify examples/accepted-proof/config examples/accepted-proof/ivk examples/accepted-proof/example.req examples/accepted-proof/example.y examples/accepted-proof/example.proof -id "$(cat examples/accepted-proof/ID.txt)"
Expect last line YES, exit code 0. This example is a small model with synthetic weights, so it
shows only that the verifier works, not anything about a served model.
W=$(mktemp -d) && LOOK="$W/look" && echo "LOOK=$LOOK"
blocks/first_look.sh "${LOOK:?first run the line with mktemp}"
Expect exit code 0, and the third line from the end starts AS EXPECTED: the honest copy was accepted.
examples/quickstart-gpu.sh
This builds cp_verify, cp_commit and cp_prove, then commits, proves one 64-token request and
verifies it.
- It can be silent for over 15 minutes. It logs to
quickstart-gpu.login a directory it names first. - Success is exit code 0 and a line starting
quickstart-gpu: all steps ended as expected. - Also check that every
Compiled byline ends inrelease 13.2, V13.2.86. A newer nvcc does not stop the build, but it is not supported.
Full size, after that build:
W=$(mktemp -d) && WORK="$W/example" && echo "WORK=$WORK"
prover/examples/run_target_example.sh target_full_f2 "${WORK:?first run the line with mktemp}"
Expect status 0, a proof of work: line, then YES. Progress goes to $WORK/cp_commit.log.
With too little memory it exits with code 2 at the start.
For the real Maverick weights, see route 4 and
examples/maverick/run.sh. To commit to and serve a model of your
own, see Serving and the
provider's duties. Those pages are required reading before using weights
that must stay secret.
bin/cp_verify CONFIG_DIR PUBLIC_KEY_DIR REQUEST_FILE TOKEN_FILE PROOF_FILE -id COMMITMENT_ID
- The commitment ID: get it from a party your user trusts, never from the server being checked or from files sent with the proof. The stored examples are the only exception.
- The request file: build it yourself (Verify a proof).
- What to read: the exit code together with the last line.
| Exit | Last line | Meaning |
|---|---|---|
| 0 | YES / WITHIN LIMITS: … |
Accepted under exact match / clipped gap |
| 1 | NO: <name> |
Rejected (or a local fault) |
| 5 | NOT CHECKED: <name> |
No verdict; retry once only for internal-stage or internal-exception-parFor, otherwise report |
| 14 | NOT-A-VERIFICATION |
The call was invalid; see the lines after ---- the call ---- |
| 2, 3, 4, 15 | Bad command line or file, or a partial call; no verdict |
verifier/docs/output.md has the full table.
integrations/check_proof.py wraps the call and the comparisons.
D=$(mktemp -d) && ./run_suite.py cpu "$D/work"These are the CPU tests. They overwrite bin/cp_verify and
bin/weight_sums.
- Exit 0 means a pass; 77 means tools are missing.
- Exit 3 means a test saw something false accepted: report it per SECURITY.md.
See SUITE.md.
- Never get past an error with
sudo,chmod,chown,rm, or by editing a source file. Stop and ask your user. - Don't build
prover/unless the task needs it. - Report the exact command, the exit code and the full last line. For an accepted proof, also report
the commitment ID you passed and the
covered:andresult-v1lines.
- Guides: Getting started, How it works, Verify a proof, Serving, What is proved, Weight hiding, Known limits, Performance, Terms.
- Reference: docs, glossary, protocol, format spec, Lean library, Lean verifier, tests, paper, SECURITY.md.
- Examples and checks: the stored example, a full-size proof, rejected proofs, faults, the integration's limits, checking the Lean library, its results.
- Limits: docs/limits.md, docs/limits-further.md, the table of all rows, lean/LIMITS.md, known errors.
Apache 2.0: see LICENSE. Material from other projects keeps its own licence. NOTICE and THIRD_PARTY.md list it, PUBLISHED-WORKS.md lists the published works that the code applies or names, and licenses/ has the texts.
We thank Alessandro Chiesa, Giacomo Fenzi, Guy Weissenberg, Miranda Christ, Rahul Dalal, and Ben Mayer for their input and collaboration on FlashProve. We also thank James Petrie for VerInf.