Layers
| Layer | What it does |
|---|---|
src/ | Nunjucks, HTML, CSS, and JavaScript source for resident and admin pages |
dist/ | Eleventy build output deployed by Cloudflare Pages |
functions/ | Pages Functions for subscriptions, feeds, renewal, webhooks, cron, and admin APIs |
SUBS_DB | D1 subscription, event, email, template, and access data |
CAL_BUCKET | R2 object storage for programs.ics |
The top-level Pages middleware allows known pages, APIs, calendar paths, admin paths, and static assets. Unknown paths temporarily redirect to /subscribe, and invalid calendar_id values are stripped.
Programs calendar
The Programs service has two separate paths:
- a producer that reads selected Notion events every 15 minutes and writes deterministic ICS bytes to R2;
- a consumer that validates a private token hash and serves those bytes to a calendar app.
Keeping those paths separate means a temporary Notion problem does not stop the last successfully generated feed from being served.
Token and renewal boundary
Feed tokens are shown once to the resident and stored only as SHA-256 hashes. Renewal email does not require recovering that token: it uses a different HMAC-signed credential containing the token-row id and an expiry. The renewal credential can extend a row but cannot read the feed.
Admin boundary
flowchart TD REQUEST["Request under /api/admin/"] --> ID["Verify Cloudflare Access JWT"] ID -->|invalid| DENY["Deny"] ID --> ACTION["Map method + route to action"] ACTION -->|unmapped| DENY ACTION --> PDP["Ask RC Console policy service"] PDP -->|explicit allow| HANDLER["Run the endpoint"] PDP -->|deny, error, or timeout| DENY
The outer Cloudflare Access policy and the in-code JWT verification establish identity. The RC Console response establishes action-level authorization. Neither is treated as a substitute for the other.
Internal calendar
The legacy Internal calendar remains separate. Its subscription request and ICS route proxy to n8n and rely on their internal access boundary. This path does not generate the public Programs feed.
Failure behavior
- Missing or invalid feed tokens return non-revealing responses.
- Event regeneration skips the R2 write when bytes did not change.
- Event logging failures do not fail resident requests.
- Admin policy-service failures deny the admin action.
- Renewal and webhook paths use idempotency keys where retries are expected.