API Keys
Tiers, rate limits, issuance, rotation.
Tiers
| Tier | Rate limit | Use case | Issuance |
|---|---|---|---|
| Anonymous | 60/min per IP | Docs browsing, quick curls | None needed |
| Free | 60/min per key | Signed-up devs (placeholder) | Not currently issued |
| Pro | 600/min per key | Integrators, dashboards | Internal CLI |
| Enterprise | 6,000/min per key | Volume consumers, analytics | Internal CLI |
Per-route overrides always apply (see API.md).
Issuance (internal CLI)
pnpm --filter @hatch/api exec tsx src/scripts/issue-api-key.ts \
--tier pro \
--label acme-prod \
--owner ops@acme.xyz
Output:
Created key: hk_live_abc123def456...
Prefix: hk_live_abc1
Tier: pro
Label: acme-prod
Owner: ops@acme.xyz
Give this plaintext key to the customer NOW. It will never appear again.
You see the plaintext exactly once. The DB stores only the sha256 hash and an indexed prefix for identification. If you lose the plaintext, issue a new one and revoke the old.
Usage
Authorization: Bearer hk_live_abc123...
// @hatch/sdk
import { HatchClient } from '@hatch/sdk';
const hatch = new HatchClient({ apiKey: process.env.HATCH_API_KEY });
await hatch.getScoreByToken('0x...');
# curl
curl -H "Authorization: Bearer hk_live_abc..." \
https://api.gohatch.fun/api/v1/score/0x...
Storage model
apiKeys table:
id uuid
prefix text (indexed, e.g. "hk_live_abc1") ◀── shown in dashboards
hashSha256 text (64 hex chars) ◀── verification
tier text ('free' | 'pro' | 'enterprise')
label text (customer-facing name)
owner text (internal ops contact)
createdAt timestamptz
lastUsedAt timestamptz (updated on every successful auth)
revokedAt timestamptz (soft-delete; null = active)
- sha256, not bcrypt. API-key auth is high-throughput (every request). sha256 is constant-time enough that we don't need bcrypt's cost factor, and the secret is user-generated high-entropy (128 bits from crypto RNG) so dictionary attacks are impractical.
- Prefix is indexed so a leaked key in a log can be identified (hatch.support can search by prefix without ever seeing plaintext).
lastUsedAtupdates every successful auth so ops can identify unused keys for rotation.
Middleware flow
incoming request
│
▼
apiKeyAuth middleware
├─ no header: anonymous tier (60/min per IP)
├─ header present, invalid format: 401 unauthorized
├─ header present, valid format:
│ ├─ sha256(presented) vs stored hash
│ ├─ miss: 401 unauthorized ◀── never silently downgrade
│ ├─ hit + revokedAt != null: 401 revoked
│ └─ hit + active: authenticate, set ctx.tier, update lastUsedAt
└─ downstream rate-limit middleware uses ctx.tier
Source: apps/api/src/middleware/api-key-auth.ts.
Rotation
Rotate quarterly, on a key compromise, or when an owner departs.
# 1. Issue new key
pnpm --filter @hatch/api exec tsx src/scripts/issue-api-key.ts \
--tier pro --label acme-prod-v2 --owner ops@acme.xyz
# 2. Share new key with customer. Give them 48h grace.
# 3. Revoke old key
pnpm --filter @hatch/api exec tsx src/scripts/revoke-api-key.ts \
--prefix hk_live_abc1
Customers see the old prefix in their own systems; they can confirm the revoke landed via a 401 on the old key.
Customer-facing self-serve (planned — H.3)
Sprint H.3 ships:
- Customer dashboard at
/dashboard/api(after wallet auth). - Self-serve issuance, rotation, and revocation.
- Usage metering + monthly billing for pro/enterprise tiers.
- Webhooks for
apikey.near_rate_limit.
Today: issuance + rotation + revocation happen through the internal CLI
only. Customers email support@gohatch.fun to request a key.
Security rules
- Never log the plaintext key — only the prefix.
- Never store the plaintext anywhere except the customer's secret manager.
- Leaked keys: rotate within 1 hour of disclosure.
- Gitleaks catches commits that leak keys via the
hk_live_prefix pattern (see.gitleaks.toml). - Add more patterns to gitleaks as we introduce new token formats.
Testing against the API
Use the free tier (anonymous) for development. Free tier is designed to be enough for building + testing; upgrade to pro only when you actually need >60 req/min sustained.
# No auth needed
curl https://api.gohatch.fun/api/v1/leaderboards
For write-path tests (scoring, enrollment), use a pro key against our
staging API. Ping support@ for a time-boxed staging key.