Search projects, hackathons, sponsors and tech

    ← All projects

    SPECTER

    SPECTER: Stealth Post quantum enabling Cryptographic Transaction Engine for Routing

    Demo

    About

    SPECTER — The private payments layer for onchain money, built to outlive quantum computers. "Send to a name. Only the recipient can discover, link, or spend it." ═══════════════════════════════════════════ THE PROBLEM ═══════════════════════════════════════════ • Every payment on a public blockchain is a permanent, searchable record — your counterparties, balances, salary, and runway are visible to anyone with a block explorer. • Stealth addresses (Umbra, Fluidkey, ERC-5564) are the current fix: each payment gets a fresh, unlinkable address. • But EVERY one of them is built on ECDH over secp256k1 — the exact math Shor's algorithm breaks on a quantum computer. • This is not theoretical. "Harvest now, decrypt later": adversaries are archiving on-chain data today to de-anonymize it tomorrow. Announcement data is public and permanent — it cannot be re-encrypted after the fact. • NIST finalized its post-quantum standards in 2024 (ML-KEM / FIPS 203) and the migration is already being mandated. On-chain payment privacy has to be rebuilt once, correctly, before that deadline. ═══════════════════════════════════════════ WHAT SPECTER IS ═══════════════════════════════════════════ SPECTER is a production-grade stealth address protocom-vulnerable key exchange at the heart of stealth addresses with ML-KEM-768 (NIST FIPS 203) — the standardized post-quantum KEM — while keeping the UX you just pay to alice.eth or bob.sui. It ships as a full stack, not a paper: • specterpq.com — live web wallet: create an identitysend, scan, claim, and sweep funds with receipts and claim history. • @specterpq/sdk — browser-first SDK (Rust → WebAssemys, payment construction, and scanning all run in the user's browser. • specter-api — public-data services: announcement reon, IPFS pinning, gas-sponsored relaying. • specter-* (Rust workspace) — the core: ML-KEM-768 pth derivation, batch scanning engine, Turso/libSQL persistence. • MCP server with per-call x402 payments — AI agents with no API key and no subscription. Live today on Ethereum (mainnet + testnet) and Sui (mainnet + testnet), with ENS and SuiNS name resolution, plus an announcer contract deployed ═══════════════════════════════════════════ HOW IT WORKS — THREE STEPS ═══════════════════════════════════════════ ① SET UP (once) The recipient's device generates two keypairs — a sa post-quantum ML-KEM-768 VIEWING key — bundles the public halves into a meta-address, pins it to IPFS, and attaches the CID to an ENS or SuiNSeave the device. The meta-address is public and reveals no history. ② SEND (pay to a name) The sender resolves alice.eth → meta-address, derivs for this payment, sends funds there, and publishes a small announcement to the registry. The sender learns nothing about the recipient's other paymentsn can link the payment back to alice.eth. ③ RECEIVE (discover and claim) The recipient's wallet scans announcements, discardly with a 1-byte view tag, and recognizes what's theirs. Claiming is one in-app flow: live balances for every discovered stealth address, sweep to any ransfer signed locally — no private-key export, no wallet import. Produces a downloadable receipt (PNG/PDF/JSON) and a per-identity claim history. ═══════════════════════════════════════════ THE CRYPTOGRAPHY (for technical reviewers) ═══════════════════════════════════════════ Meta-address (protocol v2): spending_pub (secp256k1, 33B) ‖ viewing_pk (ML-KEM-NS / SuiNS Per-payment derivation: (shared_secret, ciphertext) = ML-KEM-768.Encaps(viewing_pk) t = H_to_scalar("SPECTER_STEALTH_TWEAK_V2" ‖ P = spending_pub + t·G // stealth address — sender-computable from PUBLIC data p = spending_sk + t (mod n) // stealth priT spending key eth_addr = keccak256(uncompressed(P))[12:] sui_addr = blake2b256(0x01 ‖ compressed(P)) view_tag = SHAKE256("SPECTER_VIEW_TAG_V1" ‖ shared_secret, 1)[0] Why this split is the right one: • The VIEWING key is post-quantum. It is the key thatecisely what harvest-now-decrypt-later attacks target. It stays quantum-safe forever. • The SPENDING key is secp256k1 because Ethereum and . A quantum adversary able to break it could already drain any ordinary account, so the meaningful post-quantum guarantee belongs in discovery — which Key properties: • Because p·G = P, the address the sender funds is exactly the one the recipient can spend — and the sender provably CANNOT derive p. This is enession test (sender_cannot_derive_stealth_private_key). • View-only discovery: detection needs only the viewiC key, so it can run on a watch-only client, an auditor, or a service. Turning a discovery into a spendable key is a separate, device-local step. • Publish integrity: a wrong view_tag would make a payment land correctly yet be invisible forever. SPECTER closes this with a server-authoritt_id → the server publishes the exact announcement it generated) plus a client-side pending vault, a phase-aware send state machine, and one-click rec ═══════════════════════════════════════════ PERFORMANCE ═══════════════════════════════════════════ • ML-KEM-768 key generation ............... < 1 ms • Encapsulation (stealth address creation) . < 2 ms • View-tag check per announcement .......... ~0.5 µs • Full scan — 100,000 announcements ........ ~1–2 s ed systems) • Announcement publish (Turso/SQLite) ...... < 5 ms The 1-byte view tag is the dominant speed primitive: s ever requires a full ML-KEM decapsulation. ═══════════════════════════════════════════ SECURITY & TRUST MODEL ═══════════════════════════════════════════ Design principle: the server holds no secret it could • Non-custodial by construction — keygen, scanning, and spend-key derivation run in-browser via the WASM SDK. spending_sk and viewing_sk never deliberately exposes no endpoint that accepts a secret key. • Standards-grade pure-Rust crypto (RustCrypto: ml-keth #![forbid(unsafe_code)] workspace-wide. • Secrets are Zeroize'd on drop; comparisons are cons • On-device key vault: secret keys stored only as AES-256-GCM ciphertext, unlocked by password (PBKDF2-SHA256, 600k iterations) or passke • Hardened services: per-IP rate limiting on writes, security headers, WAL-mode SQLite. ═══════════════════════════════════════════ INTEGRATIONS ═══════════════════════════════════════════ • ENS + SuiNS — pay to a human-readable name; the met name record. • IPFS (Pinata) — meta-addresses pinned and content-addressed. • Yellow Network — private state-channel trading. Trahe trading relationship on-chain (Alice ── channel ──> Bob). SPECTER routes the channel through a one-time stealth address (Alice ── channel ──> 0xStnot connect the channel to Bob or prove that Alice and Bob trade at all. • Monad Testnet — deployed announcer contract (0x7a687B5a7c98c880f23F00003A820e7E2fF7fDaC). • MCP + x402 — ten agent-callable tools plus publishelti-step flows, so an AI agent can send a private payment unaided. ═══════════════════════════════════════════ WHO IT'S FOR ═══════════════════════════════════════════ • Payroll & contractors — pay a team on-chain without • Corporate & DAO treasuries — move funds without broadcasting cash position and vendors. • Creators, donations & aid — receive at a public namacker. • Personal finance — get paid to you.eth without turning it into a lifelong searchable profile. • Wallets & apps — embed quantum-safe private payment SDK calls. ═══════════════════════════════════════════ WHY IT MATTERS ═══════════════════════════════════════════ SPECTER is the first production-grade stealth address protocol that is quantum-safe — and it's shipped, not proposed. It extends ERC-5564 with built on peer-reviewed research (arXiv 2501.13733) and the NIST FIPS 203 standard, and is open source across the entire stack. Every competitor will eventust-quantum cryptography. We already did. Live: specterpq.com · Docs: docs.specterpq.com · SDK: npm @specterpq/sdk

    How it's made

    STACK • Core: Rust workspace, 13 crates, #![forbid(unsafe_code)]. RustCrypto only — ml-kem (FIPS 203), sha3 (SHAKE256), k256, blake2, aes-gcm, subtle, zeroize. • SDK: the same Rust core compiled to WebAssembly, shipped as @specterpq/sdk on npm. One codebase is the CLI, the server, and the browser crypto — no reimplementation, no chance of the JS and Rust derivations drifting apart. • API: Axum + tower-http, governor for per-IP rate limits, alloy for Ethereum, reqwest for IPFS/RPC. Release profile: LTO, codegen-units=1, panic=abort, strip. • Web: React 18 + Vite + TypeScript, Tailwind/shadcn, Dynamic Labs (EVM wallets), @mysten/dapp-kit + @mysten/suins (Sui), @erc7824/nitrolite (Yellow), TanStack Query. • Data: Turso (libSQL), pulled in remote-only with default-features=false so release builds carry no C library — the container stays tiny and cross-compiles clean. • Indexing: Envio HyperIndex on Monad testnet, dual-writing to Envio Postgres (GraphQL) and Turso (what the scanner reads), plus a standalone viem poller as a fallback path. • Deploy: Railway (Rust API + poller), Vercel (web), Pinata (IPFS), PostHog. THE HACKY PARTS WORTH MENTIONING 1) Fitting a 1088-byte ML-KEM ciphertext into ERC-556 ERC-5564's ephemeralPubKey field is bytes32. An ML-KEM-768 ciphertext is 1088 bytes — it does not fit, and logging it would be brutally exp only keccak256(ciphertext) as ephemeralKeyHash, while the full ciphertext rides along in the announce() calldata. The indexer turns on Envio's traor `input`, decodes the ciphertext back out of the calldata, verifies keccak256(ciphertext) === ephemeralKeyHash, and drops the row on mismatch. Cheauctable off-chain, still ERC-5564-shaped. 2) A metadata blob that is encrypted but still scannable. Announcement metadata is 77 bytes plaintext: [view_ta32B] [chain_id 8B][reserved 4B]. Encrypting all of it would break scanning; leaving it clear publishes the amount. So byte 0 (the view tag) stays ] are AES-256-GCM sealed → 93 bytes on-chain. Scanners still filter 255/256 events with one byte comparison and zero crypto; only the recipient rs. The key and nonce are derived from the ML-KEM shared secret with domain-separated SHAKE256 — key = SHAKE256("SPECTER_META_ENC_KEY_V1" ‖ nonce = SHAKE256("SPECTER_META_ENC_NONCE_V1" ‖ ss)[..12]. Every encapsulation yields a fresh shared secret, so nonce reuse is structurally i to be stored or transmitted. 3) Making a wrong view tag impossible. A view_tag that disagrees with the ciphertext producerectly and is invisible to the recipient forever — unrecoverable, silent. We made publishing server-authoritative: the client calls create, gets ad the server publishes the exact announcement it generated. The client also keeps a pending vault (public fields only) and a phase-aware send staed send resumes from a later session instead of stranding funds. 4) Scanning that scales. 1-byte view tag first (~0.5 µs, discards ~99.6%), the on the survivors — 100k announcements in ~1–2 s. Backed by a 256-slot LRU per view tag and per-wallet scanner checkpoints for incremental restar 5) Keys that never leave the device. Secrets live only as AES-256-GCM ciphertext in the browser, unlocked by password (PBKDF2-SHA256, 600k iterations) or passkey (WebAuthnberately has no endpoint that accepts a secret key. A regression test (sender_cannot_derive_stealth_private_key) proves the recipient's spend key. PARTNER TECH — WHAT IT ACTUALLY BOUGHT US • ENS + SuiNS — the entire UX rests on this. Users paess (1.2 KB, way past any text record) lives on IPFS with only the CID on the name record. • Monad — cheap enough to put the announcement registnadBFT's single-slot finality let us index at confirmed_block_threshold: 2 instead of waiting out reorgs. • Envio HyperIndex — the calldata trick above only works because field_selection can request raw transaction input alongside the event. • Bazantic x402 Gateway — wrapped the API as 10 MCP tttlement (Tempo, chainId 4217), plus 3 Recipes encoding the multi-step ordering. We A/B'd it: an agent with raw tools produced 77-byte unencrypte with the Recipe produced 93-byte encrypted metadata. Same network, same tools — the Recipe was the only variable, and the difference is measurable