Authentication
Everything under /api/* requires a Bearer token, with two exceptions:
POST /api/waitlist (the landing page form) and GET /_debug/:slug, which are
public by design.
curl -s https://301.so/api/links \ -H "Authorization: Bearer $TOKEN" \ -A "my-client/1.0"Without it:
{ "error": "unauthorized" }…with HTTP 401. The same 401 covers a missing header, a malformed one, an
unknown token and a revoked one — an error that distinguishes those is an
error that helps whoever is guessing.
Minting a token
Section titled “Minting a token”Tokens are created from the repo, not from the API — there is no endpoint that creates credentials:
cd apps/workernode scripts/mint-token.mjs "my laptop"The token is printed once. The database only ever stores its SHA-256, so a lost token cannot be recovered, only replaced. Lookup happens by hash through SQLite’s unique index: no plaintext secret is ever compared, and the response time has nothing to teach an attacker.
A token belongs to a team, and every API query is constrained to that team.
Today there is exactly one team, one domain and one user — but the schema has
carried team_id since the first commit, because retrofitting it onto a live
database is the migration you never get to do.
Each successful call updates last_used_at, best-effort: if that write fails,
your request still succeeds. Knowing a token is still in use is worth less than
serving the request.
The 1010 trap
Section titled “The 1010 trap”The reference client already does this — see Clients.