oc · docs
custody contract

orangecheck · Custody contract

Status: this is the design, not today. No federation is bound. /api/federations is the machine-readable answer and it currently serves a single entry with no invite, so no client can join one. While it is in testing, OrangeCheck holds every user's accrued balance and is the party that moves it. That is a stage, deliberately: OrangeCheck operates every role until independent operators take them. Read the descriptor schema, graduation envelope and rotation rules below as the contract that comes into force when a federation binds, and ochk.app/custody as the present tense.

ochk.app is designed to be federation-custodied by default, with a move to your own keys on demand. This page is the user-facing explanation; the protocol-level contract is at oc-attest-protocol/FEDERATION-CUSTODY.md (v1.2-draft-1, additive extension).

Two questions get conflated here, and this page used to conflate them. Self-custody of your signing key is live and real on the BIP-322 path — that key never reaches OC. Self-custody of your sats is not live for anyone. The rest of this page is about the second one. The in-tree audit at NON_CUSTODIAL_AUDIT.md walks the gap with file paths.

What "federation-custodied" will mean

Once a federation is bound, your orangecheck account is backed by a Bitcoin address (BIP-322 path) or a federation-issued ecash note set (email-OTP path). For the federation path, the private key that controls the underlying on-chain UTXOs is held collectively by an M-of-N guardian set — no single guardian (and not OC the company) can spend or freeze. Recovery is by re-proving your email, not by remembering twelve words. The guardians threshold-sign on your behalf when you sign in; you don't see it and never need to.

None of that is in force. The guardians that once ran this were torn down in July 2026, and the browser-side Fedimint client that would have joined a federation was removed from oc-me-web in September 2026 — it could only ever reach its own error path.

The three options at threshold

signing_methodhow it workslive today?
federation custody (the destination)fedimint_thresholdguardians collectively hold the key. recovery by re-proving email.no. no federation is bound, so no client can join one — /api/federations publishes that state.
fedimint clientfedimint_threshold (different federation)sweep to a fedimint federation you trust more (your community's, your country's, your own).opens once (1) binds; depends on the graduation envelope.
self-custodybip-322sweep to any BIP-322-capable Bitcoin wallet (UniSat, Xverse, Leather, Alby, Sparrow, hardware wallet). only you can sign.sign-in is live (in-place, no redirect) and the signing key is yours. the sat sweep is not — there is no federation balance to sweep.

The protocol accommodates the round-trip back to federation custody via attestation re-issuance under the new custody descriptor.

Graduation envelope

Per the v1.2-draft-1 oc-attest extension, graduation publishes an envelope carrying both an M-of-N guardian quorum sig (federation releases custody) and a self-key sig (you prove you hold the key now). The envelope is anchored to a bitcoin block via OpenTimestamps so any verifier can confirm graduation happened at a specific time.

{
    "v": 1,
    "kind": "orangecheck-graduation",
    "address": "bc1q...",
    "from_federation": "fed:abc123…",
    "to": "single-key",
    "graduated_at": "2026-04-30T20:00:00Z",
    "proof": [
        { "address": "bc1qg1…", "sig": "..." },
        { "address": "bc1qg2…", "sig": "..." },
        { "address": "bc1qg3…", "sig": "..." },
        { "address": "bc1q...", "sig": "...", "kind": "self-key" }
    ]
}
FieldRule
from_federationContent hash of the federation custody descriptor releasing the address.
to"single-key" (graduating to BIP-322) or "federation" (re-binding to a new descriptor).
proofM signatures from M of N declared guardians PLUS one self-key signature.
graduated_atISO-8601 timestamp; the OTS proof anchors this to a Bitcoin block.

The envelope contract is finalized at the spec level and reflected in the /me/balance UI, which states today what it can and cannot do. The producing path — collecting a quorum signature from a real federation — opens when a federation binds.

Guardian descriptor

A federation custody descriptor is a content-addressed canonical JSON object — same shape as oc-agent-protocol's federation principal, re-purposed for custody:

{
    "v": 1,
    "kind": "attest-federation-custody",
    "address": "bc1q…",
    "threshold": "3-of-5",
    "guardians": [
        { "address": "bc1qg1…", "alg": "bip322", "name": "alice" },
        { "address": "bc1qg2…", "alg": "bip322", "name": "bob" },
        { "address": "bc1qg3…", "alg": "bip322", "name": "carol" },
        { "address": "bc1qg4…", "alg": "bip322", "name": "dave" },
        { "address": "bc1qg5…", "alg": "bip322", "name": "erin" }
    ],
    "implementation": {
        "kind": "fedimint",
        "federation_id": "fed11qgqzcq…",
        "version": "0.4"
    }
}

The descriptor id is fed: + lowercase hex SHA-256(canonical_descriptor_bytes).

What stays the same when you move to your own keys

Stays the sameChanges
Your bitcoin address (the canonical identifier)The signing method (fedimint_threshold → bip-322)
Your backing, if you backed your account with a coinWho holds the private key (you, instead of the federation)
Your /me/earn history, connected sites, agent delegationsHow recovery works (you own backup; lose the key, lose the funds)
Your sat-earning rateThe shape of signing_method in attestation envelopes

Guardian rotation

Guardian rotation produces a new descriptor with a new id. Old attestations remain verifiable against the old descriptor; new attestations bind to the new descriptor. Rotation envelopes are authenticated by M-of-N signatures from the old descriptor and OTS-anchored so any verifier can resolve the active descriptor at any historical instant.

Reading further