lific 2.5.0

Local-first, lightweight issue tracker. Single binary, SQLite-backed, MCP-native.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
<p align="center">
  <img src="LificHero.png" alt="Lific Issue tracking built for AI-driven development" width="800">
</p>

<p align="center">
  <a href="https://github.com/VoidNullable/lific/actions/workflows/ci.yml"><img src="https://github.com/VoidNullable/lific/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://crates.io/crates/lific"><img src="https://img.shields.io/crates/v/lific" alt="crates.io"></a>
  <a href="https://github.com/VoidNullable/lific/releases"><img src="https://img.shields.io/github/v/release/VoidNullable/lific" alt="Release"></a>
  <a href="LICENSE"><img src="https://img.shields.io/github/license/VoidNullable/lific" alt="License"></a>
  <a href="https://discord.gg/uWvaFC4f7D"><img src="https://img.shields.io/discord/1516612377196363889?logo=discord&logoColor=white&label=discord&color=5865F2" alt="Discord"></a>
</p>

<p align="center">
  <strong>Issue tracking for the agentic coding era.</strong><br>
  One binary. One SQLite database (plus an attachments dir). MCP built in.
</p>

<p align="center">
  <a href="https://lific.dev"><strong>lific.dev</strong></a>
  &nbsp;·&nbsp;
  <a href="https://lific.dev/docs">Docs</a>
  &nbsp;·&nbsp;
  <a href="https://discord.gg/uWvaFC4f7D">Discord</a>
</p>

---

Your agent can write the code. What it can't do is remember: the plan dies with the context window, the TODO list rots in a markdown file, and the next session starts from zero. Lific is the missing memory: a self-hosted, single-binary issue tracker whose primary user is often an agent rather than a person.

Three numbers instead of adjectives:

- **27 MCP tools in 5,641 tokens.** That's the measured size of the full `tools/list` response at v2.2.1 (o200k tokenizer). Your entire tracker costs about as much context as one long file read.
- **One ~25 MB binary.** Embedded SQLite, embedded web UI, backups built in. The data set is just the database and a content-addressed `attachments/` dir beside it (both covered by the automatic backups). No Docker, no Postgres, no reverse proxy, no daemon farm. Copy it to a server, point your agents at it, done.
- **11 AI clients configured by one command.** `lific connect` writes correct MCP config into OpenCode, Claude Code, Cursor, VS Code, Codex, Zed, and more. No hand-edited JSON.

Identifiers are human-readable everywhere: `APP-42`, never a UUID. They survive being spoken, logged, grepped, and pasted into a prompt.

## 60-second setup

```bash
cargo install lific     # or grab a binary from the releases page

lific init              # config + database + your API key, printed once -
                        # then installs a background service and starts it.
                        # The server is now on :3456 and survives reboot.
lific connect           # writes MCP config into your AI clients
```

That's the whole thing. `lific init` sets everything up in your OS's standard locations (config in `~/.config/lific/`, data in `~/.local/share/lific/` on Linux; macOS and Windows equivalents) so it works the same from any directory - use `lific init --here` if you'd rather keep a directory-local instance (`./lific.toml` + `./lific.db`). It registers the server with your OS service manager (a systemd user unit on Linux, a LaunchAgent on macOS), so it isn't a process tied to your terminal - it's still running tomorrow. `lific connect` then detects the AI tools installed on your machine, lets you pick, mints a per-tool API key, and merges correct MCP config into each one without overwriting existing config. Restart your client and the Lific tools are there.

Manage the service anytime with `lific service status | restart | stop | uninstall`. Prefer a foreground process (containers, supervisors, debugging)? `lific init --no-service` skips the service and `lific start` runs the server in your terminal.

The web UI is at `http://localhost:3456`. Sign up there to create your account, then grant it admin rights from the CLI: `lific user promote --username <username>`.

Verify any setup with:

```bash
lific doctor            # green/yellow/red checks: config, database, server,
                        # OAuth discovery, and a real MCP round-trip
```

`doctor` exits nonzero if anything is actually broken, so agents and CI can gate on it.

## What your agent can now do

- **Ask "what can I work on right now?" in one call.** `list_issues(project="APP", workable=true)` returns only issues with every blocker resolved. Dependency-aware triage without a graph query.
- **Keep a plan alive across sessions.** Plans are persistent, nestable step trees. A fresh session calls `get_plan` and resumes exactly where the last one left off. No `MEMORY.md`, no re-priming ritual.
- **Break work down and wire it up.** Create issues, link blockers (`blocks`, `relates_to`, `duplicate`), group them into modules, and mirror plan steps to real issues with two-way done/close sync.
- **Leave a real audit trail.** `get_activity` answers "what changed while I was gone": who changed what, when, and through which tool. Every agent's work is attributed (more below).
- **Write docs where the issues live.** Markdown pages in folders, with comments, labels, lifecycle status, and Mermaid diagrams. Design decisions stay next to the work they justify.
- **Edit without resending.** `edit_issue` / `edit_page` do targeted find-and-replace, so updating one line of a long description doesn't cost the whole document in tokens.
- **Take everything with you.** `export` turns an issue, a page, or a whole project into portable markdown, no lock-in.

## Every tool gets its own identity, and that's the point

`lific connect` mints a **separate bot identity per tool**, owned by your account (`opencode-blake`, `cursor-blake`, ...). When several agents work the same project, provenance is the primitive that keeps you sane:

- **The audit log shows which harness made every change.** "OpenCode closed APP-42", "Cursor edited the design page". All attributed to you, never blurred together.
- **Revoke one tool without touching the others.** Cursor misbehaving? Disconnect just its key and OpenCode keeps working.
- **Scoped authority.** Each bot inherits its owner's project access and nothing more.

This is the recommended way to connect agent harnesses.

## Connecting AI tools

**`lific connect`** is the front door. It supports eleven clients out of the box:

`opencode` · `claude-code` · `claude-desktop` · `cursor` · `vscode` · `codex` · `zed` · `gemini` · `windsurf` · `goose` · `crush`

```bash
lific connect                                    # interactive picker
lific connect --client opencode --client cursor --yes   # non-interactive
lific connect --client claude-code --scope project      # repo-local .mcp.json
lific connect --client zed --stdio               # no server: direct SQLite over stdio
lific connect --dry-run --client vscode          # preview without writing
```

Each client gets its native schema (`mcpServers` vs `servers` vs `mcp`, Codex TOML with an env-var token, Goose YAML; the quirks are handled). JSON configs are merged non-destructively; a file `connect` can't parse safely is left untouched and you get the exact snippet to paste instead.

<details>
<summary>OAuth, if you'd rather auth as yourself</summary>

Lific implements the full MCP authorization spec (RFC 9728 protected-resource metadata, dynamic client registration, PKCE), so OAuth-capable clients can connect with **just the URL** and complete auth in the browser:

```bash
lific connect --oauth --client opencode   # writes a header-less config, mints nothing
opencode mcp auth lific                   # browser opens → sign in → approve

# or with any client's native command:
claude mcp add --transport http lific http://localhost:3456/mcp
```

The trade-off: an OAuth token **is you**. Changes made through it are indistinguishable from your own edits in the audit log, with no per-harness attribution. Fine for personally browsing your tracker from an editor; for agents doing real work, prefer the per-tool bot identities above.

**Headless / SSH / agents.** No browser on the box? The device flow has you covered:

```bash
lific login                     # prints a URL + short code; approve on any device
lific login --non-interactive   # agent mode: prints JSON {verification_uri, user_code,
                                #   device_code, next_step} and exits immediately
lific login --complete <code>   # finish later, from a script or a second session
```

Tokens are stored in your OS keyring (Secret Service / Keychain / Credential Manager), falling back to a `0600` file with a loud warning when no keyring exists.

</details>

<details>
<summary>Manual configuration (any MCP client)</summary>

**Remote (Streamable HTTP):**

```json
{
  "lific": {
    "type": "remote",
    "url": "http://localhost:3456/mcp",
    "headers": { "Authorization": "Bearer your-api-key" }
  }
}
```

**Local (stdio, no server):**

```json
{
  "lific": {
    "type": "local",
    "command": ["lific", "--db", "path/to/lific.db", "mcp"]
  }
}
```

Create keys anytime with `lific key create --name my-key`.

</details>

<details>
<summary>Web UI setup (if you prefer clicking)</summary>

Go to **Settings > Connected tools** in the web UI. Pick your tool, click Connect, and paste the generated config snippet.

Each connection creates a bot identity tied to your account (the CLI's `connect` does the same). Changes show up attributed to you, tagged with which tool made them.

</details>

## Plans: state that outlives the session

An agent's plan shouldn't die when its context does. A **plan** is an ordered, arbitrarily-nestable tree of steps that persists across sessions and compaction. Start a new session, call `get_plan`, and it's still there, ready to resume.

- **Steps can mirror issues.** Link a step to an issue and the two stay in sync: close the issue and the step checks itself; mark the step done and the issue closes. Reopen the issue and the step reopens, with a note of why.
- **Authored in one call.** `create_plan` builds a full nested tree at once; `edit_plan_step` and `update_plan_step` keep it current.
- **First-class in the UI.** A Plans tab sits alongside Issues, Board, Modules, and Pages: a real tree view with done toggles, per-step markdown notes, issue chips, and an activity timeline.
- **Fully tracked.** Every plan and step change lands in the audit log, including the issue-driven cascades.

Issues stay flat and lateral; the hierarchy lives on the plan. It's the difference between an issue tracker and a project planner.

## Built for agents, not just reachable by them

- **`lific agents-md`** writes an idempotent, marker-delimited block into your repo's `AGENTS.md` telling every agent that this project uses Lific: project identifier, CLI examples, and the workflow conventions. `lific connect` offers to do this automatically in project context.
- **Session instructions.** The MCP server ships its conventions in the `initialize` response, so connected agents know how Lific wants to be used without you explaining it.
- **Self-onboarding.** On a fresh database, the MCP tools tell the agent exactly how to bootstrap (`create a project first: manage_resource(...)`) instead of returning an empty list.
- **Pipe-native CLI.** Output auto-upgrades to JSON when piped, so `lific issue list --project APP | jq` just works, no `--json` needed (though it's there). Prompts never hang a non-interactive caller; they fail fast and name the bypass flag.
- **Shell completions:** `lific completion fish | source` (bash, zsh, fish, powershell, elvish).

The CLI works directly against the database, with no server or auth required. Data commands also support an HTTP backend when you want the CLI to operate on a remote Lific instance:

```bash
lific project list
lific issue list --project APP
lific issue create --project APP --title "Fix login bug" --priority high
lific issue update APP-42 --status done
lific search "authentication" --project APP

# Remote mode (the same commands use the server API)
lific --backend http --url https://lific.example.com --api-key "$LIFIC_API_KEY" issue list --project APP
lific --backend http --url https://lific.example.com --api-key "$LIFIC_API_KEY" issue update APP-42 --status done
```

`--backend http` can read the URL and bearer key from `LIFIC_URL` and `LIFIC_API_KEY`. If no API key is supplied, it also uses the credential from `lific login` (`LIFIC_TOKEN`, keyring, or credential file). The default backend remains direct SQL; HTTP mode never opens the local database for data commands. Its human-readable output is currently pretty-printed JSON. A remote project export downloads one archive file from the server rather than individual Markdown files.

## MCP tools

All 27, in 5,641 tokens:

| Family | Tools |
|--------|-------|
| Issues | `list_issues` · `get_issue` · `create_issue` · `update_issue` · `bulk_update` · `edit_issue` · `get_board` |
| Relations | `link_issues` · `unlink_issues` |
| Pages | `get_page` · `create_page` · `update_page` · `edit_page` |
| Plans | `create_plan` · `get_plan` · `edit_plan_step` · `update_plan_step` |
| Comments | `add_comment` · `list_comments` · `edit_comment` · `delete_comment` |
| Search & history | `search` · `get_activity` |
| Structure | `list_resources` · `manage_resource` · `delete` |
| Export | `export` (issue, page, or whole project by ID) |

Everything takes human-readable identifiers (`project="APP"`, not `project_id=7`). The behaviors worth knowing about are covered in "What your agent can now do" above; for exact schemas, connect a client and read `tools/list`.

## Features

| Category | What you get |
|----------|-------------|
| **Issue tracking** | Status, priority, modules with icons, labels, relations, comments, board view, fuzzy search, sort by recent activity |
| **Plans** | Persisted, nestable step trees that outlive a session; steps mirror issues with two-way done/close sync |
| **Documentation** | Markdown pages in recursive folders, with comments, labels, lifecycle status, full-text search, and Mermaid diagrams |
| **MCP interface** | 27 tools, human-readable identifiers, compact schema, session instructions |
| **Onboarding** | One-command setup (`lific init` installs a background service), `lific connect` (11 clients), `lific doctor`, `lific agents-md`, shell completions |
| **REST API** | Resource endpoints, search, board view, and relationship/planning operations |
| **Web UI** | Markdown editing with live preview, drag-and-drop board, Mermaid and code-copy, dark/light theme |
| **User accounts** | Individual auth, per-tool bot identities, project membership and roles |
| **Auth** | OAuth 2.1 (PKCE, dynamic client registration, RFC 9728 discovery), RFC 8628 device flow, API keys, token revocation |
| **Backups** | `lific dump` / `lific restore` single-archive backups, plus automatic interval archives with retention |
| **CLI** | Scriptable issue/project/page/plan commands, TTY-aware JSON output, works with no server running |
| **Single binary** | No runtime dependencies, embedded SQLite, ~25 MB |

## When Lific is the wrong tool

Honesty is cheaper than churn:

- **You need enterprise team features.** SSO/SAML isn't here, and neither is the sprint-estimate-gantt layer of project management. If you're coordinating forty humans, use Linear or Plane.
- **You want issues as files in the repo.** Lific is a database with an API, not markdown-in-git. If you want `git diff` on your task list, a markdown-native tracker fits better.
- **You need distributed multi-writer sync.** One Lific instance is one SQLite file: a single source of truth that's trivially backed up, not a CRDT. Multiple agents talk to one server; the server doesn't merge with other servers.

For one human directing several agents across personal projects (the thing it's built for), none of those trade-offs bite.

## Authorization

Lific has project-scoped, default-deny authorization: viewer / maintainer / lead membership is enforced on project-scoped REST and MCP calls, including reads. Instance administrators and operator-trusted credentials intentionally bypass project membership checks, while instance-scoped endpoints have their own rules. **Fresh installs (created on 2.0+) enforce it by default; instances upgraded from an earlier version keep it off** until you opt in - nothing changes under you on upgrade. Toggle it at runtime:

```bash
lific instance set --authz-enforced true    # or false
```

With enforcement on, a newly created user sees nothing until they're granted membership. Manage access from the CLI (or the web UI's project members page):

```bash
lific member add --project LIF --user sam              # viewer by default
lific member add --all --user sam --role maintainer    # every project at once
lific member role -p LIF -u sam -r lead                # change a role
lific member remove -p LIF -u sam
lific member list --project LIF
```

Forgotten password? The operator can reset one from the shell (this signs out all of that user's sessions):

```bash
lific user set-password --username sam
```

**Auth can be turned off entirely for a private, local instance** with `required = false` under `[auth]` in `lific.toml`. Credential-less requests then get admin-equivalent access; a presented-but-invalid token still fails loudly. The web UI signs you in automatically as the first admin (the single-user auto-login flow) instead of showing a login form - if no account exists yet, the signup screen still appears so there's an identity to attribute work to. This is a config-file key on purpose (flipping it requires shell access, like minting an operator key), and it comes with guard rails: the server refuses to start if `server.public_url` points anywhere but localhost, and logs a prominent warning otherwise - the default bind is `0.0.0.0`, so keep an auth-less instance loopback-only or firewalled.

**Unbound API keys bypass authorization by design.** A key with no user binding - the one `lific start` auto-mints on a keyless DB, and the ones `lific key create` and `connect`'s fresh-install path produce - is *operator-trusted*: it can only be created by someone with shell access to the server, so it's treated as admin-equivalent even in enforced mode. That's what keeps the zero-user `init → start → connect` flow working with enforcement on. The threat the default guards against is a web-signup stranger's session/OAuth token, not the operator's own shell-minted key. Audit these keys any time with:

```bash
lific key list
```

Prefer per-tool **bot identities** (what `lific connect` mints when you have a user account) over unbound keys: a bot inherits its owner's project access and shows up in the audit log by name.

## Configuration

<details>
<summary><code>lific.toml</code></summary>

`lific init` generates this:

```toml
[server]
host = "0.0.0.0"
port = 3456
cors_origins = []
trusted_proxies = ["127.0.0.0/8", "::1/128"]

[database]
path = "lific.db"

[backup]
enabled = true
dir = "backups"
interval_minutes = 60
retain = 24

[log]
level = "info"

[auth]
allow_signup = true
required = true
```

CLI flags (`--db`, `--port`, `--host`) override config values. Set `server.public_url` when exposing Lific beyond localhost; it becomes the OAuth issuer and the URL `lific connect` writes into client configs. `server.trusted_proxies` controls which peers may supply `X-Forwarded-For` or `X-Real-IP`; it defaults to loopback for Tailscale Funnel. Add only proxy IPs/CIDRs you operate.

Config is discovered in standard locations, first match wins:

1. `--config <path>` (used alone, no fallback)
2. `./lific.toml` (current directory)
3. User config dir: `~/.config/lific/lific.toml` on Linux (`$XDG_CONFIG_HOME` respected), `~/Library/Application Support/lific/` on macOS, `%APPDATA%\lific\` on Windows
4. System config dir: `/etc/lific/lific.toml` on Linux/BSD, `/Library/Application Support/Lific/` on macOS, `%ProgramData%\lific\` on Windows

A relative `database.path` always resolves against the config file's own directory, so the same config works no matter where the process starts. `lific init --config /path/to/lific.toml` and `lific service install --config ...` root the whole instance (config, database, service working directory) at that path.

</details>

## Backup and restore

The data set is the database plus a content-addressed `attachments/` dir beside it. `lific dump` packages both into one self-contained archive, taking a consistent DB snapshot via `VACUUM INTO` that is safe while the server is running:

```bash
lific dump                      # → ./lific_20260703_141500.tar.gz
lific dump --out /mnt/backups   # directory → default filename inside it
```

Each archive contains the DB snapshot, every attachment blob, and a `manifest.json` (Lific version, schema version, sizes). Restoring is the mirror image. Stop the server first:

```bash
lific restore lific_20260703_141500.tar.gz          # refuses to overwrite an existing db
lific restore lific_20260703_141500.tar.gz --force  # moves the current db aside to lific.db.pre-restore-<ts>
```

Restores are staged (a failure leaves the original data dir untouched) and refuse archives created by a newer Lific; older archives are fine, and pending migrations apply on next start.

The automatic interval backups (`[backup]` in config) write the same `.tar.gz` artifact to the backup dir with rotation. External backup harnesses (restic, borg, cron) can either scoop up that dir or call `lific dump` as a pre-backup hook:

```bash
# e.g. restic pre-hook
lific dump --out /srv/backup-staging/lific.tar.gz && restic backup /srv/backup-staging
```

## Building from source

### Requirements

- **Rust 1.88+** required
- **Bun** optional, only needed if you want the web UI

SQLite is bundled via `rusqlite` and compiled into the binary. No system SQLite required.

### API-only build (no web UI)

```bash
git clone https://github.com/VoidNullable/lific
cd lific
mkdir -p web/dist
cargo build --release
```

The `mkdir -p web/dist` creates the empty directory that `rust-embed` expects at compile time. The resulting binary has full functionality (MCP, REST API, CLI, OAuth, backups) but visiting the web UI will return a message pointing you to build the frontend.

### Full build (with web UI)

```bash
git clone https://github.com/VoidNullable/lific
cd lific
cd web && bun install && bun run build && cd ..
cargo build --release
```

The frontend is a Svelte 5 SPA built with Vite. `bun run build` outputs static files to `web/dist/`, which `cargo build` embeds into the binary. The final binary is fully self-contained with no runtime dependencies.

### Docker (optional)

You don't need Docker to run Lific; it's one binary. The `Dockerfile` in the repo exists mainly so MCP directory indexers can build and verify the server, but it produces a working image (full web UI, distroless runtime) if a container fits your setup:

```bash
docker build -t lific .
docker run -p 3456:3456 -v lific-data:/data lific
```

## Community

Questions, feedback, or a setup worth showing off? Join the [Lific Discord](https://discord.gg/uWvaFC4f7D). Release announcements land there too, and support questions get answered fastest in #support.

## Contributing

Issues and PRs welcome. If you're planning something big, open an issue first so we can talk about it before you put in the work.

## MCP Registry

Lific ships a registry manifest (`server.json`) for the official [MCP Registry](https://registry.modelcontextprotocol.io). Its canonical registry name:

- `mcp-name: io.github.VoidNullable/lific`

## License

[Apache-2.0](LICENSE)