About Compass

Bounded disclosure, on the wire.

Compass is a private eligibility firewall on 0G. Clinics extend service. Subpoenas reach bucketed timestamps and cryptographic commitments. No name, HKID, employer, or document fields.

Architecture

Compass four-layer architectureUser holds an SD-JWT credential. The vault is AES-256-GCM-encrypted and uploaded to 0G Storage. The receipt-signer inside Phala dstack TDX receives the policy-relevant claims, evaluates the predicate, and signs a receipt with a key sealed inside the attested image. ReceiptIssued lands on 0G Chain — public fields only.Layer 1Next.js · user-controlled EOAUser device· secp256k1 key (Privy wired; fixture by default)· AES-256-GCM vault (Node CLI v1, browser v2)· SD-JWT VC (selective disclosure capable)Layer 2Galileo testnet · encrypted blob0G Storage· ciphertext SD-JWT bundle· Merkle root → AgentRegistry.encryptedURI· user-held decryption key, never on-chainLayer 3Phala dstack TDX · receipt-signer0G Sealed Inference· dstack-derived secp256k1 signer· per-receipt quote (freshness binding)· policy evaluator → eligible / deniedLayer 4AgentRegistry · CompassHub0G Chain· soulbound INFT (agent identity)· single-use grant + nullifier (contract primitives)· ReceiptIssued event (15-min bucket)encrypted vault → 0G Storagepolicy-relevant claimsReceiptIssued

0G integration

Component0G layerRole
Agent ID0G Chain (AgentRegistry)Soulbound INFT bound to the user's EOA; Privy embedded wallet wires the EOA when NEXT_PUBLIC_PRIVY_APP_ID is set, fixture mode otherwise
Encrypted vault0G StorageAES-256-GCM ciphertext of SD-JWT VC bundle; root hash on-chain
Sealed inference0G TeeML / Phala dstack TDXReceives selectively-disclosed claims; evaluates policy; signs receipt with sealed key
Receipt log0G Chain (CompassHub)ReceiptIssued: receiptId, policyId, nullifier, agentIdCommitment, resultHash, attestationDigest, expiry, 15-min timestamp bucket — no name, HKID, employer, or document fields

How Compass differs from adjacent projects

ProjectWhat it provesWhere Compass differs
Worldcoin / World IDProof of personhood via iris scan; one-human-one-vote primitive.Compass proves eligibility for a specific service, not personhood. We don't take biometrics; the worker's claims live in an SD-JWT VC she controls, encrypted with a key only she holds.
Polygon IDZK-proof-based verifiable credentials; selective disclosure of issuer-signed claims.Compass v1 uses SD-JWT + sealed TEE inference instead of in-browser ZK proofs — sub-second receipt mint vs ~5-30s ZK proving. The v2 dual-path roadmap adds an optional ZK route for higher-threat contexts; see docs/zk-future-work.md.
Anon AadhaarZK proofs over India's Aadhaar ID; prove residency / age without revealing the Aadhaar number.Compass is not tied to a specific issuer-of-record. Any NGO with an Ed25519 key can issue a credential that Compass verifies; the trust list is governed separately. The migrant-worker context typically has no trusted government issuer; NGO-issued credentials are the only available path.
zkPassWeb2 data → ZK proofs; prove claims about your bank account, social profile, etc. without revealing the underlying account.Compass operates on NGO-issued attestations of physical-world status (visa, employment, residency), not Web2 scraped data. The threat model — subpoena resistance for already-issued credentials — is different from "import my Web2 attribute privately."
Privacy Pools / Tornado Cash v2Anonymous transactions with optional inclusion proofs into a set of "honest" deposits.Compass is not a transaction-privacy primitive — it's a credential-privacy primitive. The on-chain receipt is the audit trail the system wants; the privacy property is on the inputs the receipt commits to, not on hiding the receipt itself.

We respect every project in this table — most of Compass' primitives were inspired by their work. The differentiation is in the migrant-worker threat model: asynchronous subpoena resistance for NGO-issued service eligibility, optimised for interactive intake at clinic kiosks.

Standards alignment

Compass' selective-disclosure path uses SD-JWT VC (draft-ietf-oauth-sd-jwt-vc, advanced to -16 in April 2026; Compass v0.5 pins -15 for the hackathon cycle with a v0.6 roll-forward planned). This is the same wire format the EU Digital Identity Wallet (eIDAS 2.0, in effect 2025) standardised for member-state digital credentials. The underlying SD-JWT selective-disclosure primitive shipped as RFC 9601 in December 2024.

What this buys us: a Compass-issued credential can be presented to any verifier built against the EUDIW reference stack, and conversely an NGO running Compass alongside an eIDAS-compliant wallet can interoperate without re-issuance. The honest caveat: until the IETF draft hits RFC, every implementation is tracking a moving target — Compass tracks it openly in docs/honest-limits.md §6.

What's real / what's mocked

ComponentStatusNote
AgentRegistry contractrealERC-7857 stripped; deployed to both 0G Galileo testnet (0x461e…c2D8) and 0G Aristotle mainnet (0xf1FA…0Bf9, 2026-05-11)
CompassHub contractrealpolicies + Authwit + receipts; deployed to both 0G Galileo testnet (0x60Bb…7c3b) and 0G Aristotle mainnet (0xe42f…C58b, 2026-05-11)
Browser AES-256-GCM encryptionreallive on /onboard step 3 — non-extractable AES-256 in IndexedDB encrypts the issued SD-JWT VC before localStorage persist; plaintext never enters localStorage
0G Storage ciphertext uploaddraftAES-256-GCM round-trip on Node CLI; live 0G upload behind COMPASS_LIVE_STORAGE=1; browser-side upload is v2
Receipt-signer servicerealdstack TDX dual-boot; per-receipt quote freshness binding
Phala Cloud TDX deployreallive: ethAddress 0xaba6...a7e7, composeHash 0x1884...cea0 — see docs/notes/phala-deployment.md
SD-JWT VC issuers (HELP, Bethune, Hospital)mockedreal NGOs; signing keys are local Ed25519 fixtures, not endorsed by the NGOs
SD-JWT VC live issuer servicedraftPOST /api/issue signs Ed25519 SD-JWT VC when ISSUER_PRIVATE_KEY env set; rendered in /vault from localStorage. v2 adds browser AES-256-GCM + 0G Storage upload
Trust list governancedraftv1 owner-managed via docs/policies/*.json; v2 DAO design specced at docs/trust-list-governance.md (5-of-7 quorum for adds, 3-of-7 for revokes, 7-day timelock with 24h revoke expedite, on-chain TrustList contract on Aristotle)
On-chain verifyAttestationstubbedreal RA quote verification is gas-prohibitive on-chain (cert chain + ECDSA + 4KB quote); off-chain verifier in enclave/src/verify-attestation.ts + verify-receipt CLI does the heavy lift
RA-quote-bound attestationDigest in receiptsreal/api/consume calls live Phala TDX enclave (composeHash 0x1884…cea0, signer 0xaba6…a7e7); attestationDigest is sha256 of canonicalized receipt + per-receipt TDX quote whose report_data binds (signer, image, receiptId). Live state surfaced at /api/tee-status; falls back to stub digest if the enclave is stopped between demo cycles
0G broker processResponse co-signaturedraftout of scope for v1; receipt has its own signature chain
Privy embedded walletdraftwired in /onboard step 1; live behind NEXT_PUBLIC_PRIVY_APP_ID, fixture timer in default build
/onboard step 2 — live mintAgentdraftPrivy embedded wallet → AgentRegistry.mintAgent on Galileo (chainId 16602), gated on user-funded gas; fixture timer when Privy is unset
Authwit grant — browser-side EIP-712 signingrealPrivy embedded wallet signs the Compass Grant typed data on /onboard step 4 (Galileo chainId 16602, CompassHub verifying contract), no popup-required sub-flow
CompassHub.consumeGrantAndIssueReceipt — atomic on-chainrealPOST /api/consume relays Maria's signed grant; PROVIDER_PRIVATE_KEY-held wallet calls Galileo CompassHub; emits GrantConsumed + ReceiptIssued in one tx; nullifier + receiptId stored as used
Aristotle mainnet (chainId 16661) deployrealdeployed 2026-05-11 — AgentRegistry 0xf1FA…0Bf9, CompassHub 0xe42f…C58b. All 3 demo policies registered on-chain (HELP, Bethune, Hospital); provider relayer 0xaD7…b0a funded 0.05 OG. Funding path: MoonPay → ETH on Ethereum L1 → hub.0g.ai TokenFlight Cross-Chain Swap (Hyperstream/Khalani) → native OG on Aristotle. /api/consume reads NEXT_PUBLIC_COMPASS_USE_MAINNET=1 and routes the relayer to Aristotle; unset routes it to Galileo.
Kiosk mode for NGO drop-in centresdraftlive at /kiosk — locked nav, 4-step welcome→sign-in→mint→credential→request-eligibility flow with large touch targets + plain-language labels + receipt-as-intake-artifact ending. Reuses Privy + on-chain primitives from /onboard, restyled for shared tablet use. v2 adds auto-reset timer + audio cues for low-literacy assistance
Kiosk localization (5 languages)draftlive at /kiosk welcome screen — language picker for English, Filipino (Tagalog), Bahasa Indonesia, Bahasa Malaysia, 廣東話 (Cantonese). All non-English strings are AI-generated baseline pending native-speaker review (gated on C.4 outreach replies). String table at app/src/lib/i18n/kiosk-strings.ts; current scope is kiosk-only — the rest of the app stays English. Honest-limits disclosure in the file header
Spline 3D scene on /aboutrealactivated 2026-05-10. @splinetool/react-spline + @splinetool/runtime installed; <SplineScene /> renders below the architecture diagram when NEXT_PUBLIC_COMPASS_SPLINE_SCENE_URL is set. Lazy-imported behind Suspense so the runtime (~530KB) loads off the critical path. Falls back to null when env var is unset, so the route stays unchanged on a clean clone with no env file. R13 LCP guard still in force: if Lighthouse mobile drops below 85, swap to environmental SVG.
HLS / MP4 hero video background on /draftscaffolded at app/src/components/primitives/VideoBackground.tsx; renders null until NEXT_PUBLIC_COMPASS_HERO_VIDEO_URL env var points at an HLS playlist (.m3u8) or MP4 URL. Default hero uses AmbientSphere SVG. Asset prep + Pexels sourcing + ffmpeg recipe documented in docs/notes/hero-video-sourcing.md. R16 LCP guard: Lighthouse mobile floor 85; rollback is unsetting the env var
3D force-graph view of public audit logreallive at /audit-graph.html — standalone HTML using 3d-force-graph CDN; bucket-clustered receipt nodes with no inter-receipt edges (cannot suggest correlation between disclosures). Gentle camera orbit + per-policy color. v2 swaps the fixture node list for live Galileo ReceiptIssued event queries

Cryptographic chain

  1. 01

    SD-JWT VC encrypted in the browser

    Per-device AES-256-GCM key generated in WebCrypto with extractable=false, persisted as an opaque CryptoKey handle in IndexedDB; only ciphertext + IV land in localStorage. 0G Storage upload (Merkle-root committed to AgentRegistry.encryptedURI) is on the v0.6 roadmap.

  2. 02

    Receipt-signer derives sealed key inside TDX

    dstack.getKey('compass-receipt-signer') returns a deterministic secp256k1 priv sealed to the attested image; composeHash is the externally-verifiable binding.

  3. 03

    Per-receipt quote binds (signer, image, receiptId)

    report_data = sha256(ethAddress || composeHash || receiptId). Defeats archived-quote replay across deployments.

  4. 04

    Receipt signed with sealed key, digest covers quoteCommitment

    secp256k1 lowS over canonicalized receipt; verifier recovers signer, matches against quote, validates the chain.

  5. 05

    ReceiptIssued event on 0G Chain

    receiptId, policyId, nullifier, agentIdCommitment, resultHash, attestationDigest, expiry, 15-min timestampBucket. No name, HKID, employer, or document fields.

Honest limits

Compass narrows disclosure but does not eliminate it. Coercion, deniable encryption, full k-anonymity against statistical re-identification, and SD-JWT VC draft churn all remain open issues. See docs/honest-limits.md for the full list.

Credits

Persona narrative inspired by the work of Mission for Migrant Workers, HELP for Domestic Workers, and Bethune House Migrant Women's Refuge in Hong Kong. Compass does not represent any real worker; the demo flow is a composite. Built solo by Stephen Sookra for the 0G APAC Hackathon (Track 5).

Watch the 3-min demo →