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.
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.
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.
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.
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.jsonand 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.
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.
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.
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.