- funds
- BIP-322 users: untouched (own wallet). Email-OTP users: also untouched — the federation's guardians are independent operators (vetted via /operator), not OC. The new owner cannot make threshold-signed federation withdrawals; they would have to corrupt enough independent guardians to reach threshold, which is the property the federation's charter prohibits.
- identity
- New owner controls OC_AUTH_PRIVATE_JWK so could mint sessions. They cannot forge BIP-322 signatures from your wallet.
- historical envelopes
- All anchored envelopes verify offline forever. Independent of OC infra.
- signing new envelopes
- BIP-322 users sign as before. Email users authenticate to the federation directly via their mnemonic; the new owner cannot interpose.
What OC stores. What nobody holds. What happens if OC disappears.
The non-custodial claim is the most important commitment in the product. This page reports actual implementation state — what is persisted in OC infrastructure, what enables fund movement (nobody, today), what happens in the four standard disappearance scenarios, and what recovery looks like. Backed by an in-tree NON_CUSTODIAL_AUDIT.md that walks the eight audit questions with file paths.
every field, every store, every category.
The list below is what an auditor would find in OC’s production infrastructure today — Supabase rows on ochk.io, Upstash Redis indexes on me.ochk.io, OTS calendar entries (public), Nostr relays (public). Every item is metadata-only; none of it enables OC to sign on a user’s behalf.
| field | where it lives | notes |
|---|---|---|
| account row | oc-www · Supabase `accounts` table | did_oc (opaque · sha256-derived random; same shape regardless of signup path), display_name, nostr_npub, created_at, last_signed_in_at. The underlying email or BTC address is in linked_identities, encrypted at rest (email) or stored plaintext (BTC, since BTC addresses are public on-chain). No password hash. No private key. No mnemonic. |
| session jti | oc-www · Supabase `sessions` revocation list | just the JWT id, account id, issued_at, revoked_at. The session JWT itself lives only as a cookie on the user’s browser, signed by OC’s Ed25519 auth key. |
| envelopes (per billable event) | oc-me-web · Upstash Redis (events-store) | content-addressed envelope: id, project_key, user_address, fee breakdown, occurred_at. Each envelope is signed at write time with the me.ochk.io envelope-signing key (Ed25519, public JWK at me.ochk.io/.well-known/oc-envelope-jwks.json) — distinct from the auth host JWT key. Indexed by both project_key AND user_address; see "the cross-site graph" section below. |
| rebind envelopes (per custody-state transition) | oc-me-web · Upstash Redis (rebind-store) | records every fedimint_threshold ⇄ fedimint_client ⇄ bip322 transition. Same content-addressed scheme + envelope-signing key as billable events. Surfaced on /verify/[id] and /me/identity (the user's own custody-state timeline). |
| projects, webhooks, sends, payments, attest tiers, notifications | oc-me-web · Upstash Redis (per-store) | project_key + IntegratorPriceConfig + JWK URL pattern + domain verification state. Webhook endpoint URLs + rolling latency stats. Outflow ledger entries. All metadata; no signing capability. |
| OpenTimestamps proofs | public · OTS calendars | public by design. Anyone can fetch + verify against Bitcoin headers without OC online. |
| Nostr-published events | public · 4 relays (damus, nostr.band, nos.lol, snort.social) | kind-30078–30099 family. Independent of OC; relays continue to serve if OC vanishes. |
| OTP token (email-OTP signin · 10-min window only) | oc-www · stateless JWT carried by the browser | NOT stored server-side. {email, sha256(code), exp} signed with the OC auth key; the user holds it between /start and /verify. |
nothing. that is the whole design.
You hold your own Bitcoin wallet. OC sees only signatures and public addresses. No mnemonic, no private key, no recovery factor that can move your funds reaches OC infrastructure at any point. If OC vanished tomorrow, your wallet is unaffected.
The Fedimint web client is shipped: when you sign up via email-OTP, a federation-held wallet provisions in your browser, and the mnemonic stays with you. OC provisions federations — four guardian seats with distinct hardware-backed operator keys · operational independence is enforced by operator-key diversity, not vendor diversity; the cryptographic invariant survives even when daemons share a vendor, and OC holds zero shares. oc-me-1 has completed its DKG ceremony and is live; email-OTP users provision a federation-custodied wallet against it under their opaque did:oc identity. The remaining gated piece is the federation's Lightning gateway that routes cash-out. Users can link a Bitcoin address now to receive direct payouts without waiting. Status: /custody.
Architectural note (per the in-tree audit, §2.4): at no point in either signin path does any OC-operated process hold material that could move user funds. BIP-322 keys live in the user’s wallet; federation key shares live with the federation’s guardians, never OC. The OC database is metadata only.
every sign-in is a bitcoin onboarding event.
The federation is the on-ramp, not the destination. “No custody” only holds in a world where users actually exercise the exit; if users accumulate sats and never graduate, OC is functionally custodial regardless of the threshold scheme. The exit door — federation custody to self-custody — is the product thesis, not a v2 feature.
Federation custody on the OC-introduced federation. Threshold guardians (3-of-4 for v1) hold key shares; OC the company holds none. Default for email-OTP signups.
Federation custody on a federation the user picked themselves. Same reputation graph, different guardian set. Recorded as a rebind envelope on the canonical event log.
Full self-custody. The user signs with their own Bitcoin key. Default for BIP-322 signin; reachable from any federation state via /me/graduate.
The user's opaque did:oc identity is stable across all three custody states · the underlying email or BTC may change (rotate, link more) but the public identifier stays the same. Stamps, attestations, agent delegations, votes, and bonded pledges all key on did:oc — the reputation graph travels with the identity on every rebind. Each transition is recorded as an anchored rebind envelope, viewable on /verify/<id> like any other envelope.
/me/graduate ↗four scenarios, walked through honestly.
- funds
- BIP-322 users: untouched. Email-OTP users: the bound federation continues operating independently; you keep access via the same Fedimint web client (or any compatible alternate client) using your saved mnemonic. OC the company has no key material in the federation.
- identity
- Auth host goes dark. JWK published at ochk.io stops serving. Existing sessions valid until expiry.
- historical envelopes
- All anchored envelopes verify offline forever.
- signing new envelopes
- BIP-322 users sign anywhere. Email users authenticate to the federation directly via their mnemonic — no OC involvement required.
- funds
- No fund movement is possible via OC compromise. OC has no signing capability over user wallets.
- identity
- Attacker can mint sessions for any account. Mitigation: rotate ochk.io/.well-known/jwks.json and revoke the compromised kid.
- historical envelopes
- Existing anchored envelopes are signed under the SEPARATE me.ochk.io envelope key, not the auth host key — they verify regardless. OTS-anchored history is intact under both keys' published JWKs.
- signing new envelopes
- Auth-key compromise is unrelated to envelope signing; new envelopes continue to sign under the me.ochk.io envelope key. Mitigation focus is on revoking active sessions and re-issuing the auth host JWK.
- funds
- No fund movement is possible. The envelope key only signs records; it has no authority over user wallets, federation balances, or oc_session tokens.
- identity
- Unaffected.
- historical envelopes
- Past envelopes signed under the leaked kid stay verifiable; the published JWKS continues to expose the old kid for offline verification of the historical record. Trust in those signatures is degraded (an attacker holding the key could forge a same-kid envelope), but the ROW must still appear in the events-store / rebind-store / payments index to be discoverable — and OC controls those writes.
- signing new envelopes
- Mitigation: rotate OC_ENVELOPE_PRIVATE_HEX, deploy a new kid, JWKS publishes both for the overlap window. New envelopes sign under the new kid.
- funds
- OC the company never runs a guardian — that's the architectural commitment behind the operator program at /operator. If a guardian operator is seized in any federation, the federation continues signing as long as the remaining guardians reach threshold (3-of-4 for OC-Me Federation v1).
- identity
- Unchanged.
- historical envelopes
- Unchanged.
- signing new envelopes
- Unchanged.
what happens if you forget something.
OC has no signing capability over user wallets in either path, which is the load-bearing property — but it also means OC cannot recover an account for you. Recovery is the user's wallet (BIP-322) or the federation operator's flow (email-OTP, via the saved mnemonic + the federation's mediated re-bind).
| forgets… | BIP-322 path | email-OTP path |
|---|---|---|
| their wallet (lost device, forgot seed) | standard Bitcoin wallet recovery — your seed phrase, your problem. OC cannot recover for you. Sat balance is on chain or in the wallet’s mempool, recoverable only via the seed. | the Fedimint web client is wired (browser-side, mnemonic stays in IndexedDB); recovery is via the mnemonic the user saved at provisioning. OC has no key material either way — this is the federation's mediated re-bind, not OC. Federations are OC-provisioned (operators hold the keys); oc-me-1 has completed DKG and is live — the remaining gated piece is the Lightning gateway that routes cash-out. |
| access to their email | no impact — BIP-322 users do not use email at all. | recovery routes through the federation operator's flow (prove control via the saved mnemonic + cooldown delay), not OC. The federation client is shipped and oc-me-1's invite is bound (DKG complete); the open work is the Lightning gateway that routes cash-out. |
| compromised email | no impact. | attacker can sign in as the user with a fresh OTP. Mitigation: as soon as the user notices, rotate the recovery email + revoke active sessions via /me/settings. Envelopes already anchored on Bitcoin stay verifiable; new envelopes signed under the compromised period can be flagged via the standard session-revocation flow at the auth host. |
currently a policy commitment, not architectural enforcement.
The events-store on me.ochk.io keys envelopes by both project_key AND user_address (see oc-me-web/src/lib/developer/events-store.ts — the events:by_user:<addr>: index returns every event for a given user across every site that integrated). OC commits not to join those tables; that is a policy commitment, not a cryptographic one.
A single Upstash SCAN over the by_user index returns every event for any user across all integrating sites. The OC billing engine requires this join to credit the right user wallet. Today, that graph is reachable in plain storage; the protection is policy + audit-log surveillance, not architecture.
Per-site identity derivation: the user’s wallet derives a blinded identifier per integrator from a shared secret + project_key. OC sees (project_key, blinded_user_id) and cannot link blinded_user_id values across projects without the user’s secret. Aggregate billing computes via blinded coupons. Active research; not yet shipped.
the rate caps are public so legitimate users can see they're below them.
Limits are intentionally generous for legitimate users and visibly tight enough that scripted sybil farming is unprofitable. They evolve with usage data; this page is the canonical source of truth.
| limit | value | why |
|---|---|---|
| class A · per identity · per day | 5 | bounds new-account-bonus farming. legitimate users join a few sites a week, not five a day. |
| class A · per identity · per month | 30 | monthly cap on durable state-transitions per OC identity. |
| single-site contribution to a user's monthly earnings | ≤ 60% | prevents a malicious site from inflating one user's stack to make a wash-trade flow look organic. |
| review threshold · class A · per month | ≥ $50 | flags an identity whose monthly class-A earnings exceed this threshold for human review. class B and C are self-bounded and never trigger. |
| drop window · max length | 92 days | the platform ceiling on integrator drop schedules — no payout window may hold an earner's vested share longer, and an open window's boundary can only move earlier. |
responsible disclosure
Security issues — wire-format bugs, signature-verification gaps, envelope confusion attacks, custody-related concerns — go to security@ochk.io. Triage in 48 hours. We coordinate disclosure with the affected protocol repos in github.com/orangecheck.
The internal NON_CUSTODIAL_AUDIT.md (in-tree at oc-me-web) walks the eight audit questions in §2 of the audit addendum with file paths. It is the source of truth this page renders for users. If you find something that page misses, the contact line above gets it triaged.