oc · docs
who can sign in

Who can sign in

Every orangecheck account signs in to your app by default, however it was made: email, Google, GitHub or a Bitcoin wallet. To keep bots out, you can let in only accounts backed by bitcoin. The person has backed their account with one coin they hold (bonds). The coin stays in their wallet, and you never see it: your token carries only floors, when the coin was last checked, and a nullifier for your app.

1. Set the floor

Open your project on ochk.app and choose Use orangecheck sign-in. That is the project's Sign-in tab: How people sign in, then Who can sign in below it. Pick a minimum amount and age, for example 100,000 sats unspent 30 days. The lowest floor is 1,000 sats: ochk.io won't take a smaller coin. The floor is saved in the project and served, uncached, at GET https://me.ochk.io/api/integrator/<project_key>/sign-in.

2. Enforce it on your server

import { withOcAuth } from '@orangecheck/me-client/server';

export default withOcAuth(
    async (req, res) => {
        if (!req.ocSession) return res.status(401).json({ ok: false });
        res.status(200).json({ ok: true, did_oc: req.ocSession.did_oc });
    },
    {
        audience: 'https://yourapp.example',
        projectKey: process.env.OC_PROJECT_KEY!,
    }
);

A change in your settings is enforced within a minute. An account below the floor gets:

403 {
  "ok": false,
  "reason": "bond_required",
  "need": { "min_sats": 100000, "min_days": 30 },
  "have": { "sats_floor": 10000, "days_floor": 90 },
  "back_url": "https://ochk.app/me/account#backing"
}

Link that person to back_url, where they can back their account. have is null when no coin backs it. If your requirement has never been readable, the request gets 503 bond_policy_unavailable rather than being let through unchecked. A projectKey that names no project gets 500 unknown_project_key (me-client 0.32 and later).

ochk.io re-checks the coin when its last check is over ten minutes old, and every claim says when that was (oc_bond.checked_at). You can't ask for a fresh check, but you can refuse a stale one with maxAgeSecs, which only code sets.

In code, requireBond: { minSats, minDays, maxAgeSecs } does the same as the project setting and wins over projectKey. readBondClaim(session) and bondMeets(session, { minSats, minDays, maxAgeSecs }) come from @orangecheck/me-client/server (or @orangecheck/auth-core) for your own checks: a fast lane, a higher free tier, a ban list.

3. Keep one account per nullifier

oc_bond.nullifier is the same for the same coin at your app every time, and only ochk.io can compute it, so it doesn't reveal the coin. Keep one account per nullifier: that is the real bot check at your site. A floor says some coin backs the account; the nullifier tells you whether that coin already backed another account of yours. Ban a nullifier to ban the coin.

A coin backs one account at a time. Released from one account, it can back a different one only 30 days later. Sites on *.ochk.io share one family session, so they share one nullifier.

Requires @orangecheck/me-client ≥ 0.27.3. back_url and the snake_case need arrive in 0.28.0.