collin/anvil
- don't expose a users email on their profile page
- don't include anvil before the breadcrumbs in the repo name i.e. anvil/collin/repo just do collin/repo
-
implement github action style CI feature (core done — see "Session notes" below; the "isolated workers" idea is the (c) sandboxed broker, still TODO)
- I want to get this to a sufficient state that we could bootstrap this app using this CI and automate deployments if we wished
- probably might want to have other isolated anvil workers or something running for CI jobs
- implement github pages style hosting feature
- security audit (started: see (b) threat-model below)
Session notes / resume point
Last updated: 2026-06-10. Working state is clean: cargo build, cargo clippy --workspace, cargo fmt --all, and cargo test --workspace all pass.
Everything below is UNCOMMITTED (repo convention: commit only when asked).
Two of your top-of-file items are quick wins we noticed but did NOT do yet:
- profile email —
user_profileincrates/anvil-web/src/ui.rsrendersowner.email; just drop that block. - breadcrumb
anvil/prefix —repo_indexheader in the same file starts witha href="/" { "anvil" } " / "; remove the leading anvil link.
Done this session
- CI UI — runs list
/{owner}/{repo}/ci, run-detail (status/timing/log), per-commit status badges, "CI" nav link. (crates/anvil-web/src/ui.rs) - CD redeploy webhook — on a green run of
[ci] deploy_branchin the single[ci] deploy_repo, POST to[ci] deploy_webhook(X-Anvil-Deploy-Secretheader). Scoped to ONE repo.CiConfigincrates/anvil-core/src/config.rs;deploy()incrates/anvil-ci/src/lib.rs. Docs:DEPLOY.md§7,deploy/anvil.toml.reqwestadded with NO TLS feature (keeps musl cross-compile aws-lc-free). - Docker socket on hagrid —
deploy/run.shmounts it +--group-adds the gid for the non-root user; caveat inDEPLOY.md§4. - Toasty ORM cleanup —
ci.rslist_by_repo/latest_for_commit/queued_idsnow sort/limit/filter in SQL, not in memory. Verified by the newordering_and_limit_run_in_the_databasetest. (Sweep: these were the only real instances;repos::list_all_with_ownersorts by a joined username and needs all rows — intentionally left.) - (a) CSRF + cookie hardening —
- Cookie:
HttpOnly+SameSite=Lax+Secure(auto viaConfig::secure_cookies()when base_url is https). - Synchronizer token
HMAC-SHA256(server_secret, session); secret persisted atdata_dir/csrf_secret(App::csrf_tokenincrates/anvil-core/src/lib.rs). Depshmac,sha2. Csrfextractor + constant-timeverify_csrf(crates/anvil-web/src/auth.rs). Hiddencsrffield + verification on add/delete SSH key, new repo, repo settings. Login exempt; logout relies on SameSite.- htmx insurance:
auth::csrf_contextmiddleware → request-scoped task-local;layoutsends the token viahx-headerson every htmx request.
- Cookie:
Next up (the agreed a/b/c plan — (a) done)
(b) untrusted-mode threat-model doc ← START HERE
Write docs/untrusted-mode.md (or SECURITY.md). Capture the severity-ranked
analysis:
- CI runner = root-equiv RCE via Docker socket — the hard blocker; fix is (c).
- Stored XSS if we ever serve raw blobs → separate origin +
text/plain+ CSP. - Git resource exhaustion (pack/decompression bombs, huge ref ads) → size/ storage quotas + timeouts.
- Open registration anti-abuse (email verify, rate limit, CAPTCHA, quotas, ban).
- Authorization granularity → collaborator roles + per-repo tokens / deploy keys.
- Webhook SSRF (when webhooks land) → block private IPs / metadata / localhost.
Already-right: private repos 404 (no leak), reserved usernames +
/-/, CI tar-upload (not bind-mount), single-repo CD gate, argon2, CSRF + Secure cookies. Recommendation to record: single-tenant/owner-operated stays the supported stance; untrusted is gated behind (c).
(c) sandboxed CI broker
Make anvil the only Docker client; the job container gets NO socket. Forbid bind
mounts; --cap-drop=ALL, --security-opt=no-new-privileges, read-only rootfs,
non-root uid, --pids-limit, mem/cpu caps, wall-clock timeout, egress limits,
image allowlist + CI-minute quotas. Stronger tier: gVisor/Kata/Firecracker
microVMs (this is the "isolated workers" idea from the list above). Current
runner: crates/anvil-ci/src/lib.rs::execute.
Loose ends
- CSRF header consumption:
hx-headerssends the token as acsrfheader, butverify_csrfonly reads the form field. When we add a tokenless htmx action (rawhx-post/hx-delete, no<form>), also read thecsrfheader. - Toasty migrations: schema only pushed on a fresh DB (
db::connect); new columns won't apply to an existing DB until migrations land. (Thedata_dir/csrf_secretfile is created automatically — no DB change.) - Suggested commits when ready: (1) CI UI, (2) CD webhook + deploy wiring,
(3) ci.rs ORM cleanup + test, (4) CSRF + cookies. Trailer:
Co-Authored-By: Claude ....
Remaining roadmap (plan milestones beyond a/b/c)
- Issues · 9. Pull requests (gix merge) · 10. Webhooks (mind the SSRF item) · github-pages-style static hosting (your list item).