Add an API-key auth path to allow trusted bots/agents to use the faucet
- Dominant language
- TypeScript
- Stars
- 30
- Forks
- 29
- Avg merge
- 16h 32m
- Merged PRs (30d)
- 9
Description
### Describe the feature you would like
Summary
Add an optional, key-authenticated path to POST /api/faucet that bypasses reCAPTCHA for trusted programmatic callers (CI, dev scripts, AI agents such as the Celo MCP server), while preserving — and ideally tightening — abuse controls. The existing browser + reCAPTCHA flow stays unchanged.
Background / current behavior
Today every faucet request is gated on a Google reCAPTCHA v3 token:
components/request-form.tsx mints a token via executeRecaptcha('faucet').
pages/api/faucet.ts calls captchaVerify(captchaToken) and rejects the request (HTTP 401) unless Google's siteverify passes.
GitHub auth is optional and only raises limits (AuthLevel.authenticated → 10x amount, higher daily cap). Rate limiting is enforced in the Firebase backend (sendRequest / database-helper.ts) keyed on IP + hashed email.
Because reCAPTCHA v3 is score-based, headless/automated callers score poorly and are rejected, and the token can't be minted without driving a real browser. Net effect: legitimate automation can't reliably use the faucet. This blocks CI pipelines, local dev scripts, and agent tooling (we want to add a request_faucet_funds tool to celo-org/celo-mcp, which needs a non-captcha entry point).
Proposed change
Add an API-key path to /api/faucet:
Accept a key via Authorization: Bearer (or x-api-key).
In pages/api/faucet.ts: if a valid key is present, skip captchaVerify and proceed; otherwise fall through to the existing reCAPTCHA flow (no change for browsers).
Rate-limit per API key (not per IP) for keyed requests — extend sendRequest / database-helper.ts to accept a key identity alongside the existing IP / hashed-email keys.
Keys are individually issuable, revocable, and throttleable.
Files likely touched
apps/web/pages/api/faucet.ts — key check + branch around captchaVerify.
apps/web/utils/ — key validation helper + secret lookup (env / Firestore).
apps/firebase/src/database-helper.ts (+ sendRequest) — per-key rate-limit bucket.
Docs / README — document the endpoint, headers, limits, and how to request a key.
Research & decisions to be made
This is the part that needs discussion before implementation — capturing the open questions and the trade-offs for each.
1. Auth model: static API key vs. wallet-signature vs. proof-of-work.
Static bearer key — simplest to build and consume; matches what Cardano, Chainstack, and Aptos faucets do. Downside: keys leak, so issuance + revocation + per-key limits are mandatory.
Wallet-signature challenge (agent signs a nonce with the destination address) — no shared secret to leak, ties requests to the receiving wallet, naturally Sybil-limited per address. More work for callers; agents must hold a signer.
Proof-of-work — no accounts to manage, but weak Sybil resistance and awkward for agents.
Decision needed: which model (or key now, signature later)? Recommend starting with static keys behind an allowlist, designed so a signature path can be added without a breaking change.
2. Key issuance & storage. Manual allowlist (env var / small Firestore collection) to start, vs. a self-serve request flow. Where do secrets live, and are they hashed at rest? Decision: most likely a Firestore apiKeys collection with hashed keys + owner label + per-key rate config; self-serve deferred to a follow-up.
3. Rate-limit policy for keyed traffic. What daily cap and token amount does a keyed request map to — reuse AuthLevel.authenticated, or introduce a new AuthLevel.apiKey tier with its own numbers? Should limits be global-per-key, or per-(key, beneficiary)? Decision needed on the tier and the bucket key.
4. Abuse / Sybil surface. A captcha bypass is a drain vector on testnet funds if a key leaks or an allowlisted actor misbehaves. Mitigations to decide on: per-key daily cap, global daily ceiling across all keys, instant revocation, and per-key metrics/alerting. Confirm testnet-only scope (no mainnet exposure).
5. Scope of networks. Enable the key path for all faucet networks (incl. Celo Sepolia) or gate it to specific testnets initially?
6. Observability. Add per-key request metrics (extend metrics.ts) so abuse is visible and keys can be throttled/revoked on signal.
7. Alternative considered — x402 / pay-per-call (Circle et al.): metered agent payments instead of allowlisted keys. Almost certainly overkill for a free testnet faucet; note it as a future option if abuse becomes unmanageable, not for v1.
Non-goals
No change to the human browser flow or the reCAPTCHA path.
Not removing rate limits — this changes who proves they're allowed, not how much they get.
No mainnet exposure.
Follow-up
Once this lands, add a request_faucet_funds tool to celo-org/celo-mcp that authenticates via this key path (it would be the first state-changing tool in that server).
### Additional context
_No response_
Contributor guide
Research direction
Start by reading apps/web/pages/api/faucet.ts and the existing sendRequest flow in apps/firebase/src/database-helper.ts, then inspect the apps/web/utils area and current reCAPTCHA path. Done means an agreed auth and key-storage model, per-key limits and revocation, preserved browser behavior, testnet-only scope, observability, and documentation for the endpoint and key process.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- firebase, typescript
- Domain
- api, authentication, backend, databases, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100