Skip to content

Security

What we store. What can move funds. What happens if we disappear.

Plain answers, for you and for your security reviewer. Who holds the sats is computed live on Custody.

On this page

Every field, every store.

What an auditor would find in production today: Supabase rows on ochk.io, Upstash Redis behind me.ochk.io and ochk.app, OpenTimestamps calendar entries and Nostr relays (both public). None of it is key material that can sign from your own Bitcoin wallet. The envelope store still matters: an accrued balance is recomputed from those signed envelopes on every read. So who holds that balance is a separate question, computed on /custody.
  • Account row

    Where
    ochk.io · Supabase, the accounts table
    What
    Your did:oc, an opaque random id with the same shape for every sign-in path. A display name, a Nostr npub, when you joined and when you last signed in. The email or Bitcoin address behind it sits in a separate table: an email encrypted at rest, a Bitcoin address in plain text, since it is public on chain. No password hash. No private key. No recovery phrase.
  • Session id

    Where
    ochk.io · Supabase, the session revocation list
    What
    The token id, the account id, when it was issued and when it was revoked. The token itself lives only in your browser, as a cookie signed with the Ed25519 sign-in key.
  • Envelopes, one per billable event

    Where
    me.ochk.io and ochk.app · Upstash Redis, events store
    What
    A signed record: a routing id, the project key, your address, the fee split and when it happened. Its content address is the sha256 of its canonical bytes. Each is signed when written with the envelope key (Ed25519, public at me.ochk.io/.well-known/oc-envelope-jwks.json), which is separate from the sign-in key. Indexed by project and by user: see the cross-site graph.
  • Rebind envelopes, one per change of custody state

    Where
    me.ochk.io and ochk.app · Upstash Redis, rebind store
    What
    Each move between federation custody and your own Bitcoin key. Same scheme and same key as event envelopes. Shown on /verify and on your own identity timeline.
  • Projects, webhooks, sends, payments, attest tiers, notifications

    Where
    me.ochk.io and ochk.app · Upstash Redis, one store each
    What
    Project keys, price configs, JWK URL patterns and domain verification state. Webhook URLs and their latency figures. Outflow ledger entries. Metadata only: none of it can sign.
  • OpenTimestamps proofs

    Where
    Public · OpenTimestamps calendars
    What
    Public by design. Anyone can check them against Bitcoin headers with orangecheck offline.
  • Nostr commitments

    Where
    Public · 4 relays (relay.damus.io, nos.lol, relay.snort.social, relay.ochk.io)
    What
    Kind 30087. A salted hash of each envelope, never the envelope: no identity, site, amount or envelope id. Drop manifests, which name no one, are published in full.
  • Email sign-in code, for ten minutes only

    Where
    ochk.io · a stateless token your browser carries
    What
    Not stored on a server. Your email, the sha256 of the code and an expiry, signed with the sign-in key. Your browser holds it between asking for the code and entering it.

Nothing can move your own wallet. Your accrued sats are a different question.

OrangeCheck holds your accrued sats during bring-up — it is operating every role until independent operators take them. Federation custody and graduation to your own keys are what replace it. That line is computed from the federation registry and the rail, not written here, so the live answer is always the one on /custody.

If you sign in with a Bitcoin wallet

You hold your own wallet. OrangeCheck sees only signatures and public addresses. No recovery phrase, private key or recovery factor that can move your funds reaches orangecheck at any point. If orangecheck vanished tomorrow, your wallet is unaffected.

If you sign in with email, today

Where email sign-in is headed: a wallet held by a federation behind your identity, with you holding its recovery secret. The browser client that would have set one up was removed with the guardians, so that is a design to rebuild, not a feature waiting to be switched on. The target is four guardian seats with distinct hardware-backed operator keys, held by independent operators. Once the seats are key-diverse, orangecheck cannot move funds. During bootstrap orangecheck operates the seats, so who holds the keys, and whether any federation is live, is computed on /custody.

You can bind a Bitcoin address now, but binding is not settlement. The payout rail is in bring-up, so no sats move yet. On-chain payouts are deferred: the deployed payer settles to Lightning addresses only.

In short: no process orangecheck runs holds key material that could move your own wallet. BIP-322 keys stay in your wallet, and the database is metadata. What orangecheck does hold during bring-up is the executor's settlement signing key, for the process that pays out. Federation key shares live with the guardian seats, which orangecheck operates during bootstrap. The sats earned here: OrangeCheck holds what accrues until a federation takes over.

Every sign-in is a Bitcoin onboarding event.

The federation is the on-ramp, not the destination. Holding people's sats only stays temporary if people take the way out. If they accumulate sats and never graduate, orangecheck is a custodian in practice, whatever the threshold scheme. The way out, from federation custody to your own keys, is the thesis, not a later feature.

Federation custody

Federation custody on the federation orangecheck introduced. The target: 3-of-4 threshold guardians hold the key shares, and orangecheck the company holds none. That is where email sign-ups are headed, not where they are. Until a federation is bound and off bootstrap, orangecheck holds those sats. Whether one is bound, and who holds its keys, is computed on /custody.

A federation you pick

Federation custody on a federation you pick, with the same reputation graph and a different guardian set, recorded as a rebind envelope. Reserved, and nothing can produce it today: the browser client this mode names was removed. No account carries it, and a bound federation would not make it reachable.

Your own Bitcoin key

You sign with your own Bitcoin key. That half is live, and orangecheck never sees its key material. Moving accrued sats into those keys is the other half. Graduation to your own keys is not live: no federation is bound yet.

Your did:oc stays the same across all three states. The email or Bitcoin address behind it can change, but the public id does not. Stamps, attestations, agent delegations, votes and bonded pledges all key on did:oc, so your reputation travels with you on every rebind. Each move is recorded as an anchored rebind envelope you can check on /verify, like any other envelope.

Five scenarios, walked through.

What happens to your funds, your identity and your envelopes if orangecheck is bought, shuts down, or loses a key.
  • A hostile owner buys orangecheck

    Funds
    Wallets of people who sign in with BIP-322: untouched. OrangeCheck never had key material for them. Sats earned here are different. OrangeCheck holds your accrued sats during bring-up — it is operating every role until independent operators take them. Federation custody and graduation to your own keys are what replace it. A hostile buyer inherits whatever orangecheck holds. Graduation to your own keys is not live: no federation is bound yet. Once guardians are independent operators, vetted through the operator program, a new owner cannot make threshold-signed withdrawals without corrupting enough of them to reach the threshold, which the charter forbids. That protection arrives with key diversity. /custody shows the current tier.
    Identity
    The new owner controls the sign-in key, so could mint sessions. They cannot forge a BIP-322 signature from your wallet.
    Past envelopes
    Every anchored envelope verifies offline, forever, without orangecheck.
    New envelopes
    BIP-322 users sign as before; a new owner cannot forge those signatures. Email users hold no key here, so whoever runs orangecheck signs for them, and a new owner inherits that. Handing the role to a federation is what ends it.
  • OrangeCheck goes bankrupt and shuts down

    Funds
    BIP-322 wallets: untouched. Sats earned here are the other axis. There is no stored balance: it is recomputed from signed envelopes, and it is a claim on whoever holds it. OrangeCheck holds your accrued sats during bring-up — it is operating every role until independent operators take them. Federation custody and graduation to your own keys are what replace it.
    Identity
    The auth host goes dark and the key it publishes at ochk.io stops being served. Existing sessions stay valid until they expire.
    Past envelopes
    Every anchored envelope verifies offline, forever.
    New envelopes
    BIP-322 users sign anywhere: a Bitcoin wallet needs nothing from orangecheck. Email sign-in runs through the auth host, so it stops when orangecheck does. Moving it to a federation is the fix.
  • The sign-in key leaks

    Funds
    An attacker minting sessions cannot sign from your Bitcoin wallet; no orangecheck key reaches it. They can act as you inside the app, including asking to cash out your accrued balance. The fix: rotate the sign-in key and revoke active sessions.
    Identity
    The attacker can mint a session for any account. The fix: rotate ochk.io/.well-known/jwks.json and revoke the leaked key id.
    Past envelopes
    Envelopes are signed with a separate envelope key, not the sign-in key, so they still verify. Anchored history stays intact under both keys' published JWKs.
    New envelopes
    Unaffected: new envelopes keep signing under the envelope key. The work is revoking sessions and reissuing the sign-in key.
  • The envelope key leaks

    Funds
    No funds can move. The envelope key only signs records. It has no authority over wallets, federation balances or session tokens.
    Identity
    Unaffected.
    Past envelopes
    Envelopes under the leaked key id stay verifiable, and the published key set keeps the old id for checking history offline. Trust in those signatures is weaker: whoever holds the key could forge one with the same id. A forged envelope still has to appear in the events, rebind or payments index to be found, and orangecheck controls those writes.
    New envelopes
    The fix: rotate the envelope key and deploy a new key id. The key set publishes both during the overlap; new envelopes sign under the new id.
  • A regulator seizes a guardian's key share

    Funds
    OrangeCheck's commitment is not to run a guardian in the target: independent operators come through the operator program. During bootstrap orangecheck operates the seats to bring a federation up, so this holds fully once the federation is key-diverse, and /custody shows the tier. If an independent operator is seized later, the federation keeps signing as long as the rest reach the threshold, for example 3 of 4.
    Identity
    Unchanged.
    Past envelopes
    Unchanged.
    New envelopes
    Unchanged.

What happens if you lose something.

OrangeCheck holds no key that can sign from a Bitcoin wallet, and no recovery phrase for anyone. That is also why it cannot recover an account for you. With a wallet, recovery is your wallet. With email, it is the email itself today; a federation operator's mediated re-bind is what replaces it.
  • Your wallet: a lost device, a forgotten seed

    Signed in with a Bitcoin wallet
    Standard Bitcoin wallet recovery, from your seed phrase. OrangeCheck cannot do it for you. Your sats are on chain or in the wallet, recoverable only with that seed.
    Signed in with email
    You signed in with email, so you hold no key here. OrangeCheck signs on your behalf. There is no browser-held recovery phrase to lose on this path, and none to recover with. A federation's mediated re-bind is what recovery becomes. Whether a federation is bound, and who holds an accrued balance meanwhile, is computed on /custody.
  • Access to your email

    Signed in with a Bitcoin wallet
    No effect. Wallet sign-in does not use email.
    Signed in with email
    Email is the only credential on this path today, so losing it loses the sign-in. OrangeCheck has no second way to check it is you. Linking a Bitcoin address to the same did:oc removes that dependency. A federation operator's mediated flow is what replaces it; whether a federation is bound is read from the registry on /custody.
  • Control of your email

    Signed in with a Bitcoin wallet
    No effect.
    Signed in with email
    An attacker can sign in as you with a fresh code. Once you notice, change your recovery email and revoke active sessions in /me/settings. Envelopes already anchored stay verifiable. Envelopes signed during the compromise can be flagged through session revocation on the auth host.

The cross-site graph is a policy commitment today, not architecture.

The events store keeps an index by person, and one lookup in it returns every event for that person across every app that uses orangecheck. orangecheck commits not to join those records. That is a policy, not cryptography.

Today

One lookup in the by-person index returns every event for anyone across all apps. Billing needs that join to credit the right person. So the graph is reachable in plain storage. The protection is policy and audit-log monitoring, not architecture.

Planned

Per-site identity: your wallet derives a blinded id for each app from a shared secret and the project key. orangecheck sees only the project and a blinded id, and cannot link those across projects without your secret. Billing totals use blinded coupons. Active research, not shipped.

The rate caps are public, so real people can see they are below them.

Generous for real people, tight enough that scripted sybil farming does not pay. They change with usage data. This page is the source of truth.
  • Class A events per identity, per day

    5
    Why
    Bounds new-account bonus farming. Real people join a few sites a week, not 5 a day.
  • Class A events per identity, per month

    30
    Why
    A monthly cap on durable state changes per identity.
  • One site's share of a person's monthly earnings

    ≤ 60%
    Why
    Stops a site inflating one person's earnings to make a wash flow look organic.
  • Human review, class A earnings per month

    ≥ $50
    Why
    An identity whose class A earnings pass this in a month goes to a person for review. Classes B and C are self-bounded and never trigger it.
  • Longest drop window

    92 days
    Why
    The ceiling on a site's drop schedule. No window can hold an earner's vested share longer, and an open window's boundary can only move earlier.

Reporting a problem

Found something? Tell us first.

Wire-format bugs, gaps in signature checks, envelope confusion, custody concerns. Triage within 48 hours, and disclosure coordinated with the affected protocol repositories on GitHub.