A page keeps one canonical slug and can have up to 10 long-lived,
removable aliases in the same URL namespace. Serving is serve-direct:
routes::serve falls back to Db::get_page_by_alias on a canonical miss
and serves the canonical page's R2 content under its <slug>/ prefix — no
redirect, no second copy, and a real page always wins over an alias of
the same name.
- migrations: new `aliases` table + idx_aliases_slug (schema.sql +
idempotent 0003-aliases.sql).
- db.rs: get_page_by_alias, insert_alias (NameTaken on collision),
delete_alias (page-scoped), list/count/delete-for-page, alias_exists.
- api.rs: POST /api/pages/:slug/aliases (can_upload + owner-or-admin) and
DELETE /api/pages/:slug/aliases/:alias (owner-or-admin); create_page
rejects a name held by an alias; delete_page removes a page's aliases;
GET /api/pages returns each page's aliases[].
- Namespace uniqueness across pages.slug + aliases.alias is enforced in
the handlers (SQLite can't express the cross-table rule).
- Frontend: alias chips (× to remove) + an "Add alias" button.
Replaces the earlier rename+conditional-redirect idea (dropped for
301-cache fragility). No R2 or behavioral change to existing pages.
196 unit + 20 doctests, both-target clippy clean.
Bean: pages-9lsk.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Micro static-site host running entirely on Cloudflare: a Rust->wasm
Worker (workers-rs 0.7 + axum 0.8) serving user-uploaded .html/.zip
content from R2 at pages.elijah.run/<slug>/, with D1 for users/
sessions/page metadata and GitHub OAuth for login. Admins approve
uploaders, delete pages, and can mark pages/users trusted.
Security design (docs/plans/2026-07-12-pages-design.md): hosted
content is served with a CSP sandbox (opaque origin — no cookies or
storage, cannot make credentialed /api calls) unless admin-trusted;
strict slug grammar + reserved names; streaming zip validation
(traversal, bombs, size/entry/depth limits); SHA-256-hashed session
tokens in __Host- cookies; Origin checks on all mutations; upload
quotas and size caps.
Includes OpenTofu infra (R2, D1, DNS; wrangler owns the Worker
deploy), an embedded single-file management UI, deployment guide,
and 148 native unit tests + 18 doctests (clippy -D warnings clean on
native and wasm32). Deployed and verified in production.
Squash of 15 commits from pages-v0: each ticket implemented by a
Claude Sonnet agent, reviewed by a Claude Opus agent, tracked with
beans (pages-lxjz..pages-shqc).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>