Reference operator
Architecture
Sparse map of what ships today: where keys live, how revoke works, what the resolver stores, and the tradeoffs we locked for v1.
The product is not “a QR that links somewhere.” It is signed public state that any camera can read - with honest limits on what a scan proves.
Live loop (Phase A)
Create
Sign card + QR credential
Resolver
Verify signature, store status
Scan
HTTPS → current state UI
Static site on Cloudflare Pages (site/). API + scan resolver
on Cloudflare Workers with D1 for cards, QR credentials,
and revocation records. Private keys never leave the owner’s device.
Revoke (how it actually works)
Authorize
Ed25519 in browser tab
Resolver
Check sig → update D1
Next scan
Same URL, new answer
-
Not key rotation on the sticker
The printed URL keeps
profile_idandqr_id. Revoke changes server-side status, not the ink. - Signed authorization Resolver accepts revoke only from the owner key (or recovery key). No password-reset override on the operator.
- Per-QR scope One wristband or sticker can be revoked while sibling QRs on the same card stay active.
What lives where
- Browser (owner device) Private Ed25519 key, recovery key display, encrypted backup export. Lost tab = lost signing ability unless backup saved.
- Resolver (public) Signed card JSON, QR credential rows, status flags, vouches, revocation records. Returns scan HTML + JSON - not a people trail.
- Physical tag object HTTPS URL only. Camera decodes it; resolver decides current meaning.
Browser custody (on this device)
The resolver stores public state. Signing ability lives in the steward’s browser — not on humanity.llc servers. See Safari keys for iOS-specific limits.
-
Tab session vs saved wallet
sessionStorage(hc_created) holds signing keys for this tab.localStorage(hc_wallet) survives tab close after Save ownership on this device — but other tabs must restore keys before signing. -
Device shell
Hub, wallet, status dot, and inbox on
/,/wallet/, and/created/. No accounts — everything is local to this browser. - PWA vs Safari browser Add to Home Screen shares saved wallet but not tab session. iPhone Camera QR opens Safari, not the Home Screen app — plan for restore or same-tab scan.
- Hosted steward tier Optional push for live proof when the tab is hidden — partial, production-gated. Keys still never leave the device.
Decisions & tradeoffs
-
Open
hc/v1spec, reference operator first humanity.llc proves the loop; other hosts can implement the same API later (federation is direction, not day-one). - HTTPS QR with ids in the URL Any camera works today. Tradeoff: printed ids are permanent; privacy modes live in scan response, not URL shape (v1 lock).
- Minimal object state, no scan analytics or alerts Store status + expiry, not who scanned. Future interaction signals must stay coarse and log-free: no timestamps, locations, IPs, or per-scan history.
- Mechanism-revealing scan UI Separate card status, human trust, QR status, and live control. Limits live in one settings row - not buried in marketing copy.
- Not blockchain, not legal ID Signed social state for communities that refuse surveillance - not KYC, not a global uniqueness oracle.
Security & assurance
Words like signed, revoke, and resolver need backing - threat models, failure modes, tests, and clean boundaries. The repo documents these for reviewers and implementers.
- Threat model & adversarial review Abuse paths (collusion, replay, stolen stickers), privacy leaks, and required hardening - documented before broad launch.
- Crypto & replay boundaries Ed25519 + JCS canonicalization; typed payloads with nonces on revoke, vouch, and live-control responses; resolver rejects stale or mismatched signatures.
-
Automated tests (Worker)
Vitest covers create, scan, revoke, live control, crypto, CORS, and key backup -
run
npm run worker:testbefore deploy. - Key recovery boundaries Owner key stays in browser; recovery key or encrypted backup unlocks revoke/live proof later. Operator cannot reset or impersonate - lost keys mean lost signing ability.
- Failure modes we design for Revoked QR still resolves (not 404 silence); live proof expires in 120s; duplicate webhook/idempotency planned for commerce; federation to avoid single-operator honeypot.
Not in v1
Commerce webhooks, Commons Pass, NFC/mesh carriers, native apps, public directory, scan logging. See research directions for carrier experiments.
Full specs
- V1_ADVERSARIAL_REVIEW.md Threat paths, severity table, required hardening
- V1_PRODUCT_TRUST_MODEL.md What scans prove and explicitly do not prove
- V1_0_ARCHITECTURE_ROADMAP.md Build order, phases, repo layout
- Technical Standards v1.0 Wire formats, crypto, resolver API
- Operator data policy What the resolver stores and must not store
- Back to landing