cd /news/developer-tools/show-hn-beaver-backlog-an-issue-trac… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-92547] src=github.com β†— pub= topic=developer-tools verified=true sentiment=Β· neutral

Show HN: Beaver Backlog, an issue tracker that lives in your repo

Beaver Backlog, a local-first issue tracker that stores issues as Markdown files inside a project's repository, has been released as an open-source tool by developer Stefan (builtbystef). The tool requires Go 1.26 or later and can be installed via `go install github.com/builtbystef/beaver-backlog/cmd/beaver@latest`. It provides a CLI and a web UI for managing issues without an external service, account, or database, and is designed to be agent-friendly for coding agents.

read9 min views1 publishedAug 11, 2026
Show HN: Beaver Backlog, an issue tracker that lives in your repo
Image: source

A local-first issue tracker that stores issues as Markdown files inside your project.

Humans and coding agents coordinate work through the files themselves.

No external service, account, or database needed.

Install Β· Quick start Β· Web UI Β· Screenshots Β· Commands Β· Agents Β· Docs

$ beaver create "Login form rejects valid passwords" --label bug --priority high
Created ix2guj  Login form rejects valid passwords
  .beaver/issues/ix2guj-login-form-rejects-valid-passwords.md

Most issue trackers live outside the codebase, behind a web app and an API. Beaver Backlog keeps project work in the repository, as plain files that travel with your code:

Markdown-first: every issue is a human-readable.md

file with a small YAML header. Read it, edit it, diff it, and review it like any other file.Local by default: issues live on your disk, in your project.** Version-control-friendly**: plain text that Git diffs and merges cleanly.** Agent-friendly**: coding agents read, create, and update issues through the same files as you.** Nothing hidden**: the files are the only source of truth. The CLI and the web UI are thin clients over them; hand-editing an issue file is a first-class operation.

With Go 1.26 or later:

go install github.com/builtbystef/beaver-backlog/cmd/beaver@latest

Or build from a clone:

git clone https://github.com/builtbystef/beaver-backlog.git
cd beaver-backlog
go build ./cmd/beaver
bash
$ beaver init
Initialized empty Beaver Backlog store in /home/you/project/.beaver

$ beaver create "Login form rejects valid passwords" --label bug --priority high
Created ix2guj  Login form rejects valid passwords
  .beaver/issues/ix2guj-login-form-rejects-valid-passwords.md

$ beaver list
ID      PRIORITY  STATE  ASSIGNEE  LABELS  TITLE
ix2guj  high      todo   -         bug     Login form rejects valid passwords

$ beaver start ix2guj
Started ix2guj (claimed for stefan)

$ beaver note ix2guj "root cause: form strips ! before hashing"
Added note to ix2guj as stefan

$ beaver update ix2guj --priority urgent --label regression
Updated ix2guj

$ beaver done ix2guj
Marked ix2guj done

State changes are verbs of their own (start

, done

, cancel

, reopen

); every other field β€” title, description, assignee, priority, labels, relationships β€” changes through beaver update

.

beaver serve

starts a local web UI on loopback over the same files. It uses port 2328 by default and scans forward if that port is occupied (override it with --port

). No daemon and no build step; stop it with Ctrl-C.

Boardβ€” issues as cards in state columns; drag a card to move it.** List**β€” the same issues as a table, sharing one filter bar with the board (label, priority, assignee, text search).** Graph**β€” the dependency graph as a server-rendered picture: layered layout, parent clusters, dependency arrows; pan, zoom, and filter it.Issue pagesβ€” rendered Markdown descriptions and notes, every field editable, plus creating new issues in the browser.** Doctor**β€” store health as a page, with the same safe repair asdoctor --fix

.

Open pages notice when the store changes underneath them β€” a pull, a hand edit, another actor β€” and redraw themselves. Every control is a plain HTML form first, so the UI keeps working with JavaScript disabled; scripts only add polish on top. The UI follows your system's light or dark theme, and writes are attributed just like CLI writes (beaver serve --as <actor>

).

The board The graph
The issue list
An issue page
Dark mode: board
Dark mode: graph

Each issue is one file in .beaver/issues/

, named <id>-<slug>.md

for readability. The short random ID is the identity; the slug just mirrors the title.

---
id: ix2guj
title: Login form rejects valid passwords
state: done
assignee: stefan
priority: high
labels:
  - bug
created: 2026-07-06T20:28:40Z
updated: 2026-07-06T20:28:59Z
---

When a user submits a correct password containing a `!`, the form clears and
shows "invalid credentials". Expected: the login succeeds.

## Notes

**stefan** β€” 2026-07-06T20:28:59Z

root cause: form strips ! before hashing

The frontmatter is machine-owned (Beaver Backlog keeps it formatted, and unknown keys you add by hand are preserved verbatim, never interpreted); the issue body is yours (Beaver Backlog only ever appends notes to it). State is one of todo

, in-progress

, done

, or cancelled

β€” cancelled meaning deliberately abandoned, kept visible so nobody re-files it. Any state may move to any other; the tracker records reality rather than enforcing a workflow.

Fourteen of them, and they fit on one screen:

Command What it does
beaver init
Initialize a store in the current project
beaver create "<title>"
Create an issue (--body , --body-file , --label , --priority , --depends-on , --parent )
beaver list
List issues (--state , --ready , --blocked , --label , --priority , --assignee , --parent , --search )
beaver show <ref>
Show an issue, including what it waits on and whether it is ready
beaver start <ref>
Move to in-progress, auto-claiming if unowned
beaver done <ref>
Mark done
beaver cancel <ref>
Deliberately abandon (terminal, but not completed)
beaver reopen <ref>
Return an issue to todo, clearing its assignee
beaver update <ref>
Change any non-state field (see below)
beaver note <ref> "<text>"
Append an attributed, timestamped note
beaver delete <ref>
Delete the file (for junk; the VCS keeps history)
beaver doctor
Check store health; --fix repairs what is safe to repair
beaver serve
Serve the local web UI on loopback until interrupted (--port , --as )
beaver whoami
Print the actor you resolve as

update

takes as many fields as you like in one invocation, and writes nothing at all if they net out to no change:

Flag What it changes
--title <text>
The title, renaming the file to the fresh slug (the ID is fixed)
--body <text> / --body-file <path>
The description, leaving the ## Notes section untouched
--assignee <actor> / --unassign
The assignee
--priority <level>
Priority (urgent –low , or none to clear)
--label <spec>
Labels: bug or +bug adds, -bug removes; repeatable, CSV
--depends-on <spec>
Blocking edges, by ref, with the same + /- syntax
--parent <ref> / --no-parent
The parent issue

A <ref>

is an issue's ID, its slug, or its file name β€” resolved by exact match only, never by prefix or fuzzy match. Run beaver help

for full usage.

Output format auto-detects: human-readable tables on a terminal, JSON when piped (override with --format human|json

). Exit codes are stable β€” 0

success, 1

runtime failure, 2

usage error, 3

issue or store not found.

Every mutation is attributed to an actor β€” a free-form name; humans and agents are treated identically. Identity resolves from --as

, then the BEAVER_BACKLOG_ACTOR

environment variable, then per-machine user config (a human is prompted once, in a terminal). Set BEAVER_BACKLOG_ACTOR

in an agent's environment and every claim and note it makes is attributed correctly.

A complete issue β€” title, description, and metadata β€” is one command: pass a short description inline with --body

, or pipe multi-line Markdown through --body-file -

(a path works too) and skip the shell quoting:

$ beaver create "Login form rejects valid passwords" --label bug --priority high --body-file - <<'EOF'
When a user submits a correct password containing a `!`, the form clears and
shows "invalid credentials". Expected: the login succeeds.
EOF
Created t4y1gv  Login form rejects valid passwords
  .beaver/issues/t4y1gv-login-form-rejects-valid-passwords.md

Routine upkeep is likewise one command rather than a sequence β€” update

takes every field it changes at once, and reports the result in the same single-issue JSON shape the lifecycle verbs use:

$ beaver update t4y1gv --priority urgent --label regression,-needs-triage --assignee agent-7
Updated t4y1gv

Nothing here needs a terminal or an $EDITOR

. To rewrite a description, beaver update <ref> --body "<markdown>"

replaces it and leaves the ## Notes

section byte-identical; --body-file <path>

reads it from a file, and --body-file -

from stdin, which is the way to send multi-line Markdown without shell quoting:

$ beaver update t4y1gv --body-file - <<'EOF'
Submitting a correct password containing a `!` clears the form and shows
"invalid credentials". Expected: the login succeeds.
EOF
Updated t4y1gv

Editing the issue file directly is equally first-class β€” the files are the source of truth, and that is the interactive path this tool deliberately does not wrap in a command. Three rules keep a hand edit safe:

Leave the It is the append-only coordination journal; rewriting or dropping another actor's entries breaks the contract. Edit the description above it, and add your own entries only through## Notes

section alone.beaver note

.Never change theβ€” it is the issue's identity, and the filename merely mirrors it.id

fieldFollow up with A hand edit alone does not bump thebeaver note <ref> "<what you changed>"

.updated

timestamp; a note both journals the change for other actors and bumps it.

beaver doctor

is the net under all of it: run it after a hand edit and it reports anything the edit left behind. Whatever drifts anyway β€” say a filename gone stale after a hand-retitle β€” is lint, not corruption, and doctor --fix

repairs it.

Beaver Backlog is local-first and has no sync layer, so it cannot lock an issue. Instead an actor claims one by setting its assignee

field, and that claim travels through the VCS like any other change. It is a signal, not a rule: two actors on different branches can claim the same issue, and the merge surfaces the clash. Push claims early and integrate often.

Give each concurrent agent its own working tree (a separate git worktree

or clone). Two agents sharing one checkout can silently overwrite each other's edits β€” that configuration is unsupported.

Issues relate through depends_on

and parent

, stored one-sided on the dependent or child. beaver list --ready

shows what is actionable now (todo, every dependency done); --blocked

shows what is waiting.

beaver init

writes .beaver/config.yml

, which is committed and shared like the issues. It records the store's format version and nothing else today. Beaver Backlog only ever reads and writes files β€” it never runs a version-control system, so committing your issue files stays entirely yours to do (and stays free to bundle with the code the issue produced).

Your identity lives in per-machine user config, never in the repository.

Distributed, hand-editable files can drift: filenames out of sync with titles, dangling references after a bad merge, dependency cycles, typo'd frontmatter keys. Everyday commands degrade gracefully β€” an invalid file is skipped with a warning, never a crash β€” and beaver doctor

reports everything it finds. doctor --fix

repairs only what is unambiguous (like drifted filenames) and never removes data.

β€” the project's language: what an issue, actor, claim, and note precisely mean.docs/GLOSSARY.md

β€” the conventions this repository uses to track its own work with Beaver Backlog.docs/TRACKER.md

β€” the modules and the seams between them.docs/ARCHITECTURE.md

β€” the coding and test conventions used by contributors.docs/CODING_STANDARDS.md

β€” the architecture decisions behind the design and their tradeoffs.docs/adr/

Beaver Backlog is now at 1.0.0. It will continue to evolve, but compatibility matters: changes to the issue file format, commands, flags, machine-readable output, and other public behavior will aim to remain backward-compatible. Unavoidable breaking changes may still happen, but they will be kept to a minimum and called out clearly.

Contributions are welcome β€” see CONTRIBUTING.md for how to build, test, and submit changes.

── more in #developer-tools 4 stories Β· sorted by recency
── more on @beaver backlog 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/show-hn-beaver-backl…] indexed:0 read:9min 2026-08-11 Β· β€”