Developer guide
Security
The security measures built into the site — headers, input limits, the API proxy allowlist and protected endpoints.
The site is public and has no user accounts, so its security work focuses on safe defaults, limits on anything that costs money (AI calls, KV reads), and not becoming a relay for attacks.
Response headers
Set on every response — by src/hooks.server.ts for Worker responses and by _headers for static
assets (Cloudflare Pages doesn't apply _headers to Worker responses):
| Header | Value / purpose |
|---|---|
Strict-Transport-Security |
HTTPS only, one year, including subdomains |
X-Content-Type-Options |
nosniff |
X-Frame-Options and CSP frame-ancestors 'self' |
Other sites can't embed the site in a frame |
CSP base-uri 'self'; object-src 'none' |
Blocks <base> hijacking and plugins |
Referrer-Policy |
strict-origin-when-cross-origin |
Permissions-Policy |
Camera, microphone, geolocation, payment and USB disabled |
Cross-Origin-Opener-Policy |
same-origin |
Protections per endpoint
flowchart TB
subgraph AI["POST /api/ai"]
A0["Same-origin browser requests only"] --> A1["Body at most 1 MB"]
A1 --> A2["YAML at most 512 KiB, field at most 1,024 chars, at most 100 issues"]
A2 --> A3["Action allowlist, validate and compare disabled"]
A3 --> A4["Release names resolved from a fixed list, path segments validated"]
A4 --> A5["Fixes verified before they are returned or cached"]
end
subgraph CS["GET /api/cache-status"]
C1["Per-CRD lookups: a few KV reads"] --> C2["Full summary requires a bearer token"]
end
- AI cost control. Size limits stop a single request from sending a huge prompt, LLM actions without a UI are disabled, and other sites' pages can't call the endpoint. Scripts can still call it directly, so add a Cloudflare rate-limiting rule (below).
- Shared cache integrity. AI fixes are cached under a hash of the full prompt and only after server-side verification, so one client can't plant a bad fix that others receive.
- No outbound proxy. The site has no endpoint that forwards requests to other hosts, so it can't be used to reach internal addresses (an earlier unused proxy route was removed).
- Cache summary.
?summary=truescans every CRD of a release (hundreds of KV reads). It is disabled unless theCACHE_STATUS_TOKENsecret is set, and then requiresAuthorization: Bearer ….
Rate limiting /api/ai
Rate limiting is configured at the Cloudflare edge, not in code. In the dashboard: your domain or the Pages project → Security → WAF → Rate limiting rules → Create rule:
| Setting | Value |
|---|---|
| If incoming requests match | URI Path equals /api/ai and Request Method equals POST |
| With the same characteristics | IP |
| When rate exceeds | 20 requests per 1 minute |
| Then | Block for 1 minute (returns 429; the page shows "Too many AI requests") |
A bulk AI Fix on a large bundle makes a request per remaining issue, so keep the limit above ~15 per
minute for real users. Rate limiting rules apply to custom domains on a Cloudflare zone; a
*.pages.dev hostname alone can't have WAF rules.
Rendering untrusted text
- Search highlighting and the markdown renderer for AI text escape HTML before adding their own markup.
- That renderer only turns
http(s)URLs and site-relative paths into links; protocol-relative//hostlinks are not treated as internal. - Regex search is bounded (pattern length, scanned text, iterations) to avoid slow patterns.
- Mermaid diagrams in these docs render with
securityLevel: 'strict'.
Secrets
- Tokens are passed through environment variables (
CLOUDFLARE_API_TOKEN) or Cloudflare secrets, never committed..env*and.dev.varsare git-ignored. - Rotate any token that was ever pasted into a chat, ticket or log.
Dependencies and runtime
- Node.js is pinned to a release that includes the latest security fixes (see Build & deploy).
npm auditandpnpm auditshould report no vulnerabilities before a deploy.