anvilsign in

collin/anvil

BoardRenderedSource

Todo2 open

  1. ability to link to deployed / live site
  2. agent view, we have list of repos what about list of agents

Backlog14 open

  1. finish the CI pipeline move to TOML: rename .anvil/ci.yml to

    .anvil/ci.toml (and convert it) in every repo on the instance, then delete ci::LEGACY_PIPELINE_PATH and the legacy branch of load_pipeline / enqueue_ci_for_push. Until then a stale .anvil/ci.yml still enqueues a run, which fails telling you to rename the file — the point being that CI going quiet is louder than CI going missing.

  2. 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 write main
    • 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_concurrent bounds 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)
  3. pull requests (gix merge)
  4. pull mirror (maybe): a repo that virtually mirrors a GitHub repo
    • just displays it here — periodically fetched, read-only on the anvil side
  5. richer file editing: a real markdown editor with a live render preview

    (reuse render_markdown) before committing

  6. webhooks (mind the SSRF item in docs/untrusted-mode.md)
  7. 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

  8. admin usage: per-repo drill-down, and a cheap cached/periodic variant if

    the on-demand disk walk gets slow on large instances

  9. 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

  10. 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

  11. API tokens: a write scope (would need CSRF-exempt write paths) and

    last_used_at tracking

  12. single sign-on follow-ups (docs/oidc.md): silent renewal

    (prompt=none on a short local session, which is what makes revoking an SSO session propagate here), an admin view of who is linked to which sub, and unlinking an account from the settings page

  13. secrets follow-ups (docs/secrets.md): authenticate anvild secret with an

    ssh signature instead of the account password; per-step rather than per- pipeline scoping; ssh-rsa recipients (needs an RSA-OAEP branch in both the Rust and the browser halves)

  14. 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 new PeriodicJob, 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
    • 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 .db under a rollback journal can yield a corrupt file, which makes the documented backup (tar the running volume, DEPLOY.md § Operations) unsound. PRAGMA journal_mode=WAL plus busy_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, builds edits, then calls edit_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.