Zentity
Concepts

System Architecture

Overview of how Zentity's services connect and how data flows through the system

Zentity is the verified-human signal layer for the agentic internet. Its architecture separates proving (browser), verifying (server), and computing (FHE service) so that no single component handles both plaintext data and policy decisions. This document maps how services connect, how data flows between them, and what crosses each trust boundary, with the deployment context (Web2, Web3, agent) as the axis of variation.

Architecture

Components and Key Technologies

AreaTechnologiesResponsibility / Notes
Web UI + APINext.js 16 (App Router), React 19, Node.js, pnpm, tRPCPrimary UI and orchestration layer with type-safe API routes.
ZK proofsNoir, bb.js (Barretenberg), UltraHonkClient-side proving in Web Workers; server-side verification.
Liveness + face matchHuman.js + tfjs-nodeMulti-gesture liveness and face match; real-time guidance on client, verification on server.
OCRRapidOCR (PPOCRv5), python-stdnumDocument parsing, field extraction, and validation.
FHE and Web3TFHE-rs (Rust), fhEVM, Base mirrorEncrypted computation off-chain, encrypted on-chain attestation, and narrow public payment-time predicates.
StorageSQLite (libSQL/Turso), Drizzle ORMPrivacy-first storage of commitments, proofs, and encrypted blobs.
Auth + key custodyBetter Auth + WebAuthn + PRF + OPAQUE + EIP-712 WalletPasskey, OPAQUE password, or wallet signature authentication; all three derive client-held keys for sealing secrets.
Verifiable credentialsOIDC4VCI, OIDC4VP, SD-JWT VC, DCQL, JARMCredential issuance and wallet presentation via OpenID standards.
HAIP compliance@better-auth/haip (DPoP, PAR, JARM, wallet attestation, DCQL)High Assurance Interoperability Profile for regulated wallet integrations.
CIBA@better-auth/ciba (backchannel auth, poll + ping modes)Agent-initiated async authorization via email/push notification and user approval.
Social recovery signingFROST signer services (Rust/Actix)Threshold signing for guardian-approved recovery (and future registrar).
MCP identity serverNode.js, Hono, @modelcontextprotocol/sdkHTTP/stdio MCP server with OAuth-authenticated identity tools (whoami, my_profile, my_proofs, check_compliance, purchase).
ObservabilityOpenTelemetryCross-service tracing with privacy-safe attributes.

System Diagram

Loading diagram...

Cryptographic Foundation

Four cryptographic pillars (auth + key custody, ZK proofs, FHE, and commitments) interlock to eliminate plaintext data handling. This document focuses on flow and system boundaries; see Cryptographic Pillars for how the primitives bind together, Attestation & Privacy Architecture for data classification, ZK Architecture for proof system design, and Web3 Architecture for on-chain attestation.


Data Handling

We persist only the minimum required for verification and auditability:

  • Commitments and hashes for integrity and deduplication
  • Encrypted attributes (FHE ciphertexts)
  • Proof payloads + public inputs
  • Passkey-sealed profile (encrypted blob; client-decrypt only)
  • OPAQUE registration records for password users (no plaintext or password hashes)
  • Account snapshot state in identity_bundles (validityStatus, effectiveVerificationId, verificationExpiresAt, nullifierSeed, and other non-sensitive operational metadata)
  • Credential history in identity_verifications (OCR and NFC verification rows, supersession lineage, and method-specific metadata)
  • Validity transition and delivery ledgers in identity_validity_events and identity_validity_deliveries
  • Public Base mirror state containing only wallet address, active mirrored attestation state, and numeric compliance level

Key Custody

A two-layer encryption scheme (DEK wrapped by a credential-derived KEK) ensures the server stores only encrypted blobs it cannot decrypt. Users decrypt locally after an explicit credential unlock. See FHE Key Lifecycle for the full key hierarchy and credential-specific derivation paths.

Raw document images, selfies, plaintext PII, and biometric templates are never stored. Full classification and storage boundaries live in Attestation & Privacy Architecture.


Social Recovery

Zentity supports guardian-approved recovery for passkey loss. Recovery is initiated with email or a Recovery ID, guardians approve via email links or authenticator codes, and the signer services perform FROST threshold signing once the approval threshold is met. Recovery wrappers are stored in recovery_secret_wrappers, and the signer coordinator is contacted from the Next.js server (not the browser).

The FROST aggregated signature is not just an authorization gate; it is cryptographically entangled with key custody. The signature is used as input key material for HKDF-SHA256(ikm=signature, salt=challengeId, info="zentity:frost-unwrap") to derive an AES-256-GCM key that unwraps the recovery DEKs. Without the real FROST signature, the DEKs cannot be recovered even with DB access.

Three guardian types are supported: email (approval link), twoFactor (authenticator code), and custodialEmail (Zentity-operated signer, limited to 1 per user, cannot be the sole guardian).


Graduated Disclosure Depth

Zentity supports two usage modes that share the same core cryptography but differ in what is disclosed.

Non-regulated (age-gated / consumer apps)

  • The relying party receives proofs only (e.g., "over 18", "document valid").
  • No PII is shared. Verification is local to the relying party.

Regulated (banks / exchanges)

  • The user authorizes disclosure with a passkey.
  • The client decrypts the sealed profile and re-encrypts to the relying party.
  • The relying party receives PII + proofs + evidence pack as required by regulation.
  • Zentity retains cryptographic artifacts only, not plaintext PII.

ARCOM double anonymity (pairwise flows)

For DCR clients, Zentity defaults to pairwise subject identifiers (subject_type: "pairwise"), preventing cross-RP user correlation. In proof-only flows, additional ARCOM measures apply:

  • Consent records deleted after authorization code issuance (transient linkage)
  • Access token DB records deleted after JWT issuance
  • Session IP/UA metadata scrubbed
  • Opaque access tokens forced for pairwise clients (no JWT sub leakage)

Data Flows

Sign-Up and Verification

Loading diagram...

Password (OPAQUE) flow

OPAQUE authentication uses a password-derived export key:

  • Client performs OPAQUE registration and derives an export key.
  • Export key → HKDF → KEK wraps/unlocks the DEK during verification preflight and other step-up flows.
  • Server stores the OPAQUE registration record (no plaintext password).
  • Secret wrappers are stored with kek_source = "opaque".

Wallet (EIP-712) flow

Wallet authentication uses EIP-712 typed data signing to derive the KEK:

  • User signs an EIP-712 typed data message during wallet authentication and verification preflight.
  • At sign-up, we run a best-effort stability check (sign twice, compare) to reject clearly unstable signers before key wrapping.
  • This check does not guarantee future wallet behavior across firmware/app changes or device migration, so wallet users should add a backup passkey and/or guardian recovery wrapper.
  • Signature bytes are processed through HKDF-SHA256 to derive the KEK.
  • The private key never leaves the wallet; the signature stays in the browser.
  • Server stores the wallet address for account association.
  • Secret wrappers are stored with kek_source = "wallet".
  • Sign-in also requires a SIWE (EIP-191) signature for session authentication (nonce-based replay protection).
  • Supports hardware wallets (Ledger/Trezor) for enhanced security.

Disclosure

Wallets can receive credentials and respond to presentation requests directly via OID4VP, bypassing the OAuth consent UI. The verifier creates a VP session with a DCQL query, signs a JAR JWT with x5c chain, encodes it as an openid4vp:// QR code URI. The wallet presents an SD-JWT VP with KB-JWT binding, encrypted via JARM.

Loading diagram...

All token requests require DPoP (sender-constrained tokens) and PAR (pushed authorization requests). See OAuth Integrations for protocol details.

Agent Authorization (CIBA)

Agents and applications can request user authorization without a browser redirect via CIBA. The agent sends a backchannel request identifying the user; the user receives a push notification (with email fallback) and approves from the dashboard. If identity scopes are requested, the user unlocks their vault and PII is staged ephemerally for delivery via the userinfo endpoint. Poll and ping delivery modes are supported.

Loading diagram...

The access token includes an act claim identifying both the human and the agent. See Agent Architecture for host registration, session lifecycle, protocol composition, and security properties. See OAuth Integrations for endpoints, scopes, and configuration.


OCR + Liveness

Document OCR

  • The OCR service extracts fields (name, DOB, document number, country) and validates formats.
  • Images are processed transiently and never stored.
  • Only derived claims and commitments return to the web app.

Liveness + face match

  • The client runs Human.js for real-time detection and gesture guidance (smile, head turns).
  • The server re-verifies frames with Human.js (tfjs-node) and issues signed liveness/face-match claims.
  • This split keeps UX responsive while preserving server-side integrity.

For detailed liveness policy and integrity guarantees, see Tamper Model and Attestation & Privacy Architecture.


Web3 Layer

Zentity can attest verified identity on-chain using fhEVM while keeping attributes encrypted. The server (registrar) encrypts identity attributes and submits attestation; users authorize access with explicit grants.

For payment-time and resource-server checks, Zentity also writes a narrow public Base mirror that exposes only isCompliant(address,uint8), active mirrored attestation state, and numeric compliance level. The mirror is a delivery surface of the identity validity pipeline, not a second identity authority.

See Web3 Architecture and ADR-0005.


Verifiable Credentials (SSI)

Zentity issues portable verifiable credentials following OpenID standards:

  • OIDC4VCI: Pre-authorized code flow with DPoP-bound tokens, immediate and deferred issuance, status list revocation.
  • OIDC4VP: DCQL presentation queries with x509_hash client_id, JARM encrypted responses, AKI-based trust filtering.
  • SD-JWT VC: Selective disclosure with KB-JWT holder binding (cnf.jkt thumbprint, signature, freshness).

External wallets can receive and hold credentials; third-party verifiers can request presentations without Zentity involvement.

Credentials contain only derived claims, never raw PII. Claims like verified, age_verified, and verification_level indicate verification status without exposing underlying data.

See SSI Architecture for the complete Self-Sovereign Identity model.


Account-Scoped Identity Model

Zentity models identity as one account-scoped snapshot plus credential history.

  • identity_bundles is the current account snapshot. It stores the authoritative effectiveVerificationId, current validityStatus, the freshness deadline (verificationExpiresAt), and the bundle-owned nullifierSeed used for stable per-RP anti-abuse claims.
  • identity_verifications is credential history. OCR and NFC credentials append here, and older authoritative rows can become explicitly superseded without being deleted.
  • reconcileIdentityBundle(userId) is the only bundle reconciler. Verification lifecycle checkpoints call it to select the authoritative credential, compute freshness state, and preserve or reseed the RP nullifier seed according to bundle policy.
  • identity_validity_events records immutable lifecycle transitions such as verified, stale, revoked, and superseded. identity_validity_deliveries tracks downstream effects such as credential-status updates, back-channel logout, RP validity notice, CIBA cancellation, blockchain revocation delivery, and Base mirror writes.

This split is what lets OCR and NFC credentials coexist on one account without making disclosure, assurance, or operator reads re-rank raw rows ad hoc.


State Durability & Shared Devices

  • Sign-up state is local React state only (no DB, no cookies). Refreshing the page restarts the sign-up form. An anonymous session is created on load for the credential flow.
  • FHE enrollment state is tracked server-side via identity_bundles.fheKeyId. Enrollment is resumable; if partial, the preflight re-checks completion criteria.
  • Verification progress and history live in first-party DB tables keyed by user ID, with the current snapshot in identity_bundles, credential rows in identity_verifications, and supporting artifacts in drafts, signed claims, and ZK proofs.
  • Validity transitions and downstream delivery state are durable and auditable through identity_validity_events and identity_validity_deliveries.
  • Profile data lives in a credential-encrypted vault (encrypted_secrets + secret_wrappers), only decryptable client-side after a credential unlock.
  • Identity PII is never stored server-side. At consent time, PII is staged ephemerally in memory (5min TTL) and consumed once through the userinfo delivery path. The user's profile vault is the only persistent PII source.

Observability

  • Distributed tracing via OpenTelemetry across Web, FHE, and OCR
  • Sign-up and verification spans for step timing + duplicate-work signals
  • Privacy-safe telemetry (hashed IDs only; no PII)

Compliance Derivation Engine

Compliance level is computed by a pure function that takes ZK proofs, signed claims, encrypted attribute presence, and flags as input, with no DB access, no mutable booleans, and no side effects.

Levels

LevelNumericCriteria
none1Default; fewer than half of the 7 checks pass
basic2At least half of the 7 checks pass
full3All 7 checks pass
chip4NFC chip verification + sybil resistance

verified is true only for full or chip.

The 7 Boolean Checks

Each check is derived from proof/claim existence, not stored boolean columns.

CheckOCR path sourceNFC chip path source
documentVerifieddoc_validity ZK proof existsAlways true (chip is the document)
livenessVerifiedliveness_score signed claim existschip_verification claim type present
ageVerifiedage_verification ZK proof existschip_verification claim type present
faceMatchVerifiedface_match ZK proof or face_match_score claimchip_verification claim type present
nationalityVerifiednationality_membership ZK proof existshasNationalityCommitment flag
identityBoundidentity_binding ZK proof existschipNullifier present
sybilResistantEffective verified credential has an internal identity keyEffective verified credential has an internal identity key

For NFC chip checks, only claim type presence matters; boolean payloads inside the claim are ignored. This prevents a malicious server from downgrading compliance by flipping a stored boolean.

birthYearOffset

A privacy-preserving age representation: currentYear - birthYear. Validated to the range 0–255 (uint8). Stored in identity_verifications as a proof output, never client-supplied.

See Attestation & Privacy Architecture for the full data classification table and SSI Architecture for how compliance level feeds into verifiable credential claims.


Identity Revocation

Identity revocation is part of the same validity pipeline that handles freshness and re-verification.

  • Admin revocation, self-service revocation, and chain-ingested revocation all converge on the same validity transition boundary.
  • The account snapshot in identity_bundles becomes revoked, clears effectiveVerificationId, and clears nullifierSeed so a later full re-verification can establish a new unlinkable seed.
  • Credential history is preserved. Verification rows are retained for audit; current trust lives on the bundle snapshot and in the validity ledger.
  • Downstream effects fan out through the delivery ledger: issued credential status updates, back-channel logout, pending CIBA cancellation, RP validity notice, blockchain attestation revocation, and Base mirror revocation.

After revocation, the user's assurance posture drops, current proof claims stop reflecting a valid identity, and document-level dedup guards are released according to the current product policy.


Storage Model

Database schema and table relationships are documented in Attestation & Privacy Architecture. The Drizzle schema is the authoritative source for column-level detail.

On this page