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> |
1 week ago | |
|---|---|---|
| .. | ||
| .beans | 1 week ago | |
| docs | 1 week ago | |
| infra | 1 week ago | |
| migrations | 1 week ago | |
| src | 1 week ago | |
| static | 1 week ago | |
| .beans.yml | 1 week ago | |
| .dev.vars.example | 1 week ago | |
| .gitignore | 1 week ago | |
| CLAUDE.md | 1 week ago | |
| Cargo.lock | 1 week ago | |
| Cargo.toml | 1 week ago | |
| README.md | 1 week ago | |
| justfile | 1 week ago | |
| wrangler.toml | 1 week ago | |
README.md
pages
A micro static-site host at pages.elijah.run, running entirely on
Cloudflare (Workers + R2 + D1) with the Worker written in Rust compiled to
WebAssembly.
What it does
- Browse all live pages at
pages.elijah.run. - Log in with GitHub, upload a single
.htmlfile or a.zipof static assets (drag & drop), pick a name, and the content is served atpages.elijah.run/<name>/. - Admins approve which users may upload, and can delete any page.
Hosted pages are intended to be client-side-only single-page apps. They
are served with a sandboxing Content-Security-Policy (opaque origin): no
cookies, no localStorage — see docs/plans/2026-07-12-pages-design.md §2.1.
An admin can mark a specific page, or all of a user's pages, as trusted
to opt them out of that sandbox (e.g. for local-first apps that need
localStorage) — see the "Trust escape hatch" addendum in §2.1 for the
exact semantics and the security trade-off that comes with it.
How it works
One Cloudflare Worker routes everything:
/— embedded management UI (list, login, upload, admin)/auth/*— GitHub OAuth + session cookies (sessions in D1, hashed)/api/*— JSON API (list/upload/delete pages, manage users)/<slug>/*— page content served from R2 with strict security headers
Uploads are validated hard: strict slug grammar + reserved names, zip path-traversal rejection, decompression limits (10 MiB compressed, 25 MiB uncompressed, 300 files). See the design doc for the full security analysis.
Run locally
# from pages/
cp .dev.vars.example .dev.vars # fill in a GitHub OAuth app's id/secret
wrangler d1 execute pages --local --file migrations/schema.sql
wrangler dev # builds via worker-build and serves locally
Test
# from pages/
cargo fmt
cargo check --target wasm32-unknown-unknown
cargo clippy --all-targets -- -D warnings
cargo test
Deploy
Infrastructure (R2 bucket, D1 database, DNS, rate limits) is OpenTofu in
infra/; the Worker itself deploys with wrangler deploy (see
infra/README.md). Secrets via wrangler secret put GITHUB_CLIENT_SECRET.
License
Dual-licensed under Apache-2.0 and MIT, like the rest of this repository.
Disclaimer
This software was written with Claude Code. Design, review and orchestration
by Claude Fable 5 (claude-fable-5); implementation by Claude Sonnet
agents with Claude Opus review agents.