OC Pledge — Quickstart
End-to-end: construct a pledge, sign it, publish to Nostr, verify the outcome. About five minutes if you already have an OrangeCheck attestation; ten if you need to mint one first.
1. Install
yarn add @orangecheck/pledge @orangecheck/auth-client
The SDK reaches v0.1.0-beta shortly. While it ships, the protocol spec is
already
frozen
and 28 conformance vectors are public — any conforming implementation
(yours included) will produce byte-identical canonical messages.
2. Construct a pledge
import { createPledge } from '@orangecheck/pledge';
const { envelope, canonical_message, pledge_id } = await createPledge({
swearer: 'bc1qexampleaddress…',
proposition: 'I will publish my Q3 results by block 920_000.',
resolution: {
mechanism: 'stamp_published',
query: 'content_hash:sha256:7d…',
},
resolves_at: { block: 920_000 },
expires_at: '2026-08-01T00:00:00Z',
bond: {
attestation_id: '<your OrangeCheck attestation id>',
min_sats: 1_000_000,
min_days: 90,
},
});
The SDK refuses any combination that can't resolve to a deterministic
boolean — for example a proposition paired with a non-existent
mechanism, a resolves_at past expires_at, or an attestation_id the
SDK can't verify exists.
3. Sign with the wallet
import { signPledge } from '@orangecheck/pledge';
import { sign } from '@orangecheck/wallet-adapter';
const sig = await sign({ address: swearer, message: canonical_message });
const signed = signPledge({ envelope, sig });
The wallet shows the signer the exact canonical message before signing — human-readable, plaintext. The user is committing to bytes they can read.
4. Publish
import { publish } from '@orangecheck/pledge';
const { event_id } = await publish(signed);
// → posted to default Nostr relays as a kind-30078 replaceable event
// under d-tag `oc-pledge:<pledge_id>`.
5. Verify the outcome
import { resolvePledge, verify, getState } from '@orangecheck/pledge';
// At any time after `resolves_at`:
const outcome = await resolvePledge({ envelope: signed });
// → outcome envelope: { outcome: 'kept' | 'broken' | …, evidence: {…} }
const result = await verify({ envelope: signed, outcome });
// → { ok: true, state: 'kept', bond_status: 'satisfied', … }
const state = getState(signed, outcome, Date.now());
// → 'kept'
For deterministic mechanisms (chain_state, nostr_event_exists,
stamp_published, http_get_hash, dns_record), every observer with
access to the same public state produces the byte-identical outcome
envelope — no resolver is required.
For counterparty_signs and vote_resolves, the outcome envelope must
be signed by the designated resolver. Absence of that signature within
expires_at resolves the pledge to expired_unresolved.
6. Show the public history
import { pledgesSworn, outcomesFor, bondInFlight } from '@orangecheck/pledge';
const ids = await pledgesSworn('bc1q…');
const outcomes = await outcomesFor('bc1q…');
const inFlight = await bondInFlight('bc1q…');
These are raw metrics. The protocol ships no aggregated score and no reference reputation function — by design. Platforms compute derived policies from raw queries; that's the composition surface.
What's next
- How it works — the canonical message, the seven resolution mechanisms, the outcome envelope shape
- Resolution grammar — every mechanism in detail
- Composition with the family — gate predicates, agent-delegated pledges, vote-resolved disputes
- Specification — normative rules + 28 conformance vectors