id: 90121d61-86ed-48ff-8c57-b04fb8a3ce5d title: 'Cloudflare: implement Pages Functions + R2 inbox (current deploy has no inbox)' state: closed labels:
- area:inbox assignees: [] milestone: null created: 2026-10-27T17:24:52.330249308Z updated: 2026-10-27T17:45:41.613480095Z
What
The Cloudflare target is still incomplete: the repo has no functions/, no R2 code and no Pages deploy path. dist/_headers is emitted, but on Pages the forms POST to /inbox/issue and /inbox/pr-prompt with no Function behind them, so the inbox is dead. Docker/hu13 serve is the only complete target today.
Required parity
Implement Pages Functions for both endpoints with the existing inbox contract:
- urlencoded only (
application/x-www-form-urlencoded; reject multipart/JSON), 32 KiB whole-request cap, strict UTF-8, NUL → 400 - strip controls; title ≤300, body/prompt ≤20000; license required; honeypot fake-200 without storing
- same-origin Origin gate, ~500 ms fixed delay, no echo,
X-Content-Type-Options: nosniff+Cache-Control: no-store - ack uuid exactly once
- R2 object per entry with the schema:
type/title/body/handle/contact/license_version/received_at/salted_ip_hash - per-IP rate limit and global quota (or document a verified Cloudflare-native equivalent, e.g. WAF/rate rules + R2 quota check) with 429/503
Deliverables
functions/inbox/issue.js+functions/inbox/pr-prompt.js(or one shared handler)- R2 binding docs (
INBOX,INBOX_SALTsecret) andwrangler/Pages deploy recipe;_routes.jsonlimited to/inbox/* - Node tests for validation/ack/R2 writes using a fake bucket (no network)
- update
.forge/wiki/deployto describe the Pages path honestly (what works, what needs dashboard config) - clone URL: document
--public-originso the user-facinggit clone <origin>/repo.gitis real on Pages
Acceptance: local Node tests pass; the function code is deployable with a documented binding; docs no longer imply the static-only deploy has a working inbox.