SpookyID

A protocol for cryptographic identity anchored in hardware. Prove attributes about yourself — age, role, residency — without revealing the underlying data. Every proof is bound to a physical device and produces a verifiable receipt.

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.

01 — Entitlements

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/issue
POST /api/chains/entitlements/prove
POST /api/chains/entitlements/verify
02 — Contextual

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/generate
POST /api/chains/contextual/revoke
03 — Delegation

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/create
POST /api/chains/delegation/revoke
POST /api/chains/delegation/verify
04 — Recovery

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/setup
POST /api/chains/recovery/initiate
POST /api/lazarus/initiate
05 — Audit

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/query
GET /api/chains/audit/verify
06 — Attestation

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/verify
POST /api/chains/attestation/remote
07 — Federation

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/register
POST /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 isAuthorization protocolIdentity layer on OAuthAuthentication standardIdentity provider (SaaS)Credential data modelIdentity protocol
Eliminates passwordsNoNoYesNoNoYes
Hardware-bound keysNoNoYesNoNoYes
Stores user PIIN/AYes — in ID tokensNoYes — full databaseDepends on issuerNo — derived from hardware
Selective disclosureNoNoNoNoPossibleBuilt in (BBS+)
Prove attribute without revealing valueNoNoNoNoWith ZKP extensionNative
Unlinkable cross-site identitiesNoNo — same sub everywhereNoNo — same email everywhereWith did:keyNative (pairwise pseudonyms)
Authority receiptsNoNoNoNoNoEvery authorized action
Live push attestationNoNoNoMFA push (not cryptographic)NoHardware-signed approval
Breach impactToken leakedPII + token leakedKeys stay in hardwareFull PII database leakedDepends on storageNothing to leak
Audit trailNoNoNoServer logsNoHash-linked, ZK
Delegation with constraintsScopesNoNoLimitedNoTime + use limits, revocable
Identity recoveryN/AN/ACloud syncAdmin resetSocial recoveryShamir + 3-factor ceremony
Works offlineNoNoYesNoYesLeasing supports offline delegation
Open standardIETF RFCOpenID FoundationW3C / FIDOProprietaryW3COpen 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

LayerWhat existsWhat is missingSpookyID
AuthenticationOAuth, OIDC, FIDO2Prove attributes not just identityBBS+ selective disclosure
AuthorizationOAuth scopes, RBACProve authorization cryptographicallyHardware-signed policy evaluation
AttestationFIDO2, TPMBind attestation to specific actionsPush attestation per action
AuditServer logs, SIEMLogs can be tampered withHash-linked immutable chain
PrivacyGDRP policy, encryption at restPII still collected and storedPII is never collected
ReceiptsNothingNo standard for proof of authorizationCryptographic authority receipts

Specification status

ComponentStatusNotes
Entitlements (BBS+)ImplementedIssue, prove, verify
Contextual pseudonymsImplementedHMAC-based, per-site
Delegation & leasingImplementedTime/use limits, revocation
Recovery (Shamir)Implementedk-of-n guardian scheme
Recovery (Lazarus Gate)ImplementedThree-factor ceremony
Audit chainImplementedHash-linked, queryable
Attestation (Android)ImplementedKey Attestation x.509 chain
Attestation (iOS)In progressApp Attest stub exists
Attestation (TPM)PartialQuote verification, PCR pending
FederationImplementedExternal OIDC provider trust
Policy engineImplementedDeny-first, audit-always
OIDC complianceImplementedOpenID4VCI, OpenID4VP
mDL (ISO 18013-5)PlannedConformance 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)

Related work