20/mofree tier uploads
5 MBmax body size
30 dayslink expiry
slug collision retry
01
1Authorize
Key + Quota
Check
The first 12 chars of your Bearer token are prefix-looked up, bcrypt-compared, and your upload quota checked — all before HTML is parsed.
🔑
Prefix Lookup
First 12 chars extracted from Authorization: Bearer header. Queried against api_keys where prefix matches and revoked_at IS NULL.
🔐
Bcrypt Compare
Full key is bcrypt-compared (12 rounds) against the stored hash. last_used_at updated fire-and-forget on match.
👤
Load User + Tier
User record fetched from users table. Tier (free/pro) determines rate limits and retention policy downstream.
passes to rate limit gate
⏱️
Rolling 30-Day Count
Non-deleted explainers created in the last 30 days are counted. Free tier hard-stops at 20 with 429 + upgrade_url.
Body limit runs first 5 MB enforced before auth — 413 before auth is attempted
JWT sessions accepted Dashboard users can also authenticate via session cookie
Token < 12 chars returns 401 Invalid API key format immediately
Revoked key prefix found but revoked_at IS NOT NULL → 401
02
2Process
Parse, Scan
& Store
The HTML body is parsed from multipart, run through a 5-pass security scanner that redacts threats inline, then uploaded to private R2 storage.
📋
Parse Multipart
Extracts file (required HTML), plus optional title, description, and comma-separated tags. Falls back to <title> and <meta> tags.
5-pass security scan
1
DOMPurify sanitize Strips script, link, meta, iframe, form + all event handler attrs (onerror, onclick…)
→ CLEAN
2
Link hardening All <a>/<area> get rel="nofollow noopener noreferrer" + target="_blank"
→ SAFE
3
Credential scan Shannon entropy ≥ 3.5 (4.5 inside <code>/<pre>) — AWS, GitHub, Stripe → [REDACTED]
→ REDACT
4
Prompt injection "" in HTML comments → stripped
→ STRIP
5
Exfil detection fetch()/sendBeacon in <script> unless calling explainers.fyi or fonts.googleapis.com
→ FLAG
attribution footer injected
Sanitize, don't reject Threats are redacted inline — uploads are never blocked by the scanner
response.sanitized: true if any content was removed or a credential was redacted
Footer baked at upload time Attribution injected into HTML before R2 storage — not at serve time
03
3Respond
LIVE
201 +
Shareable URL
A unique 10-char slug is generated, the explainer record written to PostgreSQL, and a 201 JSON response returns the URL, expiry, and sanitization flag.
🗄️
Upload to R2
PutObjectCommand → private bucket at {userId}/{slug}.html · IPv4-forced to avoid ENETUNREACH
🔀
Generate Slug
10-char lowercase alphanumeric nanoid · unique per user per upload
🔖
Insert DB Record
On PostgreSQL 23505 collision: rollback R2 object, retry new slug — up to 3 attempts
Return 201 JSON
{ url, slug, expires_at, sanitized } — link is live immediately after response
error handling
502 R2 upload fails — no DB record written, clean failure with error message
500 All 3 slug collision retries exhausted — client should retry the request
429 Rate limit hit — response includes limit, current count, window_days, and upgrade_url
key creation facts
xp_live_…key format
12 charsprefix stored plain
22 charsnanoid suffix
5 min TTLkey flash cookie
🔒 Key shown once— never retrievable after creation