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.
vibed/pages/docs/ARCHITECTURE.md

42 lines
2.2 KiB
Markdown

# pages — Architecture
Full design + security analysis: `plans/2026-07-12-pages-design.md`.
## Components
```
┌─────────────────────────────────────────┐
browser ──────────▶ │ Cloudflare Worker (Rust → wasm, axum) │
│ │
GET / │ routes/mod.rs router construction │
GET /auth/* │ routes/auth.rs GitHub OAuth, sessions │──▶ github.com (token exchange)
* /api/* │ routes/api.rs pages + users JSON API │──▶ D1 `DB` (users/sessions/pages)
GET /<slug>/* │ routes/serve.rs page content serving │──▶ R2 `PAGES` (<slug>/<path>)
│ │
│ core/ pure policy logic (native- │
│ tested): slug, mime, upload │
│ validation, security headers │
└─────────────────────────────────────────┘
```
## Component interactions
- **core/** has no Cloudflare dependencies; it compiles natively and holds
every security-relevant decision (slug grammar, reserved names, zip
limits, MIME allowlist, CSP header values). Unit tests live here.
- **db.rs** (wasm-only) wraps D1 queries; **routes/** (wasm-only) are thin
axum handlers that call core + db + R2.
- **auth.rs** builds OAuth URLs and cookie/session token logic (pure parts
native-tested); the GitHub HTTP calls are wasm-only.
- **static/index.html** is the whole management UI, embedded at compile time
with `include_str!` and served at `/`.
## Trust boundaries
1. **Uploaded content** is untrusted: validated in core/upload.rs, stored in
R2, and always served with a sandboxing CSP (opaque origin) so it can
never make credentialed calls to `/api`.
2. **Browser → API**: session cookie (`__Host-`, HttpOnly, SameSite=Lax,
hashed in D1) + Origin check on mutating routes.
3. **Worker → GitHub**: client secret only ever used server-side.