Self-hosted issue tracker

An issue tracker your coding agent can actually use.

A cairn is a stack of stones left on a trail so whoever comes next knows the way. Every task here carries a note for whoever picks it up, human or agent.

Choose your platform
curl -fsSL https://github.com/alperkyoruk/cairn/releases/latest/download/cairn_darwin_$(uname -m | sed 's/x86_64/amd64/').tar.gz | tar -xzf - cairn && ./cairn
curl -fsSL https://github.com/alperkyoruk/cairn/releases/latest/download/cairn_linux_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz | tar -xzf - cairn && ./cairn
irm https://github.com/alperkyoruk/cairn/releases/latest/download/cairn_windows_amd64.zip -OutFile cairn.zip; Expand-Archive cairn.zip -DestinationPath . -Force; .\cairn.exe
docker run -d -p 127.0.0.1:7777:7777 -v cairn-data:/data ghcr.io/alperkyoruk/cairn:latest

Then open http://127.0.0.1:7777. That is the whole install. All builds & checksums

v0.2.2One file, ~5MBNo runtime, no dependenciesSQLiteFrontend embeddedWorks airgappedApache-2.0

Agent support is not a plugin.

How every other tracker works

Designed human-first, then agent access is bolted on afterwards as a third-party add-on wrapping the public API. The agent is a robot pretending to be a user: it posts comments, it moves cards, and the schema has no idea it exists. Nothing in the data model distinguishes a decision a person made from a step a process took.

How Cairn works

Agent behaviour is in the schema and in the rules. Agents are named actors with their own tokens. Two transitions are reserved for the human and cannot be made by an agent at all — deciding what gets worked on, and deciding something is finished. Everything else an agent does, it does as a first-class user of the same server.

Two records per task, and they are not the same kind of thing.

This is the whole idea. One is the note on top of the cairn — small, current, overwritten every time. The other is the trail behind it — append-only, never edited, never deleted.

state

one row · overwritten in place
State blocked
where_i_left_off

The importer parses the 2019 format but chokes on the 2021 header row, which added a currency column in the middle rather than at the end. Rewrote the column mapping to be name-based instead of positional.

next_step

Get a sample file from the 2022 exports and confirm the header names did not change again before finishing the mapper.

Three fields, one byline, no history. An agent picking the task up reads this and nothing else. So does the human, at a glance, from the board.

worklog

append-only · 28 entries
codex queueactive 20 Aug 09:12

Read the 2019 importer end to end and wrote a fixture from the sample file in testdata.

Fixture parses. The column mapping is positional, which is going to break on any header change.

codex 21 Aug 16:05

Ran the mapper against every file in testdata and diffed row counts against the old importer.

Identical for 2019, 41 rows short for 2021 — all of them rows where the currency column is blank.

codex activeblocked 22 Aug 10:31

Looked for the 2022 exports in the repo and in the fixtures directory.

Not there, and not in any branch. They are in the archive bucket, which this machine has no credentials for.

What was tried, what happened, and the status change it came with. Nothing here is ever edited or deleted, so the record of how a task got where it is survives the agent that wrote it.

Your agent talks to it over MCP.

Register the agent in the web UI, copy the token once, paste it into the agent's MCP config. Streamable HTTP at /mcp, same server, same database. The agent never sees the web interface — that is yours.

{
  "mcpServers": {
    "cairn": {
      "type": "http",
      "url": "http://127.0.0.1:7777/mcp",
      "headers": {
        "Authorization": "Bearer cairn_7fJq…3uQg"
      }
    }
  }
}

What the agent can do

  • Read the board and read one task with its state and full worklog
  • Write the task's state — where it left off, the next step, what it is blocked on
  • Append a worklog entry: what it tried, what happened
  • Move a task through the statuses it is allowed to move it through
  • File a new task in the backlog

What it cannot do

Queue a task from the backlog, or mark anything done. Those two transitions are the human's, enforced on the server rather than by prompt. You decide what gets worked on and you decide when it is finished.

Running in three steps.

Unpack it and run it.

One file with the frontend inside it. Nothing to install alongside, nothing to point at a database, no runtime to provision.

curl -fsSL https://github.com/alperkyoruk/cairn/releases/latest/download/cairn_$(uname -s | tr A-Z a-z)_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz | tar -xzf - cairn && ./cairn
  • macOS and Linux on amd64 and arm64, Windows on amd64, with checksums.txt.
  • Docker instead: the image is 17.3MB, linux/amd64 and linux/arm64, built FROM scratch and running as an unprivileged uid.
  • From source: git clone, then Go 1.25+ and Node 24+ and make build && ./cairn.

Name yourself.

Open the root URL and pick a username and a password. No email, no confirmation step, no account. This is your server — the name you pick is the one that appears next to every task you touch.

There is no reset email and never will be. If you forget it, run cairn --reset-password on the machine it runs on.

Register an agent.

Give it a name — claude, codex — and Cairn issues a token. It is shown exactly once and stored only as a hash. Paste it into that agent's MCP config and it starts filing its own worklog.

What it does not do, and will not.

This is a closed list, not a roadmap. Every one of them is a deliberate omission, and the tool is smaller and faster because of it.

Ganttcalendar viewsaved viewsdue datesremindersrecurring taskslabelstagsprioritiesestimatessprintscyclesmilestonesattachmentscommentssubtaskstask hierarchydependenciesCalDAVSMTPnotificationstoastsmobile appdesktop appteamsrolesinvitationssharingassignee pickersmentionsactivity feeds

There is one user. There is no one to notify, no one to assign to, and no one to share with.

Apache-2.0

Use it, fork it, run it inside a company. The whole point is that it is yours once it is on your machine — there is no hosted tier to upsell you to and no telemetry to switch off.

Contributing

Go on the server, Vue 3 on the frontend, no CSS framework and no icon library — every dependency ends up in the binary and in the supply chain of a security-adjacent self-hosted tool. Read the list above before proposing a feature.