Faucet Service¶
The faucet is the platform's sign-up desk. A prospective user hands it an email; it checks the email, creates their account (via the platform service), and — for the free tier — seeds them at the fair-share cap so continuous accrual takes over from there. It emails the user their access token. Small FastAPI app on port 8500, backed by Postgres (creates its own database on first start).
The name is historical — it began as a credit "faucet" that dripped periodic top-ups. Today it's really the signup service; the ongoing top-ups now come from the credit service's fair-share accrual.
It's standalone — it owns its own tables and talks to just one service over HTTP: the platform service (/v1/auth/onboard), which does the actual granting. There's no weekly refill and the faucet no longer talks to the credit service directly — the credit service's fair-share accrual owns ongoing free credits.
Who gets what: free vs paid¶
| Free tier | Paid tier | |
|---|---|---|
| Who | University / allowed-domain email (.edu, …) |
Anyone else |
| Allowed to sign up? | Always | Only when open_registration is on |
| Starting credits | Seeded at the fair-share cap + marked accrual_eligible |
None (buy paid credits via Stripe) |
| Ongoing credits | Continuous fair-share accrual (credit service) | — |
The faucet's real job is the free tier: verify the student, seed them, and hand accrual the keys. Paid users just get an account.
Signing up¶
Signup is owned by the platform (POST /v1/auth/signup, verified email). The platform then calls the admin-auth POST /v1/faucet/register to record the user and enforce the registration policy. The faucet issues no tokens; the anonymous /v1/faucet/claim route was removed (#251) because it returned a platform JWT without proof of mailbox ownership.
Ongoing credits come from the credit service's fair-share accrual (documented there), not the faucet.
Admin¶
All admin endpoints require Authorization: Bearer <ADMIN_TOKEN> and fail closed if the token is unset.
- Registration toggle (
/v1/faucet/pool) —open_registrationgates whether non-university emails can sign up. - User management (
/v1/faucet/users/…) — list users, read one user's status, change their tier, and revoke / restore them. Platform auth reads…/users/{email}/statuson every request, so revoking a user here blocks their access everywhere.
Tables¶
| Table | Purpose |
|---|---|
claims |
One row per email — last_claim timestamp for the cooldown / claim lock |
users |
Registered users — user_type (free/paid), status (active/revoked) |
pool_config |
Singleton — just open_registration |
Endpoints¶
| Method | Path | Auth | What it does |
|---|---|---|---|
| GET | /healthz |
— | Health check |
| POST | /v1/faucet/register |
admin | Record a user in the registry (policy-checked); issues no token |
| GET·PUT | /v1/faucet/pool |
admin | Read / toggle open_registration |
| GET | /v1/faucet/users |
admin | List registered users |
| GET | /v1/faucet/users/{email}/status |
admin | One user's status/tier (platform reads this for revocation) |
| PUT | /v1/faucet/users/{email} |
admin | Change a user's tier |
| POST | /v1/faucet/users/{email}/revoke · /restore |
admin | Disable / re-enable an account |
Configuration¶
| Variable | Default | Description |
|---|---|---|
FAUCET_DATABASE_URL |
— (required) | Postgres connection string (DB auto-created if missing) |
FAUCET_PORT |
8500 | Service port |
ADMIN_TOKEN |
— | Shared secret for admin + the onboard call |
PLATFORM_URL |
http://platform:8100 |
Where accounts are created + seeded |
ALLOWED_EMAIL_DOMAINS |
.edu,gmail.com,atomicmail.io |
Free-tier domains (comma-separated) |
CLAIM_COOLDOWN_HOURS |
~0.017 (≈1 min) | Minimum gap between claims per email |
SMTP_HOST/SMTP_PORT/SMTP_USER/SMTP_PASS/SMTP_FROM |
Brevo defaults | Email delivery |
CORS_ORIGINS |
* |
Allowed origins |
RATE_LIMIT_PER_MINUTE |
120 | Per-IP rate limit |
Running & testing¶
# Unit tests need a Postgres (test compose exposes one on host port 5433):
docker compose -f docker-compose.test.yml --profile faucet up postgres -d
pytest tests/faucet_service/ -v --ignore=tests/faucet_service/test_docker.py
# Full stack for the Docker integration test:
docker compose -f docker-compose.test.yml --profile faucet up --build -d
pytest tests/faucet_service/test_docker.py -v
Coverage: test_claim / test_claim_flows (domain rules, cooldown, tiers), test_claim_concurrency (no double-signup under a race), test_pool (open_registration), test_admin_users (user management), test_endpoints (claim + seed wiring), test_email_verify (SMTP), test_docker (deployed).