Prototype (Level A) of zero-knowledge proofs that a guardrail classifier's linear head certifies a convex harmful region — without revealing the head weights — implementing the certificates of Beyond Red-Teaming: Formal Guarantees of LLM Guardrail Classifiers (arXiv:2605.10901): the deterministic hyper-rectangle SAT/UNSAT certificate (Eq. 1 / Appendix B) and the probabilistic GMM coverage certificate (Eq. 2–3).
Stack: gnark v0.15.0, Groth16, BN254.
For a head
with
Boundary semantics: proofs exist only when the float margin
GmmCircuit proves the paper's probabilistic certificate for a committed RAW
head and a committed GMM component set,
The claim is parameterized by the public signed integer gmm-verify
prints it with directed guards). The sqrt-bracket side, the ridge sign
(
The GMM head is the RAW verify_gmm.py does not rotate), so
GMM-family vk_sha256 pinning plus the family field keep their artifacts
apart.
frontend.Committer / BSB22, eprint 2022/1072) and
compared by 32-byte point equality outside the constraint system. The only
user-supplied public inputs are groth16.Verify is constant-time in
-
Hiding of
$c_M$ : the committed$r$ is uniform in$\mathbb{F}_r$ . Two gnark subtleties are handled and regression-tested: gnark's per-proof commitment mask is pinned to zero so the points are deterministic and publishable (commitment.ProverOpts— safe ONLY because$c_M$ carries its own randomness$r$ and the region/component preimages are public; never reuse the pattern for a secret-preimage commitment), and$r$ 's wire is anchored in a constraint because a committed wire with no constraints gets a zero Pedersen basis and would silently drop out of$c_M$ . - Binding: DLOG over the SRS bases — the same honest-setup assumption as Groth16 soundness itself.
- Setup-relative: the points live in each circuit's trusted setup; publishing them pins (circuit, keys). A portable, cross-circuit commitment (LegoSNARK-style CP link) is the intended Level-B upgrade.
- gnark's Solidity exporter does not support multi-commitment proofs; on-chain verification (out of scope anyway) would need that gap closed.
Artifacts. vk_v3_d*.bin + ck_v3_d*.bin (box) and
{vk,ck}_gmm{K}{cov}_v4_d*.bin (GMM) form one trust unit per family: the
ck (commitment basis) is exported by setup because the verifying key alone
cannot recompute Pedersen digests; both are public honest-setup CRS material
and must reach the verifier through the same channel. Never accept a ck
basis supplied by a region/record publisher — a fake basis inside the span
of the true one makes an arbitrary record match an arbitrary digest.
Onboarding (amortized, once per region/record). The region record
(region.json: quantized + exact float bounds; the rotation matrix zkbox onboard-region validates the
cleartext — bound_w, bound_w_sat and the sat_capable flag (float
provenance validated AND every gmm-onboard re-derives bound_mu,
sigma_cap. Pedersen binding then transports these checks onto the
in-circuit values in every proof. The circuits do NOT range-check committed
region/component values (TestWrappingRegionSolvesInCircuit documents that
an out-of-range region satisfies the constraints): a digest accepted
without onboarding certifies nothing.
Per-proof verification (zkbox verify and
gmm-verify refuse raw records and take the locally produced onboarded
artifact. Checks: schema versions,
Out-of-band anchor. That
| quantity | scale | rounding | enforced |
|---|---|---|---|
| nearest | in-circuit: |
||
| floor | at onboarding (transported by |
||
| ceil | at onboarding (transported by |
||
| half-even | at onboarding: magnitude |
||
| nearest | in-circuit: |
||
| ceil | per-proof validation | ||
| directed by mode/sign | in-circuit 20-bit magnitude check | ||
| ceil/floor isqrt per bracket | in-circuit 78-bit check | ||
| — | per-proof: |
Box drift bound:
Outward box rounding makes the quantized box a superset of the real box
(inward for SAT claims, so exhibited corners are real points); the ridge
circuit/params.go, circuit/gmm_params.go). The plaintext certifier
classifies the boundary
All heads and datasets from the paper's artifacts; the cleartext oracles are
the paper's actual Verifier.verify / verify_gmm.verify_gmm run on the
same exported points.
Both proofs are against the SAME published
| statement | cleartext verdict | ZK outcome |
|---|---|---|
|
|
SAT ( |
mode-1 proof: verifiable safety hole, 228 B, verifies in 2 ms ✓ |
|
|
UNSAT (margin 0.597) | mode-0 proof, 228 B, verifies in 2 ms ✓ |
The first row is the paper's Table 1 verdict (single hyper-rectangle on BERT
is SAT at the operating threshold) as a zero-knowledge certificate. The
proof pair brackets the region's minimum score between the two thresholds. A
false UNSAT claim at
| model | config | oracle | ZK bracket (two 228-B proofs, same |
|---|---|---|---|
| BERT |
|
0.643666 | |
| BERT |
|
0.190996 | |
| BERT |
|
1.000000 |
|
| GPT-2 |
|
0.790942 | |
| Llama-3.1-8B |
|
0.736190 |
Llama embeddings: 10,000 prompts extracted fp16 on MPS, ~67 min one-time,
then 0.2 s per proof. An inflated-$\hat\kappa$ claim is rejected at
Box circuit (./zkbox bench, bench.csv; constraints
| d | constraints | setup | prove | onboard (once/region) | verify (per proof) | proof |
|---|---|---|---|---|---|---|
| 16 | 946 | 42 ms | 4 ms | 0.4 ms | 2 ms | 228 B |
| 64 | 3,202 | 104 ms | 7 ms | 1 ms | 2 ms | 228 B |
| 256 | 12,226 | 345 ms | 19 ms | 2 ms | 2 ms | 228 B |
| 768 (BERT/GPT-2) | 36,292 | 0.9 s | 55 ms | 7 ms | 2 ms | 228 B |
| 4096 (Llama-3.1-8B) | 192,719 | 4.6 s | 165 ms | 37 ms | 2 ms | 228 B |
GMM circuit (./zkbox gmm-bench, bench_gmm.csv; full covariance costs
| d | K | cov | constraints | setup | prove | verify | proof |
|---|---|---|---|---|---|---|---|
| 768 | 1 | full | 625,066 | 22 s | 1.2 s | 2 ms | 228 B |
| 768 | 3 | full | 1,219,914 | 53 s | 2.4 s | 2 ms | 228 B |
| 4096 | 3 | diag | 201,996 | 5.4 s | 0.2 s | 2 ms | 228 B |
Verification is flat in
The paper's deterministic certifier loops over multiple hyper-rectangles
(HDBSCAN clusters) and reports SAT if any box has a hole. This prototype
proves one box per proof; a multi-box UNSAT certificate is GmmCircuit.
go build -o zkbox ./cmd/zkbox
# ---- box certificate ----
./zkbox setup -d 768 -keys keys/ # one-time per d; writes ck too
.venv/bin/python scripts/export_head.py \
--model urbas/bert_aegis --dataset entfane/construction_points \
--out-dir export/bert --embeddings-cache export/embeddings_bert.npy
# --mode auto (default) emits the claim matching the cleartext verdict
./zkbox commit -secret export/bert/secret.json -keys keys/ -out export/bert/commitment.json
./zkbox commit-region -region export/bert/region.json -keys keys/
./zkbox onboard-region -region export/bert/region.json -keys keys/ -out export/bert/region_onboarded.json
./zkbox prove -secret export/bert/secret.json -region export/bert/region.json \
-statement export/bert/statement.json -keys keys/ -out export/bert/proof.bin
./zkbox verify -statement export/bert/statement.json \
-region-onboarded export/bert/region_onboarded.json \
-commitment export/bert/commitment.json -proof export/bert/proof.bin -keys keys/
# ---- GMM certificate ----
./zkbox gmm-setup -d 768 -k 1 -cov full -keys keys/
.venv/bin/python scripts/export_gmm.py --k 1 --cov full --threshold 0.4773 \
--embeddings-cache export/embeddings_bert.npy --out-dir export/gmm_bert
# emits statements for BOTH modes when provable (two-sided bracket)
./zkbox gmm-commit -d 768 -k 1 -cov full -secret export/gmm_bert/gmm_secret.json \
-keys keys/ -out export/gmm_bert/gmm_commitment.json
./zkbox gmm-commit-record -record export/gmm_bert/gmm_record.json -keys keys/
./zkbox gmm-onboard -record export/gmm_bert/gmm_record.json -keys keys/ -out export/gmm_bert/gmm_onboarded.json
./zkbox gmm-prove -secret export/gmm_bert/gmm_secret.json -record export/gmm_bert/gmm_record.json \
-statement export/gmm_bert/gmm_statement_mode0.json -keys keys/ -out export/gmm_bert/proof0.bin
./zkbox gmm-verify -statement export/gmm_bert/gmm_statement_mode0.json \
-record-onboarded export/gmm_bert/gmm_onboarded.json \
-commitment export/gmm_bert/gmm_commitment.json -proof export/gmm_bert/proof0.bin -keys keys/
# benchmarks
./zkbox bench -dims 16,64,256,768,4096 -out bench.csv
./zkbox gmm-bench -configs "768:1:full,768:3:full,4096:3:diag" -out bench_gmm.csvsecret.json / gmm_secret.json contain the head weights — never publish
them. Everything else (records, statements, commitments, proofs, vk, ck) is
public.
Versioning: JSON schema version 3 (loaders reject anything else). Key
files carry the circuit revision in their names — box _v3_, GMM _v4_
(sign-bit circuit); commitments and onboarded artifacts are pinned to their
setup via vk_sha256. Older artifacts are invalid: new circuit ⇒ new setup ⇒
new commitment values.
-
circuit/—BoxCircuit+GmmCircuit(two in-proof commitments each), fixed-point parameters and drift bounds (params.go,gmm_params.go) -
commitment/— point extraction, native MSMs ($c_M$ ,$c_R$ ,$c_G$ ), commitment-key (ck) serialization, deterministic-commitment prover opts -
witness/— v3 JSON interchange (decimal strings —$2^{64}$ -scale values overflow JSON numbers; signed$\hat\kappa$ splitting), onboarding and per-proof validation -
prover/— compile/setup/prove/verify wrappers, key & proof persistence, vk hashing -
internal/synth/— synthetic provable instances for tests and benches -
cmd/zkbox/— CLI (box: setup/commit/commit-region/onboard-region/prove/ verify/bench; GMM: gmm-* equivalents) -
scripts/export_head.py,scripts/export_gmm.py— exporters; both run the actual BRT certifier as the cleartext oracle and refuse to emit claims the oracle rejects (export_gmm.py --selftestchecks the$\hat\kappa$ -selection logic standalone)
Portable CP-SNARK commitment linking across circuit families and to a
canonical head commitment (Level B composition; design note tracked outside
the repo), transformer-body proofs, shared-commitment multi-box circuit,
in-circuit