Files
kennethreitzandClaude Fable 5 d3e1b62960 Step up to Responder 8.2.1 — typed params, rate limits, and a 304 for the encore
The shell picks up what the new framework offers: validated query
params (limit=abc is a 422 now, not a 500), a 512KB body cap ahead of
the JSON parser, per-IP rate limits on the PIL-rendering og card and
the editor endpoints, security headers and auto-ETags on everything,
a 60s request budget, orjson under the JSON, and lifespan instead of
on_event. Handlers return their responses now instead of mutating
resp — except where RateLimiter.limit would swallow the return value
(upstream bug; check() is called in-handler instead). Plus a web-layer
test suite and a verify skill, and the README finally serves granian.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:50:46 -04:00

1.8 KiB
Raw Permalink Blame History

name, description
name description
verify Launch and drive rhymepad.org end-to-end to verify a change against the real HTTP surface.

Verifying rhymepad.org

The surface is HTTP: a Responder app (app:api) served by granian.

Launch

uv run granian --interface asgi --host 127.0.0.1 --port 8791 app:api

Run it in the background. Boot includes rhymes.warm() (g2p model + NLTK data) via the lifespan hook — poll /healthz for up to ~2 min before declaring it dead. NLTK data missing locally? make nltk-data.

Flows worth driving

  • GET /healthz → {"ok": true} — also carries the global headers (security headers, ETag), so it doubles as a header check.
  • POST /api/analyze with {"text": "cat in a hat\nbat on a mat"} → tokens/groups JSON. Body > 512 KB → 413 (framework cap); text > rhymes.MAX_DRAFT (100k chars) → 413 with detail.
  • GET /api/lookup?word=cat&limit=5 — limit is a validated Query param: non-int or outside 1–500 → 422 problem-details JSON.
  • GET /api/og?d=<payload> → PNG + immutable Cache-Control. Build a payload: base64.urlsafe_b64encode(gzip.compress(json.dumps({"x": "..."}).encode())), strip =. Garbage d → 404 "bad draft link". Rate-limited 30/min per client IP (X-Forwarded-For when present) — 31 rapid hits show the 429.
  • GET /?d=<payload> → title swapped into <title>… · RhymePad</title> plus noindex robots meta.
  • Conditional GET: take any ETag, re-request with If-None-Match → 304.

Gotchas

  • Shell is fish; multi-line loops in Bash tool calls need bash -c.
  • The rate limiter's memory backend persists per-process — restarting granian resets budgets; repeated verify runs against one process eat the og budget (send a fresh X-Forwarded-For to get a clean one).