{"slug": "show-hn-fuse-anonymous-daily-rate-limits-for-logins-and-apis-with-zk-proofs", "title": "Show HN: Fuse – Anonymous daily rate limits for logins and APIs with ZK proofs", "summary": "A developer released Fuse, an early-stage open-source prototype that uses zero-knowledge proofs to give each anonymous visitor a daily budget of first-contact tickets — for example 200 per credential — so websites can rate-limit logins, APIs and upload forms without identifying users. The system, published on GitHub under veiovi/fuse and requiring Node.js 22 or newer plus a Chrome extension, records a deposit at a registry that never learns where the credential is used; double-spending a ticket lets the shared log recover the user's key, forfeit the deposit and block the user everywhere. The author states the cryptography is real but the money is play money and nothing has been audited, warning it should not be put in front of anything that matters yet.", "body_md": "**Fair play online, no names needed.**\n\nFuse lets websites stop mass abuse of their logins, APIs and upload forms without knowing who anyone is. Every visitor gets a daily budget of first contacts, and zero-knowledge proofs enforce it.\n\n▶ [Watch the 3-minute animated explainer](https://www.youtube.com/watch?v=QrJPH10kAC4)\n\n**Early prototype.** The cryptography is real; the money is play money.\nNothing has been audited, so don't put it in front of anything that matters yet.\n\nThis repository holds the whole system: a Chrome extension for people, an SDK and command-line tool for AI agents, and the server side, which is a registry, a shared log and a gateway you put in front of a website.\n\nA server facing a stranger asks: *who is this, and can I trust them?* To\nanswer, it checks faces: IP addresses, accounts, CAPTCHAs. Attackers rotate the\nmask. A new IP or a new account is a new visitor, and they try millions of\ntimes a day.\n\nFuse changes the question to *has this client used up its budget?* Mass abuse\nis a quantity problem: one actor, millions of tries. A quantity can be enforced\nwith math, without anyone learning who anyone is.\n\nYou put down a deposit at a registry once and get a credential. The registry records the deposit but never learns where the credential is used. In this prototype the deposit is play money; passports and EU digital-identity wallets are planned alternatives.\n\nEach credential gets a fixed number of first-contact tickets a day, for example 200, valid anywhere on the web. The first time you touch a site's login, API or upload form costs one ticket. Reading public pages is free, and after the first contact the site's normal session takes over.\n\nYou spend a ticket by sending a zero-knowledge proof that says \"a valid credential, within its budget\" without saying whose. Your own device makes the proof, in about 0.2 seconds on an Apple-silicon Mac.\n\nEach site sees its own pseudonym for you. The same site always sees the same one, so a ban sticks. Two different sites can't tell they're seeing the same person.\n\nEvery ticket you spend is one point on a secret line, and the line crosses zero at your key. One point reveals nothing. Spend the same ticket twice and the two points fix the line, which gives away the key. The shared log publishes it, your deposit is forfeit, and every site turns you away.\n\nA million attempts a day at 200 tickets per credential needs 5,000 deposits. An honest person needs one. Fuse can't judge intent: one carefully aimed attack that stays within budget still gets through. What it removes is cheap, unlimited volume.\n\nYou need Node.js 22 or newer (developed on Node 26), and Chrome for the extension.\n\n```\ngit clone https://github.com/veiovi/fuse.git\ncd fuse\nnpm install\nnpm run demo\n```\n\nThis starts a registry, a shared log, two demo websites and a live view of the whole network on your machine:\n\n| Open | What it is | \n|---|---|\n| [http://localhost:4700](http://localhost:4700) | **Explorer** : the registry, the shared log, burned keys and both sites, live, plus four one-click scenarios | \n| [http://forum.localhost:4710](http://forum.localhost:4710) | **Acme Forum** , an ordinary web app behind the Fuse gateway | \n| [http://api.localhost:4720](http://api.localhost:4720) | **Bolt API** , an Express API with the Fuse gateway built in | \n\nIn the explorer, run the four scenarios: an honest agent, a mass scanner that runs out of budget, a cheater whose key is recovered, and a ban that sticks to an anonymous pseudonym.\n\nIf your system doesn't resolve `*.localhost` names, add\n`127.0.0.1 forum.localhost api.localhost` to `/etc/hosts`.\n\n1. Run `npm run build:extension` .\n2. Open `chrome://extensions` , turn on**Developer mode** , click**Load unpacked** and choose`packages/extension/dist` .\n3. In the Fuse popup, choose **Get a credential** (the starter tier gives\n10 tickets a day).\n4. Open [http://forum.localhost:4710/new](http://forum.localhost:4710/new) . The extension asks before it spends a\nticket, makes the proof in the browser, and you're in as`anon-…` .\n\n```\nnpx fuse init --tier pro    # one credential, 200 tickets a day\nnpx fuse fetch \"http://api.localhost:4720/v1/search?q=hello\"\nnpx fuse status\n```\n\nFrom code inside this repository (the packages aren't on npm yet):\n\n``` js\nimport { FuseAgent } from '@fuse/agent';\n\nconst agent = await FuseAgent.create({ registry: 'http://localhost:4701', tier: 'pro' });\n\n// fetch() answers the site's Fuse challenge by itself.\nconst res = await agent.fetch('http://api.localhost:4720/v1/search?q=hello');\n```\n\nAs Express middleware:\n\n``` js\nimport { fuseGateway } from '@fuse/gateway';\n\napp.use(fuseGateway({\n  siteName: 'My app',\n  origins: ['https://myapp.example'],\n  protect: ['/login', '/api/*'],\n  registries: ['http://localhost:4701'],\n  log: 'http://localhost:4702',\n}));\n// Protected handlers see req.fuse = { pseudonym, short, reputation }.\n```\n\nOr as a reverse proxy in front of any app:\n\n```\nnpx fuse-gateway --upstream http://127.0.0.1:3000 --protect \"/login,/api/*\" \\\n  --registry http://localhost:4701 --log http://localhost:4702\n```\n\n1. The client asks for a protected page. The gateway answers `401` with a\none-time challenge:`WWW-Authenticate: Fuse challenge=\"…\"` .\n2. The client reserves a ticket and proves, in zero knowledge: \"I hold a credential in this registry, this ticket is within my daily budget, here is one point on my line, here is the ticket's tag, and here is my pseudonym for this site.\"\n3. It retries with `Authorization: Fuse origin=\"…\", challenge=\"…\", token=\"…\"` .\nThe token is 389 bytes.\n4. The gateway verifies the proof and adds the ticket's tag to the shared log. A new tag opens a session. A tag the log has already seen at another point reveals the key and burns the credential.\n5. Everything after that rides the site's normal session.\n\nNothing changes in IP, DNS, TLS or browsers, and a site can turn Fuse on one endpoint at a time.\n\n| Path | What it is | \n|---|---|\n| [`circuits/fuse.circom`](https://github.com/veiovi/fuse/blob/main/circuits/fuse.circom) | The zero-knowledge circuit: Rate-Limiting Nullifier (RLN) plus a per-site pseudonym (Groth16, 6,076 constraints) | \n| [`packages/core`](https://github.com/veiovi/fuse/blob/main/packages/core) | Shared cryptography and wire format: Poseidon hash, Merkle tree, key recovery, token, proving and verifying | \n| [`packages/registry`](https://github.com/veiovi/fuse/blob/main/packages/registry) | Deposit-backed registry: issues credentials, publishes valid tree roots, burns deposits | \n| [`packages/log`](https://github.com/veiovi/fuse/blob/main/packages/log) | Shared append-only log of ticket tags: catches a reused ticket, recovers the key, publishes it | \n| [`packages/gateway`](https://github.com/veiovi/fuse/blob/main/packages/gateway) | The gateway, as Express middleware or a reverse proxy: proofs, sessions, sticky bans, a strict request check | \n| [`packages/agent`](https://github.com/veiovi/fuse/blob/main/packages/agent) | Agent SDK and the `fuse` command-line tool | \n| [`packages/extension`](https://github.com/veiovi/fuse/blob/main/packages/extension) | Chrome extension (Manifest V3): asks before spending, proves in the browser | \n| [`packages/explorer`](https://github.com/veiovi/fuse/blob/main/packages/explorer) | The live network view and scenario runner | \n| [`demo/`](https://github.com/veiovi/fuse/blob/main/demo) | Two demo sites, four scenarios and the one-command runner | \n\n**Real:**\n\n- Groth16 zero-knowledge proofs, made on the device and checked by every gateway and the log\n- private membership in the registry, and per-site pseudonyms\n- daily budgets, key recovery from a reused ticket, burning the credential and its deposit\n- an append-only log with signed tree heads and inclusion proofs\n- sticky bans and per-pseudonym rate limits\n\n**Simulated or simplified:**\n\n- deposits are play money\n- the proving keys come from a single-party development setup, so whoever ran it could forge proofs\n- there is one registry and one log\n- the gateway's request check is hand-written and fuzz-tested, not formally verified\n\n**Designed but not built:** passport and EU-wallet credentials, and a formally\nverified request parser at the gateway.\n\n[docs/design.md](https://github.com/veiovi/fuse/blob/main/docs/design.md) lists every simplification and open problem.\n\n- [docs/design.md](https://github.com/veiovi/fuse/blob/main/docs/design.md) : the design, who learns what, the security\nproperties and their limits.\n- [docs/protocol.md](https://github.com/veiovi/fuse/blob/main/docs/protocol.md) : the exact messages between the components.\n- [docs/demo.md](https://github.com/veiovi/fuse/blob/main/docs/demo.md) : a walkthrough of the demo.\n\n```\nnpm test                               # unit and integration tests, with real proofs\nnode packages/extension/test/e2e.mjs   # the extension in a real Chromium (via playwright-core)\nnpm run build:circuit                  # recompile the circuit and redo the setup (needs circom 2.1+)\n```\n\nFuse combines existing pieces:\n\n- **Rate-Limiting Nullifiers** from the Privacy & Scaling Explorations team, as\nused for spam protection in Waku. These descend from 1980s digital cash that\nexposed anyone who spent a coin twice.\n- **Privacy Pass** (RFC 9576–9578), whose HTTP authentication shape Fuse's\nheaders follow.\n- **Certificate Transparency** (RFC 6962), the model for the shared log.\n\nMIT. See [LICENSE](https://github.com/veiovi/fuse/blob/main/LICENSE).", "url": "https://wpnews.pro/news/show-hn-fuse-anonymous-daily-rate-limits-for-logins-and-apis-with-zk-proofs", "canonical_source": "https://github.com/veiovi/fuse", "published_at": "2026-10-22 13:54:19+00:00", "updated_at": "2026-10-22 14:11:57.121362+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Fuse", "GitHub", "Chrome", "Node.js", "Acme Forum", "Bolt API", "Express"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-fuse-anonymous-daily-rate-limits-for-logins-and-apis-with-zk-proofs", "markdown": "https://wpnews.pro/news/show-hn-fuse-anonymous-daily-rate-limits-for-logins-and-apis-with-zk-proofs.md", "text": "https://wpnews.pro/news/show-hn-fuse-anonymous-daily-rate-limits-for-logins-and-apis-with-zk-proofs.txt", "jsonld": "https://wpnews.pro/news/show-hn-fuse-anonymous-daily-rate-limits-for-logins-and-apis-with-zk-proofs.jsonld"}}