Threat model
23 catalogued attacks, and the checkpoint trap.
Status: living document. Last revised 2026-09-01.
Scope: the Nodus verification layer. The v1 coordinator's own security posture
(no consumer auth, no rate limiting) is documented in ../nodus/README.md
§Security and is out of scope here except where it interacts with verification.
1. Parties and trust
| Party | v1 assumption | Target assumption |
|---|---|---|
| Consumer | trusted to pay | untrusted (may repudiate, may probe) |
| Coordinator | fully trusted — schedules, meters, verifies, settles | reduced: should not be able to silently forge settlement; eventually removable |
| Provider node | trusted to self-report | fully untrusted, rationally malicious |
| Hardware vendor | n/a | trusted only where a mechanism explicitly says so (L4) |
The provider is the adversary this repository is about. We model it as rational and economically motivated, not arbitrarily Byzantine: it cheats when expected profit is positive, and it is capable of engineering effort proportional to the payoff. A rational adversary model is stronger guidance for design than a worst-case one, because it makes the defence a question of expected value rather than of impossibility.
Secondary adversary: colluding providers, since a network with replication or n-of-m voting is only as good as the independence of its replicas.
2. Attack catalogue
Each attack lists the claim it violates, whether Nodus v1 stops it, and the
cheapest known defence. E00x references the experiment that measures the
defence.
2.1 Output attacks (violate Claim A)
| # | Attack | v1? | Cheapest known defence |
|---|---|---|---|
| T1 | Fabricate a plausible output, sign it | not stopped | replication / audit (E009), ZK (E001-006), TEE (E010) |
| T2 | Return a cached output for a repeated prompt | not stopped | freshness nonce bound into the committed statement |
| T3 | Return output from a smaller/cheaper model | not stopped | model-weight commitment + A-proof; or audit (E009) |
| T4 | Return output from a different model entirely | not stopped (model_version is self-attested) |
as T3 |
| T5 | Truncate generation early, claim full length | partly (completion_tokens <= max_tokens) |
coordinator-side re-tokenisation of returned text |
2.1a A threat that is also the default behaviour
E009-pre measured something that does not fit the usual attacker framing: on
this machine, MLX's matmul called with float32 inputs on the Metal GPU
returns results ~1,500× less accurate than the identical call on the CPU stream,
with a constant relative error of 7.7 × 10^-4 — roughly 11 bits of effective
mantissa. Threat T7 is therefore not only something an attacker does; it is
what a mainstream accelerator path does by default, to providers with no intent
to cheat.
Two consequences for this threat model:
- "Honest" is not well defined without a numeric contract. A job specification that names a model but not its numerical semantics does not determine a correct answer, so no verifier can be right about T7.
- The tolerance a network must grant its least precise honest hardware is the budget available to every attacker. This makes the loose-tolerance provider class strategically attractive to cheat from — a Sybil-adjacent incentive worth pricing in the CP-008 economic analysis.
2.2 Execution attacks (violate Claim B / E)
| # | Attack | v1? | Cheapest known defence |
|---|---|---|---|
| T6 | Skip layers / early-exit, output "close enough" | not stopped | segment commitments + random segment challenge (E005, E009) |
| T7 | Quantise more aggressively than specified | not stopped | MEASURED (E009-pre): tolerance-bounded audit does NOT work across devices — honest CPU/GPU divergence is within 3.7× of this attack's signature. Only exact/quantised-integer semantics, or numeric-class partitioning, defends |
| T8 | Fabricate intermediate states to satisfy a checkpoint scheme | n/a | this is the central design trap — see §3 |
| T9 | Sub-contract the job to a cheaper third party and relay | not stopped | not clearly an attack; becomes one if locality/latency is priced |
| T10 | Precompute the answer before the job is assigned | not stopped | unpredictable input / late-binding challenge |
2.3 Metering attacks (violate Claim C / D)
| # | Attack | v1? | Cheapest known defence |
|---|---|---|---|
| T11 | Inflate prompt_tokens in the attestation |
not stopped — settlement uses the attested value (coordinator/jobs.py:419-421) |
recompute from the prompt the coordinator itself sent (near-zero cost) |
| T12 | Inflate completion_tokens |
not stopped, capped only by escrow | recompute by tokenising the returned text |
| T13 | Overstate duration_ms / throughput to win scheduling |
not stopped | coordinator-side timing; reputation |
| T14 | Game the CU-v0 benchmark (run it on better hardware than serves jobs) | not stopped | random re-benchmarking bound to job execution |
| T15 | Claim the same computation for two jobs (duplicate billing) | partly (per-job replay check) | cross-job dedup on (input, model) commitments |
2.4 Protocol and identity attacks (violate Claim O)
| # | Attack | v1? | Cheapest known defence |
|---|---|---|---|
| T16 | Replay a previous result | stopped (per-job) | — |
| T17 | Edit a result in transit | stopped (HMAC) | — |
| T18 | Steal a node token and impersonate | out of scope; token is a bearer credential | asymmetric keys instead of HMAC |
| T19 | Sybil: one machine, many node identities | not stopped | proof of distinct hardware — open problem, relates to Claim D |
| T20 | Collude to pass n-of-m replication | not stopped | independent replica selection, stake, uncorrelated audit |
2.5 Attacks specific to probabilistic verification
| # | Attack | Defence |
|---|---|---|
| T21 | Cheat only on jobs unlikely to be audited (strategic cheating) | audit selection must be unpredictable to the provider at execution time; commit-then-reveal audit seed |
| T22 | Cheat on a small fraction of jobs so expected loss < expected gain | set penalty × detection probability > maximum gain — requires stake, so security becomes an economic parameter, not a cryptographic one |
| T23 | Exit scam: accumulate reputation, then defect at scale | bonded stake with delayed release; cap exposure per epoch |
T22 is important enough to state as a design principle:
Under probabilistic verification, security is bounded by
p_detect × penalty, not by soundness error alone. A verification layer without a slashable stake has no defence against a provider who cheats a little.
Quantified (E016, DERIVED). For layer-skipping under a one-random-layer
audit: a provider skipping m of L layers saves m/L of the job's compute
value V and is caught with probability m/L, so cheating is
negative-expected-value when penalty·(m/L) > V·(m/L) — the m and L cancel:
A stake equal to the job's compute value suffices, at any depth and any level of greed.
Assumes a risk-neutral provider, a collectable penalty, a cheat that is detectable once audited (true under exact arithmetic), and an audit target chosen unpredictably after the provider commits — which requires a commitment to all layer outputs, e.g. a Merkle root. Nodus v1 lacks that commitment today.
3. The checkpoint trap
A tempting and false intuition, explicitly called out so nobody in this project rediscovers it:
"If checkpoint A and checkpoint B are both correct, the computation between them must have been performed correctly."
This is false. Commitment to two endpoint states constrains nothing about the path unless the transition itself is constrained. A prover who can obtain the correct state at B by any means (cache, a different algorithm, a smaller model that happens to agree, or simply having seen this input before) satisfies both checkpoints having skipped the work.
Checkpoints become meaningful only when combined with a constraint on the transition, i.e. one of:
- a proof that
state_B = F_segment(state_A)(a real segment proof — L5), - an interactive challenge over the segment that a cheating prover cannot answer without having the intermediate values (L3),
- a binding that makes state B unpredictable without doing the work (freshness).
Checkpoints alone are an indexing structure, not a verification mechanism. Their value is that they make the challenge cheap: they let a verifier audit 1/N of the computation instead of all of it, once a transition constraint exists. Experiment 005 and 009 test exactly this distinction.
4. Mandatory question
Every mechanism proposed in this repository must answer, in its own
analysis.md:
What can a malicious provider still get away with?
An answer of "nothing" is a red flag and will be treated as an unfinished analysis.