You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
75 lines
2.6 KiB
Markdown
75 lines
2.6 KiB
Markdown
# 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 `.html` file or a `.zip` of static
|
|
assets (drag & drop), pick a name, and the content is served at
|
|
`pages.elijah.run/<name>/`.
|
|
- Update a page in place: **Update** on any page you own re-uploads its
|
|
content under the same name (the URL never changes, and files the new
|
|
upload doesn't include are removed).
|
|
- Admins approve which users may upload, and can delete or update 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/update/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
|
|
|
|
```sh
|
|
# 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
|
|
|
|
```sh
|
|
# 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](../LICENSE-APACHE) and
|
|
[MIT](../LICENSE-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.
|