collin/anvil
Todo2 open
- ability to link to deployed / live site
- agent view, we have list of repos what about list of agents
Backlog13 open
agent sessions, next milestones (docs/agent-sessions.md):
- a real checkout: the container clones from anvil's smart-HTTP endpoint and
pushes
agent/<id>back. Needs a session-scoped push credential, which does not exist (tokens are read-only, Bearer only on GET/HEAD) - ref-scope that credential to
refs/heads/agent/*— needs a ref filter in receive-pack. Until it lands a session credential could writemain - trigger surfaces: a start button on a TODO item, an issue, a red CI run
- rate limiting, so automated pushes can't queue sessions endlessly once
triggers exist (
max_concurrentbounds concurrency, not churn) - a finished session's transcript rendered on its page (it is already on
disk under
sessions/<id>.log; nothing reads it back yet)
- a real checkout: the container clones from anvil's smart-HTTP endpoint and
pushes
- pull requests (gix merge)
pull mirror (maybe): a repo that virtually mirrors a GitHub repo
- just displays it here — periodically fetched, read-only on the anvil side
richer file editing: a real markdown editor with a live render preview
(reuse
render_markdown) before committing- webhooks (mind the SSRF item in
docs/untrusted-mode.md) attachment reclaim: an orphan sweep (delete attachments no committed file
references) and/or a per-attachment delete action — the recourse once a repo hits its quota. Deferred: deletion is destructive and "orphaned" is fuzzy (tip-only vs any-ref), so it wants its own design pass
admin usage: per-repo drill-down, and a cheap cached/periodic variant if
the on-demand disk walk gets slow on large instances
periodic disk usage cache: run
usage::compute()on a timer (e.g., hourly)and store the result so the admin dashboard doesn't block on disk walks
repository preview images: extract the first "real" image (>few hundred px)
from README.md on a periodic scan, cache the attachment hash, and display in repo listings for visual browsing
API tokens: a
writescope (would need CSRF-exempt write paths) andlast_used_attrackingsingle sign-on follow-ups (docs/oidc.md): silent renewal
(
prompt=noneon a short local session, which is what makes revoking an SSO session propagate here), an admin view of who is linked to whichsub, and unlinking an account from the settings pagesecrets follow-ups (docs/secrets.md): authenticate
anvild secretwith anssh signature instead of the account password; per-step rather than per- pipeline scoping;
ssh-rsarecipients (needs an RSA-OAEP branch in both the Rust and the browser halves)Ideas from Origin https://cursor.com/blog/git-at-any-scale
Cursor's writeup of Continuity, their Spokes replacement. Most of the post is scale machinery anvil does not need (replicas, consensus, rendezvous hashing, S3 as the source of truth) because that exists to serve a monorepo's CI from a hundred read replicas. Three things do transfer, ranked by value per line.
-
packfile compaction. anvil has none at all. Every push writes a new pack via
gix_pack::Bundle::write_to_directory(vendor/gitserver-core/src/receive_pack.rs,write_pack) and nothing ever consolidates them. Object lookup is O(packs) because each index is only efficient per-pack, so every push makes every later browse, clone and CI checkout slower, permanently. The periodic runner already exists (crates/anvil-core/src/periodic.rs), so this is a newPeriodicJob, not new infrastructure. Two levels:- multi-pack-index via
gix_pack::multi_index::File::write_from_index_paths(pure gix, no CLI). One binary-searchable lookup across all packs. Start here: small, self-contained, fixes a problem already accumulating - geometric repack via
gix_pack::data::output, the same machinery upload-pack uses to build packs. More work. The post's warning about repacking being an availability hazard is a replica problem; with one node there is nothing to fail over
- multi-pack-index via
-
SQLite is not in WAL mode.
db::connect(crates/anvil-core/src/db.rs) sets no pragmas, so it runs on the default rollback journal with web, ssh, the CI dispatcher and four periodic jobs all against one file in one process. Readers block the writer, and copying a live.dbunder a rollback journal can yield a corrupt file, which makes the documented backup (tar the running volume, DEPLOY.md § Operations) unsound.PRAGMA journal_mode=WALplusbusy_timeout. -
a push log (the WAL idea, minus S3, consensus and replicas). What transfers is the observation that the pack bytes plus the ref transaction are a complete description of a push, so recording them stops the disk from being precious. Both are already in scope at one place:
apply_commands(vendor/gitserver-core/src/receive_pack.rs) writes the pack, buildsedits, then callsedit_references. The entry goes between those two steps.- buys, in order of how much we would use it: force-push undo as a UI button (every ref's prior value is recorded), a reflog that survives gc, rebuildable repos, and eventually continuous off-box backup
- keep it simple by ordering it right: a local append-only file first. No S3 client, no dependency, no network in the push path. That alone gets undo and provenance. Shipping entries off-box is a separate additive step
- the rule that makes it worth anything, and the easy one to skip: do not ack the push until the entry is durable. A log that might be missing the entry you need is worse than no log, because you will trust it
- caveat: this covers git only. Issues, users, CI runs, secrets, attachments and artifacts are not in it. Build this and keep tarring the volume for the rest and we have added a system without retiring one, so either frame it as provenance plus undo (a feature) rather than backup (an ops story), or pair it with continuous SQLite replication so both halves match.
Related, prompted by the post rather than in it:
App(crates/anvil-core/src/lib.rs) mixes durable state (db, disk) with process-local state (ci_tx,vault,user_vault,sessions,jobs) with nothing marking which is which. Each in-memory field is documented as "lost on restart, which is safe because...", which is correct, but the invariant lives in comments. The discipline underneath Continuity is knowing exactly what is truth and what is cache. Costs nothing at one process; first thing to break at two.-