Sovereign Circuit · Born Between 2 Generals
Dual Attestation Protocol
Sealed commit–reveal valuation for used hardware. Both sides price the unit privately, both publish a cryptographic commitment before either number is visible, then both reveal. Close numbers settle at the midpoint automatically. Distant numbers are broken by a published index, not by whoever has more leverage.
The single property this buys: neither side can move their number after seeing the other side's number.
1 What this protocol is for
Every hardware program that takes equipment in and gives value back has the same unsolved problem: somebody has to say what the used thing is worth.
In practice one side says it and the other side accepts it. That is how phone carrier trade-ins work, and it is the most resented moment in the entire subscription-hardware model. The customer hands over a working device, is told a number they have no way to check, and has no recourse. The vendor holds all of the information and all of the discretion.
Dual Attestation removes the discretion. It does not ask either party to trust the other. It makes the sequence of events cryptographically impossible to game.
1.1 The doctrine it comes from
The rest of the portfolio already refuses to present an assertion as evidence. This protocol applies the same rule to money: the holder's claim is an assertion, the inspector's finding is an observation, and the sealed record keeps both rather than collapsing them into a single price. The settled figure is derived from the two, and the derivation is published.
1.2 Where it is used
| Moment | Holder side | Inspector side |
|---|---|---|
| Intake of a retiring fleet | The supplying company | The program bench |
| Residual at scheduled upgrade | The subscriber | The program bench |
| Cascade re-grade between tiers | Outgoing tier record | The program bench |
| Certified material recovery | The program | The recovery partner |
Residual at scheduled upgrade is the exact moment carriers lose customer trust. Doing it under sealed dual attestation turns the most resented event in the model into the most defensible one.
3 Cryptographic dependencies
| Function | Standard |
|---|---|
| Hash | SHA-256 — FIPS 180-4 |
| Canonical serialization | RFC 8785, JSON Canonicalization Scheme |
| Nonce generation | CSPRNG per NIST SP 800-90A Rev. 1 |
| Transport authentication | HMAC — RFC 2104 |
| Party signatures (optional) | Ed25519 — RFC 8032 |
| Independent timestamping (optional) | RFC 3161 TSP |
The construction is the standard sealed-bid primitive: publish a hash of your figure plus a blinding nonce, then reveal, and the hash proves the revealed value is the one you were bound to (Chainlink on commit-and-reveal). The known failure mode of naive commit–reveal is the last-revealer advantage — whoever reveals last can compute the outcome and choose to abort. §6.4 addresses that directly, and it is the reason non-reveal is penalised rather than merely logged.
No floats, anywhere
All money is integer cents; all rates are integer basis points. The reference implementation raises on any float rather than canonicalizing it, because IEEE-754 serialization differences between two implementations would silently produce non-matching digests — the worst possible failure, since it looks like fraud.
Domain separation
Every digest is prefixed with exactly one tag —
SCDA1|commit|, SCDA1|record|,
SCDA1|round| — so a digest computed for one purpose can never be
replayed as a digest for another. It costs nothing and there is no reason to omit
it.
4 Commitment
C = SHA256( "SCDA1|commit|" || JCS(commit_body) )
{
"protocol": "sovereign-circuit/dual-attestation/1",
"round_id": "R-2026-08-0417",
"asset_id": "SC-LT-000391",
"party_id": "ACME-HOLDINGS",
"role": "holder",
"amount_cents": 42000,
"grade": "B2",
"nonce": "a1a1a1a1…a1a1"
}
4.1 Why each field is inside the digest
- round_id
- Binds the commitment to one round. Without it, a commitment can be replayed in a later round where the same number happens to be favourable.
- asset_id
- Binds it to one unit.
- party_id + role
-
Prevents a commitment from being attributed to the other side. Without
role, an adversarial coordinator holding both commitments could swap which is which after seeing the reveals and pick the better outcome. - amount_cents + grade
- The substance. Grade is committed alongside amount so a party cannot retroactively justify a price with a different condition claim.
- nonce
- Hiding. Not optional, and 32 bytes is not excessive. The plausible price range for a used business laptop is a few thousand distinct cent values in practice. Without a nonce, anyone holding the commitment can enumerate the entire space in microseconds and read the sealed amount. The nonce is what makes the commitment actually sealed.
- 32 bytes from a CSPRNG, fresh per party per round.
- MUST NOT be derived from the amount, the round id, a timestamp, or a counter.
- Reuse across rounds by the same party leaks equality of amounts.
- Published at reveal. It is not a long-term secret and MUST NOT be reused as a key.
5 Round lifecycle
-
01
Open
The coordinator publishes
round_id,asset_id, the policy, the commit deadlineT_c, the reveal deadlineT_r, and the index source that would be used on escalation.Load-bearing rule:
policy_hashMUST be published before the commit window opens. If the policy could change afterwards, the coordinator could pick a tolerance band that produced the outcome it preferred. -
02
Commit
Each party values the unit without access to the other's figure and publishes only
C. The coordinator MUST NOT disclose either commitment to the counterparty beforeT_ccloses; publishing both afterwards is encouraged, because it lets each side confirm the other was genuinely bound before reveals began.If only one party commits, the round is void and reopened. No default applies at commit stage — a party who never committed was never bound, and treating silence as agreement to a number they never saw would be unfair.
-
03
Reveal
Each party publishes
amount_cents,gradeandnonce. The coordinator recomputesCand rejects any reveal that does not match. Reveal ordering is not significant to the arithmetic. -
04
Settle
Deterministic. Given the same two revealed amounts and the same policy, every conforming implementation MUST produce the same result to the cent.
-
05
Seal
The round record is appended to the hash chain and the chain head advances. Any later edit breaks every subsequent link.
6 Settlement
6.1 Policy
{
"tolerance_bp": 1000, // 10.00% auto-settle band
"floor_cents": 2500, // low-value absolute gap
"absolute_floor_bp_exempt_cents": 5000,
"bonus_bp": 250, // verified-condition bonus
"bonus_inner_bp": 300, // bonus band
"bonus_cap_cents": 7500,
"late_reveal_penalty": "counterparty_amount",
"index_name": "SC-COMP-A"
}
policy_hash = f5bbc16aaba884177e6581aa528aa6b84c489aa842e592a9998909154ed5c0c1
6.2 Spread
spread_bp = round_half_up( (max − min) × 10000 / min )
Measured against the lower of the two amounts. This is the conservative choice: it produces a larger number than measuring against the midpoint or the higher amount, so the band is harder to satisfy. Any round that auto-settles under this definition would also auto-settle under the looser ones. In plain language, the gap is expressed as a percentage of the smaller number.
6.3 The ladder
Inside the band → midpoint
If spread_bp ≤ tolerance_bp, settle at
round_half_up((holder + inspector) / 2). Half-up, not banker's
rounding: two independent implementations must agree to the cent, and half-up is
what a non-technical participant expects when they check the arithmetic by hand.
Low-value exemption → midpoint
On a $32 accessory an $8 gap is 2500 bp and means nothing, and escalating it
costs more than the item. If both amounts are small and the absolute gap is
under floor_cents, settle at the midpoint regardless of percentage.
Outside the band → index, clamped
amount = max( min_revealed, min( max_revealed, index ) )
The clamp is not merely defensive. It guarantees escalation can never land outside what both parties actually claimed. If the index says $200 while the holder said $900 and the inspector said $300, settlement is $300 — the inspector's own number. A stale, thin or manipulated index cannot be used to pay a participant less than the program's own bench assessed.
Verified-condition bonus
If spread_bp ≤ bonus_inner_bp, add bonus_bp of the
settled amount, capped at bonus_cap_cents.
This is the economic engine and it is not goodwill. It pays participants for accurate self-assessment. A holder who grades honestly lands inside the inner band and earns more than a holder who inflates. Across a fleet that converts inspection from an adversarial cost centre into a spot-check, because incoming grades are already close. Cheaper inspection is the return on the bonus.
6.4 Non-reveal
If one party commits and then fails to reveal by T_r, settlement is
the counterparty's revealed amount, no bonus is paid, and the
default is recorded as such.
The revealing party does not get to name the price unilaterally. If they did, there would be a direct incentive to induce a non-reveal — delay the counterparty, run out the clock, then name any number. Settling at the counterparty's sealed figure is the only reading that does not reward gamesmanship.
6.5 What the protocol deliberately does not do
- It does not decide whether a unit is worth anything. That is the grading rubric.
- It does not price subscriptions. Published Sentinel prices remain a floor and are not undercut by any credit produced here.
- It does not arbitrate disputes about physical condition. It records both positions and settles the money; a condition dispute is a separate process.
Run a round in your browser.
This is the real protocol, not a mock-up. Hashes are computed with SHA-256 via the Web
Crypto API over RFC 8785-canonical JSON, using the same domain tag as the reference
implementation — the commitment for the §10.1 vector inputs reproduces
f40ccaef… byte for byte. Nothing leaves your device.
—
—
—
7 Security analysis
| Attack | Mitigation |
|---|---|
| Move your number after seeing theirs | Commitment published before reveal; the digest will not match. |
| Brute-force a commitment to read the sealed amount | 32-byte nonce. The search space is 2²⁵⁶, not the price range. |
| Replay a commitment into another round or unit | round_id and asset_id are inside the digest. |
| Swap which commitment belongs to which side | party_id and role are inside the digest. |
| Change the rules after seeing the numbers | policy_hash is published before the commit window opens. |
| Last revealer aborts an unfavourable round | §6.4 — non-reveal settles at the counterparty's amount. |
| Coordinator leaks a commitment to the other party | Commitments are hashes; leaking one reveals no amount. Leaking a reveal early does break the protocol, so reveals MUST be gated. |
| Manipulate or stale-date the index | Index is only reachable outside the band, and is clamped into the revealed range. |
| Silently rewrite a settled round | Hash chain — any edit changes the entry hash and breaks every later link. |
7.1 Residual weakness: the coordinator is not neutral
In most rounds the program is both the inspector and the coordinator. The cryptography stops it changing its own number after seeing the holder's, and stops it rewriting a sealed round. It does not structurally stop it mis-implementing the arithmetic, or withholding a reveal it dislikes and claiming default.
Three mitigations, increasing in strength: publish the reference implementation and vectors so any holder can recompute their own settlement; add RFC 3161 timestamping so commitment timing is attested by a third party rather than the program's own log; and for high-value rounds add a third attesting party — the supplier's auditor or insurer — committing alongside. The protocol generalises to n parties without structural change: the band test becomes max-versus-min across all reveals and settlement becomes the median rather than the midpoint. That is a v1.1 extension and is not specified here.
Honesty about this is the point. The protocol makes a specific, verifiable promise and does not pretend to make a larger one.
8 The sealed record
{
"protocol": "sovereign-circuit/dual-attestation/1",
"round_id": "R-2026-08-0417",
"asset_id": "SC-LT-000391",
"policy_hash": "f5bbc16a…d5c0c1",
"commitments": { "holder": "…", "inspector": "…" },
"reveals": {
"holder": { "amount_cents": 42000, "grade": "B2", "nonce": "…" },
"inspector": { "amount_cents": 39000, "grade": "B3", "nonce": "…" }
},
"settlement": {
"outcome": "auto_settled",
"amount_cents": 40500,
"spread_bp": 769,
"bonus_cents": 0,
"total_cents": 40500,
"reason": "spread 769 bp within 1000 bp band; midpoint settlement"
},
"timestamps": { "commit_opened": "…", "reveal_closed": "…", "settled": "…" }
}
The record retains both revealed amounts, not just the settled figure.
The holder's claim stays visible as an assertion and the inspector's finding stays
visible as an observation. Collapsing them into one price would destroy the evidentiary
value of the record. The reason string is human-readable on purpose: a
participant reading their own record should see the arithmetic without a decoder.
entry_hash = SHA256( "SCDA1|record|" || JCS({ prev, seq, body }) )
Genesis prev is 64 zeros. This reuses the audit-chain pattern already used
elsewhere in the platform. A Merkle log per
RFC 6962 is the natural upgrade
once efficient inclusion proofs are needed — a holder proving their single round is in
the log without downloading it. A linear chain is sufficient at pilot volume and far
easier to audit by reading.
10 Normative test vectors
A conforming implementation MUST reproduce all of these exactly. Every value below was produced by running the reference implementation, not written by hand.
Commitment
round_id R-2026-08-0417
asset_id SC-LT-000391
party_id ACME-HOLDINGS
role holder
amount_cents 42000
grade B2
nonce a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1
C = f40ccaef3378cbfb0574d0499f8a8d3bfd35d2990edcf7943e5f163ed2db9165
Mutating amount_cents to 42001, round_id to
R-2026-08-0418, or role to inspector must all be
rejected against that commitment.
Settlement
| # | Case | Holder | Inspector | Index | Spread | Outcome | Settled | Bonus | Total |
|---|---|---|---|---|---|---|---|---|---|
| TV-1 | inside band | 42000 | 39000 | — | 769 | auto | 40500 | 0 | 40500 |
| TV-2 | identical figures | 40000 | 40000 | — | 0 | auto | 40000 | 1000 | 41000 |
| TV-3 | edge, exactly at band | 44000 | 40000 | — | 1000 | auto | 42000 | 0 | 42000 |
| TV-4 | just outside band | 44100 | 40000 | 41500 | 1025 | escalated | 41500 | 0 | 41500 |
| TV-5 | index below revealed range | 90000 | 30000 | 20000 | 20000 | escalated | 30000 | 0 | 30000 |
| TV-6 | inner band, bonus | 41000 | 40000 | — | 250 | auto | 40500 | 1013 | 41513 |
| TV-7 | low-value exemption | 4000 | 3200 | — | 2500 | auto | 3600 | 0 | 3600 |
| TV-8 | non-reveal default | 38500 | — | — | n/a | defaulted | 38500 | 0 | 38500 |
Why TV-5 exists
The clamp doing its job. The index said 20000, below the inspector's own 30000, so settlement is 30000. The program cannot use a low index to pay a participant less than its own bench assessed.
Why TV-6 exists
250 bp of 40500 is 1012.5, which rounds half-up to 1013. This vector exists purely to pin the rounding direction so two implementations cannot drift by a cent.
Hash chain
entry[0].hash = 5472ffe25bd5e7e2f2cbfc38351017781c8280d6f14670c4c3bbb246c787f133
entry[1].prev = 5472ffe25bd5e7e2f2cbfc38351017781c8280d6f14670c4c3bbb246c787f133
entry[1].hash = 375e31cf0216123658529e6575925543c865b83964def4393a34625b8b1ae1ad
head = 375e31cf0216123658529e6575925543c865b83964def4393a34625b8b1ae1ad
Changing settlement.amount_cents in entry 0 from 40500 to 41000 makes
verification fail. That tamper case is asserted in the test suite.
11 Open items
These are genuinely unresolved and are called out rather than papered over.
-
The grading rubric biggest dependency
The protocol is only as fair as the grade tokens it commits to. The rubric needs testable criteria — firmware support status, TPM version, RAM, storage class, battery health — so a demotion reason is objective rather than a judgment call.
-
The index does not exist yet
SC-COMP-Aneeds a defined construction, publication cadence, source list and archive, so a historical round can be re-verified against the value that was current at the time. An index that can be silently restated is not a tie-breaker. -
Tolerance band calibration
1000 bp is a starting guess. It should be set from real paired data — bench grade versus holder self-grade across a first batch — not chosen because it is a round number.
-
Bonus cap
$75 is a placeholder. The cap must sit below the marginal cost of a full inspection, or the bonus stops paying for itself.
-
Deadlines
T_candT_rneed defaults. Too short disadvantages a holder without technical staff; too long stalls the loop. -
Identity and signatures
Ed25519 signing is optional today. It becomes necessary the moment a participant might deny having committed. Key distribution is unspecified.
12 Legal note
Do not market any part of this as insurance, and do not describe the settled figure as an appraisal, until counsel has reviewed it.
Both are regulated terms of art. The distinction between a warranty, a service contract and insurance turns on who bears the risk and whether the obligor is the party providing the goods (IRMI). Service-contract obligors face state-by-state registration and reserve requirements (NCOIL), states such as Virginia have specific statutes (Extended Service Contract Act), and California draws the line by obligor (dealer-obligor versus mechanical breakdown insurance).
The safe framing for this protocol is a contractual valuation mechanism inside a subscription agreement. The program is not producing an appraisal; it is producing a contractually agreed figure derived from two sealed inputs under published rules. That is a materially more defensible claim — and a more accurate description of what actually happens.
| Use | Avoid |
|---|---|
| settled value, agreed value | appraised value, fair market value opinion |
| protection plan, swap-on-failure | insurance, coverage, policy |
| participation credit | rebate, cash-back, payout |
| service-level obligation | claim, indemnity |
13 Why build this first
It requires no hardware. The entire protocol is specifiable, implementable, testable and demonstrable before a single unit is acquired — as this page and its passing vectors demonstrate.
It is also the best sales artifact the program has. A supplying company's procurement or finance team does not need persuading that a laptop can be wiped. They need persuading that they will not be low-balled on a fleet. A working demonstration where they commit their own number, watch the program commit blind, and see the midpoint settle automatically is a more convincing five minutes than any deck.
The artifacts
Everything on this page exists as a file. Nothing here is a rendering of something that only lives in a slide.
-
SPEC.mdThe normative specification, 13 sections, plain Markdown. -
attestation.pyThe reference implementation. Python standard library only — no dependencies to install, nothing to trust beyondhashlib. -
test_spec.pyAsserts every vector in §10 plus the tamper and invariant cases. Runpython test_spec.py; it prints one line. -
test-vectors.jsonThe same vectors, machine-readable, for a second implementation to check against. -
make_vectors.pyRegenerates the vectors, so a change to policy defaults cannot silently drift.