Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Phase 7 slice 1: review on the web, so approving does not start with a clone

78912785b00227cffe3f8fb26b057cd60159a6b4 · Keenan Nemetz · 17d ago · parent 9bea7f11

Phase 7 slice 1: review on the web, so approving does not start with a clone

Phase 7 is re-scoped: signing is deferred (the SSH handshake already establishes
who pushed, and there is no third-party verifier here), and the phase is now about
the experience of using the forge.

- GET /r/<repo>/commit/<sha>: a commit's metadata and the patch it introduced,
  per-file hunks with line numbers; the commit list links to it
- change requests show the same diff, computed three-dot: a review is what the
  branch adds since it diverged, not every commit main gained meanwhile
- diffs are capped at 512 KiB and labelled when cut, binary files are named
  rather than faked, and every line is escaped (a <script> line is a test)
- the repository README renders on its home page and markdown blobs render as
  markdown, through the sanitiser issues already use

docs/roadmap.md +56−15

@@ -179,7 +179,7 @@ Exit criteria: a user with an editor and a key files, comments on and closes an
179 179 issue without installing Vole; an invalid tree is rejected with the offending path
180 180 named and the branch unmoved; deleting the index loses no issues.
181 181
182 ### Phase 3 — Change requests — ✅ complete (review comments and diffs deferred)
182 ### Phase 3 — Change requests — ✅ complete (review comments deferred; diffs landed in Phase 7)
183 183
184 184 ✅ **Done.** A change request is a directory on `refs/heads/vole`:
185 185
@@ -386,20 +386,61 @@ filename is safe ✅ (and so is a traversal-shaped *request*). The former criter
386 386 are terminated in front of the instance, and a site is a subdomain of the domain that
387 387 certificate already covers.
388 388
389 ### Phase 7 — Signing and audit
390
391 Scope is provenance and tamper-evidence for a single instance — no peer trust, no federation.
392
393 - User ed25519 key upload; commit signature verification with a visible verified/unverified badge
394 - All metadata commits signed with the instance key; verification of instance-signed
395 metadata on read so a hand-edited ref is flagged
396 - Append-only instance audit log (auth events, key changes, ACL changes, repo deletion)
397 as a hash chain in a dedicated ref, signed
398 - `git verify-commit` interop: signatures produced must verify with stock git tooling
399
400 **Exit criteria:** a commit signed by a registered key shows verified; a metadata commit
401 altered out-of-band is flagged on read; the audit chain detects a deleted middle entry;
402 signatures verify with `git verify-commit` outside Vole.
389 ### Phase 7 — The forge experience
390
391 The system does the hard parts; this phase is about the ten minutes somebody spends
392 using it. Read-only web work first, because review is where a forge is judged and the
393 web is already the review surface.
394
395 ✅ **Slice 1 — review on the web.** `GET /r/<repo>/commit/<sha>` shows a commit's
396 metadata and the patch it introduced, with per-file hunks, line numbers and
397 added/removed rows; the commit list links to each one. A change request shows the same
398 diff, computed three-dot so a review is "what this branch adds since it diverged"
399 rather than every commit `main` gained meanwhile. Diffs are capped at 512 KiB and say
400 when they are cut, binary files are named rather than faked, and every line is escaped
401 — a diff is other people's content, and a test proves a `<script>` line does not
402 execute. A repository's README renders on its home page, and markdown blobs render as
403 markdown, through the same sanitiser issues use.
404
405 Planned:
406
407 - Identity without ceremony: `vole whoami` resolves the key a person pushes with to an
408 account (the roster is readable over SSH at `system.git`), and `vole request approve`
409 stops requiring `--as`. The name someone types has to match their key anyway, so
410 asking for it is a trap rather than a check.
411 - Filing an issue or opening a request without a clone (`--remote ssh://…`), because
412 "clone the repository to report a bug" is the roughest edge in the current flow.
413 - Navigation and empty states: repo tabs with counts, breadcrumbs everywhere, and an
414 empty state that prints the exact command that fills it.
415 - Per-file history (`/commits/<ref>/<path>`), so "when did this change" is one click.
416
417 **Exit criteria:** a reviewer can approve a change using only the web — diff, checks and
418 approvals on one page — without cloning; a new user can go from `vole init` to a pushed
419 commit following the README alone; every page has a working path to its neighbours.
420
421 ### Deferred — signing and audit
422
423 Dropped from the active roadmap by decision: an instance owns its own keys and its own
424 ACL, so a signature over an approval mostly restates what the SSH handshake already
425 established. Revisit when an instance is mirrored somewhere its operator does not
426 control, or when a third party must verify approvals without trusting the instance.
427
428 Measured findings, so the next session does not redo the experiment (git 2.53,
429 OpenSSH 10.2):
430
431 - `git commit -S` and `git commit-tree -S` both sign with `gpg.format=ssh`, so the
432 roster's keys are the signers and `vole request approve` could sign with one flag.
433 - `git verify-commit` against a roster-derived `allowed_signers` accepts any key in the
434 file and does **not** tie the signature to the committer email; identity comes from
435 `ssh-keygen -Y find-principals`, not from the commit.
436 - A signature covers a commit, not a file, so the rule would have to be "the commit
437 that last touched `approvals/<account>.json` is signed by that account" — plus a
438 format-version branch to grandfather existing approvals and mandatory signing
439 configuration for every approver.
440
441 If this returns, the cheaper shape is instance-signed push attestations plus a
442 `vole verify` that replays the tree-checkable rules from a clone — not mandatory user
443 signatures.
403 444
404 445 ### Phase 8 — Operate it
405 446