Skip to content

Protocol · identity

Prove you hold bitcoin, without showing an ID.

Sats as signal. OC Attest is the identity protocol of the orangecheck family. You sign one message with your Bitcoin wallet. Anyone can then check, from public chain data, that your address has kept N sats unspent for N days.

What your wallet signs · test vector tv01
orangecheck
identities:
address: bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq
purpose: portable reputation attestation (non-custodial)
nonce: 0011223344556677889900aabbccddee
issued_at: 2026-04-22T12:00:00Z
ack: I attest control of this address and bind it to my identities.
  • Open spec, MIT
  • No ID asked
  • Signing moves nothing
  • Checkable without us

The problem

Open apps can’t tell a person from a bot farm.

The usual fixes ask honest people for more: a phone number, an ID scan, a puzzle that breaks every month. A farm gets around them anyway.

Bitcoin left unspent is different. Holding 100,000 sats for 30 days costs a real person almost nothing. Faking it ten thousand times means funding ten thousand wallets and waiting. OC Attest turns that cost into a proof any app can read.

How it works

Sign. Share. Check.

  1. 01

    Sign

    Your wallet signs a short, plain-text message that names your address and, if you like, handles you claim (GitHub, Nostr, a domain, X). You read every line before you sign. No transaction is made.
  2. 02

    Share

    The signed message is a self-contained proof with a fixed id: the SHA-256 of the message. Keep it, attach it to a link, or publish it to Nostr relays so others can find it.
  3. 03

    Check

    Anyone verifies the signature and reads your address’s unspent coins from the chain. They get two numbers, sats and days, and decide for themselves what is enough.
The Bitcoin property it depends on
A BIP-322 signature from a real address, and the value and age of that address’s unspent outputs. A plain keypair has no balance and no age, so the proof would mean nothing without Bitcoin.

Where it stands

Open, stable, and free to use.

The spec is version 1.0, marked stable since 2026-04-25, with 28 conformance vectors. A private form for backing one account with one coin is a 1.1 draft. The tools at attest.ochk.io are free and work today against Bitcoin mainnet. OC Attest asks for no ID and never holds your coins: signing moves nothing. orangecheck as a whole has not launched publicly yet.

The spec

oc-attest-protocol

Version
1.0
Licence
MIT
Nostr kind
30078
Test vectors
28

Try it

What you can do today.

  • Build, sign and publish in one page. No account needed.

    attest.ochk.io/create
  • Paste a share link, or the message and signature by hand.

    attest.ochk.io/verify
  • By id, address or handle. Your search goes to the relays directly.

    attest.ochk.io/attest/discover
  • In the browser. No key, no sign-up.

    attest.ochk.io/playground
  • Paste addresses, set a bar, see who passes and why.

    attest.ochk.io/airdrop

For developers

Build on it.

Gate anything with one call. @orangecheck/sdk (1.13.0 on npm) finds the latest proof for an address on Nostr, checks the signature, and recomputes sats and days from live chain data.

npm install @orangecheck/sdk

SDK

@orangecheck/sdk

check, verify and discover; 1.13.0

Gate

@orangecheck/gate

middleware for Express, Next.js, Fastify and Hono that returns 403 below your bar; 0.3.0

CLI

@orangecheck/cli

oc check, oc verify and oc discover from a shell; 0.3.1

React and Python

@orangecheck/react · orangecheck

UI components, 0.2.1 on npm; the Python package, 2.2.0 on PyPI

import { check } from '@orangecheck/sdk';

const r = await check({ addr: 'bc1q...', minSats: 100_000, minDays: 30 });
if (r.ok) {
    // r.sats and r.days were read from the chain just now
} else {
    console.log(r.reasons); // e.g. ['not_found'] or ['below_min_days']
}

Adding sign-in? There are two paths, and they are different things.

The hosted account
To sign people in with an orangecheck account, use ochk.app’s sign-in: @orangecheck/me-client and a project key. It is free. Start at the quickstart. To let in only accounts backed by a coin, see require backing.
Do it yourself, no account
@orangecheck/sdk has issueChallenge and verifyChallenge, so your own server can prove a visitor controls an address and start its own session. See sign in with Bitcoin.

Check one

Verify a proof someone sent you.

Paste the share link you were sent, or its id. Share links look like https://attest.ochk.io/attest/<id>. Every published proof also has a checked page on this site, at ochk.io/a/<id>.

The verifier runs in your browser. It checks that:

  • the message has the exact fixed lines, in order, and names the same address;
  • the id is the SHA-256 of the message, so nothing was changed after signing;
  • the address’s network matches (mainnet unless the message says testnet or signet);
  • the BIP-322 signature is valid for that address (legacy signatures only for 1… addresses);
  • the address has confirmed, unspent coins now, and how many sats for how many days. It counts the oldest coins first, so an old speck of dust can’t lend its age to a fresh deposit;
  • any expiry or origin the signer set still holds.

An id or link opens its checked page here. Anything else opens attest.ochk.io/verify.

A proof passes only when the signature is valid and confirmed coins back it. A valid signature over an empty address is reported as valid but does not pass.

You don’t need us for any of this. With the message, the signature and any Bitcoin node or block explorer, you can run the same checks yourself. @orangecheck/sdk and oc verify run them on your own machine.

What you trust

Every party you rely on, by name.

Chain data
The SDK and the verify page read coins from mempool.space, then blockstream.info if that fails. You can point the SDK at your own node’s Esplora instead.
Relays, for finding proofs
By default the SDK searches relay.damus.io, nos.lol, relay.snort.social and relay.ochk.io. A relay can hide a proof. It can’t forge one, because verifiers check the signed message, not the relay’s labels.
Publishing
attest.ochk.io re-checks your proof, then wraps it in a Nostr event signed with an orangecheck service key and sends it to nine relays. That key is only the envelope. The proof is your Bitcoin signature inside.
The hosted API
attest.ochk.io/api/check runs these checks on our servers. It is a convenience, not the authority. A compromised server could lie, so for anything that matters, check it yourself. The Python SDK’s check() calls this API.
Handles
A claimed GitHub, Nostr, domain or X handle is self-asserted. Check it the way you check any handle, for example a gist or a DNS record that contains the proof’s id.
Backing on ochk.app
There, ochk.io acts as the registry: it sees which coin backs which account and keeps checking it. The apps you sign in to see only “at least N sats, held at least D days”, never the coin.

What it is not

The limits, plainly.

Not proof of personhood
One person can hold many addresses. OC Attest raises the cost of fakes. It does not make them impossible.
Not private
A published proof shows your address, its balance and its history. Use a fresh address for each context if that matters. Backing an account on ochk.app is the private form.
Not permanent
A proof shows what was true when it is checked. Spend the coins and the next check fails.
Not a reputation score
Sats and days are the facts. The reference score is advisory, and each app sets its own bar.

Where it is used

Apps that use it.

ochk.app (opens in a new tab)
Backing your account uses OC Attest’s one-coin, one-account format. The coin stays in your wallet.
OC Pledge
A pledge’s stake is an OC Attest proof, re-checked against the chain whenever the pledge is verified.
OC Stamp
A stamp can carry a proof’s id as a stake signal at the moment of signing.

Make your first proof.

The tools are free and need no account. The spec is open to anyone who wants to build their own.

With thanks to Bram Kanstein, whose work on Bitcoin as a sovereignty layer shaped the premise.