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.
Docs: rewrite the README and organize the guides
520470e19cd906bd6fff987e2d132d793ce56486 · Keenan Nemetz · 16d ago · parent c5902451
Docs: rewrite the README and organize the guides Replace the long README with a landing page, split feature details into focused guides, and remove the roadmap and spike notes. Update comments that referenced them.
docs/issues.md +126
| @@ -0,0 +1,126 @@ | |||
| 1 | # Issues | ||
| 2 | |||
| 3 | Your issues belong to the repository, not to a vendor. They clone with the code, | ||
| 4 | keep working when a hosting service has an outage, and stay readable in any text | ||
| 5 | editor. | ||
| 6 | |||
| 7 | An issue is a Markdown file stored on a special branch called `vole`. Every clone | ||
| 8 | of the repository carries the issues and their history, so there is no separate | ||
| 9 | issue database to back up. | ||
| 10 | |||
| 11 | ## Where issues live | ||
| 12 | |||
| 13 | ```text | ||
| 14 | issues/<ulid>/issue.md # the issue | ||
| 15 | issues/<ulid>/comments/<ulid>.md # a comment | ||
| 16 | ``` | ||
| 17 | |||
| 18 | Here is a complete issue: | ||
| 19 | |||
| 20 | ```markdown | ||
| 21 | --- | ||
| 22 | title: "Nothing renders on the docs page" | ||
| 23 | state: open | ||
| 24 | author: you | ||
| 25 | created: 2026-10-22T21:30:00Z | ||
| 26 | labels: | ||
| 27 | - bug | ||
| 28 | --- | ||
| 29 | Steps to reproduce... | ||
| 30 | ``` | ||
| 31 | |||
| 32 | The top section, between the `---` lines, is YAML metadata. The rest is the | ||
| 33 | Markdown body. Titles and other free text are always quoted, so characters like | ||
| 34 | `#` or a leading `-` cannot change the meaning of the file. | ||
| 35 | |||
| 36 | An issue id is a 26-character ULID. It uses uppercase letters and digits, but not | ||
| 37 | `I`, `L`, `O`, or `U`. A UUID will not work. | ||
| 38 | |||
| 39 | ## Writing an issue without the CLI | ||
| 40 | |||
| 41 | ```sh | ||
| 42 | git switch --orphan vole | ||
| 43 | ID=01M5JR2JK29KKMAA1T9MDNTPBP | ||
| 44 | mkdir -p issues/$ID | ||
| 45 | cat > issues/$ID/issue.md <<'MD' | ||
| 46 | --- | ||
| 47 | title: "Nothing renders on the docs page" | ||
| 48 | state: open | ||
| 49 | author: you | ||
| 50 | created: 2026-10-22T21:30:00Z | ||
| 51 | labels: | ||
| 52 | - bug | ||
| 53 | --- | ||
| 54 | Steps to reproduce... | ||
| 55 | MD | ||
| 56 | git add -A | ||
| 57 | git commit -m "issue: docs page blank" | ||
| 58 | git push origin vole | ||
| 59 | ``` | ||
| 60 | |||
| 61 | Vole checks the file when you push. If someone else pushed first, the CLI puts | ||
| 62 | your change on top of theirs and tries again. | ||
| 63 | |||
| 64 | ## Using the CLI | ||
| 65 | |||
| 66 | ```sh | ||
| 67 | vole issue new --title "Nothing renders on the docs page" -m "Steps..." -l bug -a wilbur | ||
| 68 | vole issue comment -m "Also affects search" 01M5JR2JK29KKMAA1T9MDNTPBP | ||
| 69 | vole issue edit-comment 01M5JR2JK29KKMAA1T9MDNTPBP 01M5N1PRC4GJPZNEEG8PR8J4D6 -m "Corrected." | ||
| 70 | vole issue rm-comment 01M5JR2JK29KKMAA1T9MDNTPBP 01M5N1PRC4GJPZNEEG8PR8J4D6 | ||
| 71 | vole issue edit 01M5JR2JK29KKMAA1T9MDNTPBP --title "A better title" -l bug -l docs | ||
| 72 | vole issue assign 01M5JR2JK29KKMAA1T9MDNTPBP wilbur | ||
| 73 | vole issue unassign 01M5JR2JK29KKMAA1T9MDNTPBP wilbur | ||
| 74 | vole issue close 01M5JR2JK29KKMAA1T9MDNTPBP | ||
| 75 | vole issue reopen 01M5JR2JK29KKMAA1T9MDNTPBP | ||
| 76 | vole issue list --state open -l bug --assignee wilbur --format '%(shortid) %(title)' | ||
| 77 | vole issue show 01M5JR2JK29KKMAA1T9MDNTPBP # a prefix is enough | ||
| 78 | ``` | ||
| 79 | |||
| 80 | `--format` understands `%(id)`, `%(shortid)`, `%(state)`, `%(author)`, `%(title)`, | ||
| 81 | `%(labels)`, `%(assignees)`, and `%(created)`. | ||
| 82 | |||
| 83 | The CLI commits and pushes for you. It uses the account your SSH key belongs to, | ||
| 84 | not the name in `git config`. If you want to commit without pushing, add | ||
| 85 | `--no-push`. | ||
| 86 | |||
| 87 | You can also create an issue in a new directory with no remote configured: | ||
| 88 | |||
| 89 | ```sh | ||
| 90 | git init bug-report | ||
| 91 | cd bug-report | ||
| 92 | vole issue new --title "the docs page is blank" \ | ||
| 93 | --remote ssh://git@forge.example.com/demo.git | ||
| 94 | ``` | ||
| 95 | |||
| 96 | ## Rules | ||
| 97 | |||
| 98 | - Issues and comments live under `issues/` and `requests/` on the `vole` branch. | ||
| 99 | Other paths are rejected. Releases and CI records have their own paths, written | ||
| 100 | by Vole. | ||
| 101 | - History on the `vole` branch must be linear. Merge commits are not allowed. | ||
| 102 | - Force-pushing `vole` requires repository admin and is recorded in | ||
| 103 | `data/audit.log`. It is meant for redaction, not normal work. | ||
| 104 | - Writing `#<ulid>` in a body links to that issue. Commits that mention an id are | ||
| 105 | shown as backlinks on the issue page. | ||
| 106 | - An assignee must be a valid account name: lowercase letters, digits, `-`, and | ||
| 107 | `_`. The name is stored as written because account names are never reused. | ||
| 108 | - The web page can filter by state, label, and assignee, and search titles and | ||
| 109 | bodies. | ||
| 110 | |||
| 111 | ## Comments | ||
| 112 | |||
| 113 | A comment is a file at `issues/<id>/comments/<ulid>.md` with `author` and | ||
| 114 | `created` metadata. | ||
| 115 | |||
| 116 | Only the author of a comment can edit or delete it. Vole compares the author on | ||
| 117 | the old version of the file with the account behind the SSH key. An admin can | ||
| 118 | override this, for example to repair a thread after someone leaves, and the | ||
| 119 | override is recorded in `data/audit.log`. | ||
| 120 | |||
| 121 | Deleting a comment removes the file, but the text remains in Git history. Editing | ||
| 122 | a comment does not add an "edited" field; the commit carries the time. Deleting is | ||
| 123 | not redaction. Redaction means an admin force-push. | ||
| 124 | |||
| 125 | The issue itself works differently. Closing, relabeling, or assigning someone | ||
| 126 | else's issue is normal triage, not a claim of authorship. | ||