Security posture
This page is the verifier-and-integrator threat model. For the family-wide
threat framework see /ecosystem/security; this file
covers the pledge-specific layer.
The full normative threat list lives in
SECURITY.md.
What a valid pledge cryptographically proves
A kept outcome envelope, when verified end-to-end, gives the verifier:
- A specific Bitcoin address committed. BIP-322 signature over the canonical message, re-checkable byte-exact.
- The proposition resolved as kept under public state. Outcome envelope's evidence section is byte-recomputable from chain / Nostr / HTTP / DNS / Stamp / Vote / counterparty inputs.
- A bond was in scope at swearing time. OrangeCheck attestation referenced in the pledge can be re-resolved against current chain state.
A broken or expired_unresolved outcome proves the inverse — specifically,
that the proposition did not resolve as the swearer committed.
What it does NOT prove
- Personhood. The address may be one human, ten humans, or zero.
- Legal enforceability. The protocol produces evidence admissible in jurisdictions that recognize digital signatures. It does not produce judgments and does not replace courts.
- Future intent. A swearer who has kept ten pledges may break the eleventh. Pledge history is descriptive, not predictive.
- Bond solvency at resolve time. The bond's attestation is re-derivable, but the swearer can spend the bonded UTXO mid-pledge (see Bond-draining below).
Threat model
Address linkability through pledge history
Publishing a pledge attaches a pledge id to a Bitcoin address. Repeated pledges from the same address build a clusterable on-chain trail. Privacy implications:
- Adversaries that already chain-cluster the address learn nothing new cryptographically.
- Adversaries that don't chain-cluster but do observe the pledge history learn the swearer is interacting with the protocol — which may be sensitive in some contexts.
Mitigation. Use a fresh address per context. The economic cost of holding
sats × days per address is real, but per-context unlinkability is achievable.
Consider this an explicit privacy-vs-cost trade-off.
Dispute-gaming via contradictory outcome envelopes
For deterministic mechanisms, a malicious resolver could publish an incorrect
outcome envelope, hoping a verifier reads only that and not the public state. A
second observer notices and publishes a correct contradicting envelope; the
pledge enters disputed.
Mitigation. Verifiers MUST recompute deterministic outcomes from public
state — never trust a published outcome envelope's result field without
re-verifying against the witness. The protocol surfaces
E_OUTCOME_RESULT_MISMATCH for this case.
Bond-draining attacks
The swearer can spend the bonded UTXO mid-pledge. The OrangeCheck attestation
referenced in the bond becomes invalid (UTXO is spent; days_unspent falls to
zero on the new UTXO).
Protocol behavior. This is not automatic reclassification. Per
SPEC §8,
the verifier surfaces E_BOND_SPENT as an error code; the integrator chooses
policy. Strict policies treat a spent bond as automatic break; lenient policies
treat it as informational. Refusing automatic reclassification preserves the
no-slashing rule — the protocol never does
anything to a swearer's status; it only exposes the facts.
Operationally, a swearer who spends the bond mid-pledge is functionally breaking the pledge. Any reasonable verifier policy will treat them that way. The protocol just doesn't enforce it on their behalf.
Race-to-abandon
A swearer foreseeing a break could publish an
abandonment envelope immediately before the foreseeable
break to dodge the broken classification.
Mitigation. Abandonment counts as broken. There is no separate "honorable
exit" status. The protocol records the swearer's honesty (they admitted the
break rather than hiding it) but does not reward it with a different ledger
classification.
Counterparty silence on counterparty_signs
A counterparty who refuses to sign the outcome envelope causes the pledge to
resolve expired_unresolved rather than broken.
Mitigation. expired_unresolved is a third outcome class, queryable
separately. Verifiers building gates can compose any policy they want — count
expired_unresolved as broken for some address classes, ignore it for others.
This was an explicit design call (see State machine) —
conflating expiration with breaking would let lazy counterparties break pledges
by silence.
HTTP non-determinism on http_get_hash
Network conditions, geo-routing, A/B tests, and CDN behaviors can produce different responses to the same URL. Strict-determinism is impossible for arbitrary HTTP.
Mitigation. The SDK requires three independent HTTP fetches and takes the
majority. Disagreements that cannot be resolved by majority fall back to
disputed. This is documented as a non-strict-determinism exception in
SPEC §3.4.5;
integrators choosing http_get_hash accept the trade-off.
Verifier obligations
A correct verifier MUST:
- Re-derive the bond from current chain state — do not trust the pledge's
declared
min_sats/min_days. - Re-execute the resolution for deterministic mechanisms — do not trust a published outcome envelope's result field without re-checking.
- Validate the BIP-322 signature byte-exact against the canonical message.
- Consult ≥ 2 Nostr relays before declaring an outcome envelope absent.
- Honor the dispute window — outcomes are not final until the window passes without contradiction.
A correct verifier SHOULD:
- Cache verification results for the lifetime of the chain snapshot used.
- Re-resolve on every gate hit if the gate is real-time.
- Log the snapshot block hash + height alongside every accepted verification so audits can reproduce the decision.
Reporting
Security issues that may affect verifiers or the reference impl: security@ochk.io. Do not open a public issue for a verifier-impacting bug.