Salary disclosure circles, sealed to an Intel TDX enclave.
A small group at the same company / role / level each privately submits their compensation into a confidential-computing enclave on EigenCompute. A classifier — Qwen 2.5 3B Instruct, running inside the same enclave — validates each submission against a public rubric. Once N validated submissions accumulate, all reveal simultaneously, signed by a key that exists only inside the chip.
The operator (me, EigenCloud, any human) literally cannot read any submission before the threshold trips. Intel TDX enforces it in silicon. There is no privacy policy to trust.
| Live demo | https://sealed-abvaj0hy4-vcraz0r-gmailcoms-projects.vercel.app |
| Backend (TEE) | https://34-178-145-214.nip.io |
| EigenCloud verifier | https://verify-sepolia.eigencloud.xyz/app/0x01B009899E66b52CF2295b8F79C3fc4E624c0A64 |
| Source | https://github.com/VictorChenCA/sealed |
| Network | Sepolia (EigenCompute private preview) |
Pay opacity costs American workers an estimated $100B/year in suppressed wages. The asymmetry isn't between you and HR — it's between you and the four other engineers at your level on your team. Levels.fyi has 750k+ disclosures, but the band tells you nothing about the conversation in your room.
That conversation never happens because whoever shares first gives everything away and learns nothing. It's a coordination failure, not a privacy problem. Existing tools (group chats, anonymous surveys) all fail the same way: someone has to read the data first.
Sealed makes "share simultaneously, or not at all" a tractable primitive. The platform you'd trust to mediate the reveal can't, because the silicon won't let it.
- Create a circle: a host defines a scope (
company / role / level / city) and a thresholdN(default 3). - Submit privately: each participant posts structured comp data (base, bonus, equity, vest, signing, city, notes) under a chosen pseudonym. A classifier inside the enclave validates plausibility against the public rubric and returns a verdict
{valid, score, reason}. - Wait: while
N-1others submit, no one — including the operator — can read any value. The status page shows progress (2 of 3 valid). - Reveal atomically: when the Nth validated submission lands, the enclave signs the full payload with its derived wallet and the dashboard unblurs all submissions at once. Median, p25, p75 surface alongside.
- Verify: anyone can hit
/verify, click through to GitHub / HuggingFace / Etherscan, or runscripts/verify-locally.shto independently confirm the running code, the model, the rubric, and the reveal signature.
Three independent chains converge in this picture:
- Request chain (left side) — user's browser hits the Vercel frontend, which proxies API calls through to the enclave's Caddy reverse proxy via Next.js rewrites. Caddy (running inside the TEE) terminates TLS so EigenCloud's networking infra cannot decrypt traffic. Express handles the API, calls Qwen for classification, signs reveals with the KMS-sealed wallet.
- Source-of-truth chain (right side) — every deployment is built from a pinned GitHub commit by EigenCloud's own build pipeline. The
(commit_sha, image_digest)tuple is registered on-chain at the Sepolia smart contract. Anyone can reproduce the build and confirm the digest matches. - Attestation chain — the wallet that signs reveals is derived from a mnemonic that EigenCloud's KMS releases only to the exact attested image digest. Modify the image, the mnemonic is refused. The wallet's pubkey is queryable in
/verifyand visible on Sepolia Etherscan.
The same diagram is also committed to docs/architecture.png as a backup asset in case the GitHub-hosted version above ever becomes unavailable.
Every reveal is verifiable against four independent public artifacts. None require trusting the operator.
| # | Link | What you check | How |
|---|---|---|---|
| 1 | Source matches running code | commit_sha returned by /verify exists in the public GitHub repo |
git clone && git checkout <sha> — read the actual code |
| 2 | Classifier model verified | classifier.model_sha256 matches the Dockerfile-pinned HuggingFace revision |
The Dockerfile aborts the build if the downloaded model doesn't sha256-match; HuggingFace's revision URL is immutable |
| 3 | Rubric pinned at v1.0 | rubric_version matches the rubric file in the same commit |
Open src/classifier/qwen.ts at the commit |
| 4 | Reveal signed by enclave key | The signature on /api/circles/:id/reveal verifies against enclave_pubkey |
Standard ECDSA recover; pubkey is also Etherscan-queryable as the only wallet with this private key |
git clone https://github.com/VictorChenCA/sealed && cd sealed
bash scripts/verify-locally.sh https://34-178-145-214.nip.ioThat script fetches /verify, confirms the commit exists on GitHub, downloads the Dockerfile at that commit, extracts the pinned MODEL_SHA256 value, and confirms the live classifier's model hash matches. Three checks, ~3 seconds.
curl https://34-178-145-214.nip.io/verify
# → note commit_sha
git clone https://github.com/VictorChenCA/sealed && cd sealed
git checkout <commit_sha>
docker build -t sealed:verify .
# image digest should match the on-chain record at:
# https://verify-sepolia.eigencloud.xyz/app/0x01B009899E66b52CF2295b8F79C3fc4E624c0A64| Approach | Operator can read submissions? | Engineering cost today |
|---|---|---|
| Trust a centralized server (AWS / GCP raw) | Yes | Low |
| Multi-party computation | No | High; latency-prohibitive for LLM judging |
| Fully homomorphic encryption | No | Too slow for LLM inference today |
| Zero-knowledge proofs of LLM inference | No | 2-3 years from production-runnable |
| Intel TDX in an attested enclave (Sealed) | No, enforced in silicon | Modest; this project shipped in 12 hours |
The remaining trust assumption is Intel itself — that the TDX attestation key hasn't been extracted by a nation-state actor. EigenLayer is layering crypto-economic security on top (restaking-based slashing for misbehaving enclaves) as defense-in-depth. v1 is hardware-rooted only.
- Runtime: EigenCompute (Intel TDX,
g1-standard-4t: 4 vCPU, 16 GB) - Backend: Node 22 + Express
- Classifier: Qwen 2.5 3B Instruct Q4_K_M (GGUF) via
node-llama-cpp, ~2 GB weights baked into the image, sha256-verified at build time against HuggingFace's pinned revision - TLS: Caddy reverse proxy inside the enclave (Let's Encrypt via HTTP-01, certs never leave the chip)
- Signing: Wallet derived from
MNEMONICsealed by EigenCompute KMS to the exact image digest - Frontend: Next.js 15 App Router + TypeScript + Tailwind, deployed to Vercel
- Auth: Privy multi-method (email / Google / Apple / GitHub / Discord / Farcaster / wallet)
- Chain: Sepolia testnet; on-chain app contract at
0x01B009899E66b52CF2295b8F79C3fc4E624c0A64 - Build: EigenCloud verifiable build mode — image built from
github.com/VictorChenCA/sealedat the pinned commit, provenance signed, image digest registered on-chain
The backend (Express + Qwen) and frontend (Next.js) are separate processes; run them in two terminals on different ports.
# Terminal 1 — backend on :3000
npm install
node scripts/download-model.mjs # ~2 GB, one-time
cp .env.example .env # set a test MNEMONIC for local only
npm run dev
# Terminal 2 — frontend on :3001
cd frontend
cp .env.example .env.local # set NEXT_PUBLIC_PRIVY_APP_ID + NEXT_PUBLIC_API_BASE_URL=http://localhost:3000
npm install
PORT=3001 npm run devOpen http://localhost:3001 — the frontend's next.config.ts rewrites /api/* and /verify to whatever NEXT_PUBLIC_API_BASE_URL points at, so the same code runs against either the local backend or the live enclave.
ecloud compute app deploy \
--force --name sealed --env-file .env.deploy \
--instance-type g1-standard-4t \
--environment sepolia \
--log-visibility public --resource-usage-monitoring enable \
--verifiable \
--repo https://github.com/VictorChenCA/sealed \
--commit $(git rev-parse HEAD) \
--build-dockerfile DockerfileVerifiable build mode delegates the Docker build to EigenCloud's pipeline. No local Docker required. End-to-end deploy time: ~7 minutes (build + push + on-chain register + TEE provisioning + cold start).
- Sybil resistance. Pseudonyms are free-text in v1. Roadmap: wallet-based posting (one submission per address), optional offer-letter cross-check by the classifier, or a small USDC bond per submission. The privacy guarantee (operator can't read) is independent of sybil resistance, so this is additive.
- Classifier correctness. Qwen 2.5 3B at Q4 is good at "is this offer plausible" — not at adversarial rubric-gaming. The rubric is public, so the bar is auditable. Upgrades to Qwen 7B (CPU-bound at current instance size) or to confidential-GPU instances will land when EigenCloud ships CC-GPU.
- Scale-to-zero. g1-standard-4t bills 24/7. A "warm one second after wake" tier would transform the cost profile for sporadically-used circles.
- Cross-circle aggregates. Sealed never produces cross-circle statistics today — by design, every reveal is bounded to one circle. A future "trusted aggregate over many circles" feature would itself need to run in the enclave with separate attestation.
- Open https://sealed-abvaj0hy4-vcraz0r-gmailcoms-projects.vercel.app
- Sign in (optional) — Privy modal supports email, Google, Apple, GitHub, Discord, wallet
- Click into the open Google · SWE · L4 circle (or create your own)
- Submit a disclosure — watch the 5-stage classifier loading inside the TEE
- When the threshold trips, the reveal unblurs all submissions atomically
- Click "See proof →" → 4 trust cards, each clickable to GitHub / HuggingFace / Etherscan
- Or skip the UI entirely:
bash scripts/verify-locally.sh https://34-178-145-214.nip.io
Built during the EigenCloud Private Preview Demo Day program. Thanks to:
- The EigenCloud team for the verifiable-build pipeline — it's the most polished TEE DX I've used.
- Anthropic Claude Design for the UI port that landed in this commit history.
- Qwen team for shipping a 3B-parameter model that runs usefully on CPU.
- ecloud Slack helpers (Matt, Mustafa) for unblocking the sepolia TLS path.
MIT.