Repository navigation
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Critical model and harness issues remain, along with a moderate invariant concern.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (2)
What changed in this PR
Adds a Quint model of the Zeronym divert protocol and runs its checks in CI.
Changes:
- Models queueing, lookup, publication, and unreliable network behavior.
- Checks known fixes and documented protocol gaps.
- Adds a dedicated Quint specification CI job.
| File | Summary | Review findings |
|---|---|---|
zeronym/spec/divert.qnt |
Protocol model and invariants | Critical (3 votes): A lying hub can expose queued or absent data as tx, allowing noEarlyBytes to pass despite early bytes. Moderate (1 vote): The L4 invariant is tautological and cannot detect a guard violation. |
zeronym/spec/check.sh |
Simulation and expected-outcome checks | Critical (3 votes): Every non-zero Quint exit is treated as an expected counterexample, allowing execution failures to pass silently. |
.github/workflows/zeronym-guards.yml |
CI job for the model | No specific findings. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…are check.sh From review on #82. Lying replies were tagged hubSaw "lie", which exempted them from noQueuedBytes and noEarlyBytes, so a lying hub releasing a queued migration's bytes passed as safe. Replies now carry the hub's real state plus an `honest` ghost flag. The shim's claims (guardHolds, pendingVisible) are checked against a lying hub as shimSafety; the hub's claims (noQueuedBytes, noEarlyBytes) are checked against an honest hub, and current.noEarlyBytes is a new expected failure recording that attestation is what stands between a queued migration and early release. check.sh read every nonzero exit as a counterexample, so a crash, a download failure or a misspelt invariant passed each "fails" row. It now classifies runs by Quint's [ok] / [violation] line and fails the gate on anything else. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The model currently masks an unresolved #80 regression, and the verification count is inaccurate.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
Resolved since last review (2)
zeronym/spec/divert.qnt models the wallet, shim and hub over a mixnet that loses, duplicates and reorders messages, with a hub that can be set to lie. It mirrors the get_transaction reply arms in intercept.rs, the queue-first lookup in server.rs, admission in queue.rs and the epoch flush in batcher.rs. check.sh simulates eight module/invariant pairs and asserts each outcome: - the four safety claims hold for the current code (L4 guard, queued bytes stay in the hub, a queue hit reads as pending, bytes only after publish); - ecb4641 and 45e408f reappear with their fixes switched off; - three known gaps stay visible: silent refusal over Nym, a lying hub faking pending, and the flush-window pending -> NOT_FOUND flip. It runs as a cheap-tier job in zeronym-guards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…are check.sh From review on #82. Lying replies were tagged hubSaw "lie", which exempted them from noQueuedBytes and noEarlyBytes, so a lying hub releasing a queued migration's bytes passed as safe. Replies now carry the hub's real state plus an `honest` ghost flag. The shim's claims (guardHolds, pendingVisible) are checked against a lying hub as shimSafety; the hub's claims (noQueuedBytes, noEarlyBytes) are checked against an honest hub, and current.noEarlyBytes is a new expected failure recording that attestation is what stands between a queued migration and early release. check.sh read every nonzero exit as a counterexample, so a crash, a download failure or a misspelt invariant passed each "fails" row. It now classifies runs by Quint's [ok] / [violation] line and fails the gate on anything else. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
768e476 to
4a456c9
Compare
From review on #82: the job runs code fetched at run time (npx, Quint's evaluator), so it should run with a SHA-pinned checkout, no persisted credentials, and contents: read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The CI matrix omits the documented lying-hub noQueuedBytes counterexample, allowing an expected failure to disappear without failing the gate.
Review effort: Lite
Findings: None
Resolved since last review (2)
From review on #82: divert.qnt documents that a lying hub breaks both hub claims, but check.sh asserted only noEarlyBytes, so the noQueuedBytes gap could disappear without failing the gate. Ten rows now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An honest hub forwards a Found with an empty body and height 0, and the shim renders pending. A conforming indexer does not send that image; the model now can, and pendingIsTrue fails when it does. Co-authored-by: Cursor <cursoragent@cursor.com>
pendingVisible ignores every reply the hub was not following its code to send, so it is not a claim about an arbitrary hub. The any-reply bundle no longer includes it. Co-authored-by: Cursor <cursoragent@cursor.com>
guardHolds repeated the only assignment of status tx, so a hub could not falsify it. The mismatch arm and the queue-hit sentinel are now runs. Co-authored-by: Cursor <cursoragent@cursor.com>
The lie was only a found reply. It can now be not-found or error, and a queued migration reported not-found is an expected failure. Co-authored-by: Cursor <cursoragent@cursor.com>
Once the wallet has seen a transaction's bytes, a later pending or not-found is a regression. Reorder, a resend after publication, and the other hub's not-found each reach it. The seeded sample does not, so the runs are the check. Co-authored-by: Cursor <cursoragent@cursor.com>
find_by_txid matches a queue entry only when its txid is the query. An unparseable body stays queued and answers not-found, and pending is only claimed for a body that has a txid. Co-authored-by: Cursor <cursoragent@cursor.com>
This reverts commit 62ec0d9.
L4 turns a mismatched found into status not-found. The suppression check follows the reply's disposition, so that mismatch is not the witness. Co-authored-by: Cursor <cursoragent@cursor.com>
This reverts commit c22aa12.
Co-authored-by: Shoaib Ahmed <sufialhussaini@gmail.com>
spec(zeronym): record the free junk burst as finding 13
…claude/spec-hub-substitution # Conflicts: # zeronym/spec/quint/README.md
docs(zeronym/spec): add the hub-hop and lookup threats from the third report
A rejection or a mined verdict burns the txid, refuses a later signing of it, and drops every queued entry that carries it. The burn set's bound stays unmodelled. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🟡 Changes recommended
A moderate active finding remains in zeronym/spec/quint/wire.qnt: the lookup arm can return an unparseable payload when its txid matches, diverging from the Rust behavior and lacking the requested regression coverage.
3 open issues
Review effort: Lite
…t flush only when idle
There was a problem hiding this comment.
🔵 Needs a closer look
Human assessment is needed because the evidence indicates a critical mismatch between the modeled burn/nullifier behavior and the shipped Rust admission logic, making the apparent verification coverage unreliable.
2 resolved since last review
Review effort: Lite


Adds a Quint specification of the zeronym protocol (wallet, shim, hub, indexer and chain, over a mixnet that loses, duplicates and reorders messages) and checks it in CI. No Rust is changed.
zeronym/spec/quint/has two layers:protocol.qnt): the components as pure functions, the hub abstract, one Byzantine component at a time. Checked byquint testand seeded simulation.hubMachine.qnt): the real hub function on a chain clock, checked exhaustively with TLC for the schedule guarantees (G6b, G6c) and known gaps.zeronym/spec/quint/README.mdhas the threat model, guarantees, trust matrix, known gaps and findings against the Rust.CI (
zeronym-guards):spec-protocolruns tiers 1 to 3b (typecheck, tests, simulation);spec-protocol-tlcruns tiers 1 and 4 (TLC, Java 21). Quint 0.33.0 and Apalache 0.62.1 are pinned.What it found
Eleven findings against the Rust, listed with their evidence in the README. Five are classed as code bugs: one HIGH (a single indexer endpoint reporting a far-ahead tip pins the hub's tip), three MEDIUM (two of which share a root cause) and one LOW. None is fixed here. Each was matched to the code by reading; nothing was reproduced against it.
What "checked" means
The hub spec is exhaustive under TLC for small, scaled-down configurations. The protocol spec's guarantees are bounded random simulation, not proof. One hub and the mixnet transport only.
Verification
spec-protocolandspec-protocol-tlcpasssh zeronym/spec/quint/check.sh: all tiers green locally (Quint 0.33.0, Rust evaluator, OpenJDK 27)sh zeronym/guards.shgreen🤖 Generated with Claude Code