How it works
SpookyID is not a SaaS product or an identity provider. It is a protocol — a set of cryptographic operations that compose into an identity layer.
The four operations
- Anchor — A cryptographic identity is derived from a hardware root (TPM, Secure Enclave, or smartcard). No personal data is stored. The identity IS the hardware.
- Prove — Generate a zero-knowledge proof that discloses specific attributes without revealing their values. "I am over 21" without showing a birthdate.
- Attest — For sensitive actions, push a challenge to the user's device. The response is signed by the secure chip — proof that a physical device approved the action.
- Receipt — Every authorized action produces a cryptographic receipt. It is verifiable by anyone, non-replayable, and does not expose the identity of the authorizer.
What makes it different
OAuth gives a token. OIDC gives an ID token. SpookyID gives a proof. The difference: a token can be stolen and replayed. A proof is bound to a specific action, a specific moment, and a specific piece of hardware. It cannot be reused or transferred.
The seven chains
SpookyID is organized into seven independent cryptographic modules called chains. Each chain solves a specific identity problem. They compose but do not depend on each other.
Selective disclosure of attributes
Issue BBS+ signed credentials. Prove "I have role X" without revealing your name, email, or any other attribute. The verifier learns exactly what you disclose.
POST /api/chains/entitlements/issuePOST /api/chains/entitlements/provePOST /api/chains/entitlements/verify
Unlinkable pairwise pseudonyms
Each relying party receives a different identifier. Site A and Site B cannot correlate that you are the same person. Revocation is per-site without affecting other sites.
POST /api/chains/contextual/generatePOST /api/chains/contextual/revoke
Authority transfer with constraints
Delegate specific capabilities to another identity with time limits and use limits. "My assistant can sign for me for the next hour, maximum 3 times." Includes leasing as a time-bounded subtype.
POST /api/chains/delegation/createPOST /api/chains/delegation/revokePOST /api/chains/delegation/verify
Identity survival when hardware is lost
Two paths: social recovery via Shamir secret sharing (k-of-n guardians), and catastrophic recovery via three-factor ceremony (device attestation, biometric proof, and channel verification).
POST /api/chains/recovery/setupPOST /api/chains/recovery/initiatePOST /api/lazarus/initiate
Hash-linked immutable log
Every significant action is logged with a hash that includes the previous event's hash. If any entry is modified, added, or removed, the chain breaks. Lifecycle state changes are audit events.
GET /api/chains/audit/queryGET /api/chains/audit/verify
Hardware-rooted trust
Verify that keys live in real hardware — TPM, Android Secure Enclave, or JavaCard. Software key extraction is not possible. Remote attestation via TPM quote and PCR validation.
POST /api/chains/attestation/verifyPOST /api/chains/attestation/remote
External identity provider trust
Register external OIDC providers (Google, Microsoft, enterprise IdPs) with explicit trust levels. Verify their tokens against their actual public keys, not a cached copy.
POST /api/chains/federation/registerPOST /api/chains/federation/verify
Policy engine
Composes the chains to answer: "Is this identity allowed to perform this action, right now?" Evaluation order: check audit chain for active status, check deny policies, check entitlements, check delegation, check attestation requirements, log the decision.
POST /api/policy/evaluate
Client SDK
The client SDK (@spooky-id/react) provides React hooks for integrating SpookyID into any application. Works with any OIDC-compatible provider; SpookyID-specific features (receipts, selective disclosure) activate when connected to a SpookyID broker.
import { SpookyProvider, useAuth } from '@spooky-id/react' // Wrap your app function App() { return ( <SpookyProvider slug="my-app" required={["age"]}> <MyPage /> </SpookyProvider> ) } // Use in any component function MyPage() { const { login, user, receipt } = useAuth() if (!user) return <button onClick={login}>Login with SpookyID</button> // user.attributes.age → boolean via ZK proof // actual age is never revealed }
Policy check (client-side)
import { checkPolicy } from '@spooky-id/auth-config' const result = checkPolicy(user.attributes, { requiredAttributes: ["age"], optionalAttributes: ["email", "region"], tolerance: "strict" }) // result.allowed → true/false // result.tier → "full" | "guest" | "denied"
Comparison with existing standards
SpookyID occupies a position that no existing standard or provider covers. The table below maps what each layer provides and where the gaps are.
| Capability | OAuth 2.0 | OpenID Connect | FIDO2 / Passkeys | Okta / Auth0 | W3C Verifiable Credentials | SpookyID |
|---|---|---|---|---|---|---|
| What it is | Authorization protocol | Identity layer on OAuth | Authentication standard | Identity provider (SaaS) | Credential data model | Identity protocol |
| Eliminates passwords | No | No | Yes | No | No | Yes |
| Hardware-bound keys | No | No | Yes | No | No | Yes |
| Stores user PII | N/A | Yes — in ID tokens | No | Yes — full database | Depends on issuer | No — derived from hardware |
| Selective disclosure | No | No | No | No | Possible | Built in (BBS+) |
| Prove attribute without revealing value | No | No | No | No | With ZKP extension | Native |
| Unlinkable cross-site identities | No | No — same sub everywhere | No | No — same email everywhere | With did:key | Native (pairwise pseudonyms) |
| Authority receipts | No | No | No | No | No | Every authorized action |
| Live push attestation | No | No | No | MFA push (not cryptographic) | No | Hardware-signed approval |
| Breach impact | Token leaked | PII + token leaked | Keys stay in hardware | Full PII database leaked | Depends on storage | Nothing to leak |
| Audit trail | No | No | No | Server logs | No | Hash-linked, ZK |
| Delegation with constraints | Scopes | No | No | Limited | No | Time + use limits, revocable |
| Identity recovery | N/A | N/A | Cloud sync | Admin reset | Social recovery | Shamir + 3-factor ceremony |
| Works offline | No | No | Yes | No | Yes | Leasing supports offline delegation |
| Open standard | IETF RFC | OpenID Foundation | W3C / FIDO | Proprietary | W3C | Open protocol |
What this means in practice
OAuth 2.0 gives you a token. That token grants access. If someone steals it, they have access. OAuth solved delegated authorization — letting an app act on your behalf. It did not solve proving that the person who holds the token should hold it.
OpenID Connect puts an identity on top of OAuth. You get an ID token with your name, email, and other claims. Every relying party sees the same identifier. Your identity is a JWT that any token thief can replay.
FIDO2 / Passkeys bind a key to hardware. This is real progress — the private key cannot be extracted. But FIDO2 only proves you hold a key. It does not prove anything about you (age, role, authorization) and it does not produce receipts. There is no selective disclosure.
Okta / Auth0 are identity providers. They store your users' emails, passwords, MFA secrets, group memberships, and session data in a database. That database is a target. Every breach of an identity provider exposes millions of records.
W3C Verifiable Credentials define a data model. A credential is a signed JSON-LD document. Selective disclosure is possible with ZKP extensions but not built in. Hardware binding is not part of the spec. There is no authority receipt mechanism.
SpookyID is the only protocol that combines all of these properties: hardware-bound keys (like FIDO2), selective disclosure of attributes (like ZK-extended VCs), unlinkable cross-site identities (no other standard), cryptographic authority receipts (no other standard), and a hash-linked audit trail (no other standard). The identity is derived from silicon, not stored in a database.
The gap SpookyID fills
| Layer | What exists | What is missing | SpookyID |
|---|---|---|---|
| Authentication | OAuth, OIDC, FIDO2 | Prove attributes not just identity | BBS+ selective disclosure |
| Authorization | OAuth scopes, RBAC | Prove authorization cryptographically | Hardware-signed policy evaluation |
| Attestation | FIDO2, TPM | Bind attestation to specific actions | Push attestation per action |
| Audit | Server logs, SIEM | Logs can be tampered with | Hash-linked immutable chain |
| Privacy | GDRP policy, encryption at rest | PII still collected and stored | PII is never collected |
| Receipts | Nothing | No standard for proof of authorization | Cryptographic authority receipts |
Specification status
| Component | Status | Notes |
|---|---|---|
| Entitlements (BBS+) | Implemented | Issue, prove, verify |
| Contextual pseudonyms | Implemented | HMAC-based, per-site |
| Delegation & leasing | Implemented | Time/use limits, revocation |
| Recovery (Shamir) | Implemented | k-of-n guardian scheme |
| Recovery (Lazarus Gate) | Implemented | Three-factor ceremony |
| Audit chain | Implemented | Hash-linked, queryable |
| Attestation (Android) | Implemented | Key Attestation x.509 chain |
| Attestation (iOS) | In progress | App Attest stub exists |
| Attestation (TPM) | Partial | Quote verification, PCR pending |
| Federation | Implemented | External OIDC provider trust |
| Policy engine | Implemented | Deny-first, audit-always |
| OIDC compliance | Implemented | OpenID4VCI, OpenID4VP |
| mDL (ISO 18013-5) | Planned | Conformance testing pending |
Cryptography
- Signatures: BBS+ on BLS12-381 (selective disclosure, pre-quantum)
- Key derivation: HKDF-SHA256 from hardware root
- Pseudonyms: HMAC-SHA256 (pairwise, unlinkable)
- Audit: SHA-256 hash chain
- Token signing: RS256 (JWT)
Note on quantum safety: BBS+ signatures on BLS12-381 are not post-quantum secure. No practical post-quantum ZK selective disclosure scheme exists yet. When one does, the signing scheme in Chain 1 can be swapped without architectural changes.
Implementation
- Backend: Rust, Axum, SQLite (SQLx)
- Mobile: Android (Kotlin), Flutter (Dart + Rust FFI)
- Client SDK: TypeScript/React
- Protocol: OIDC (OpenID4VCI, OpenID4VP)