Issue tracking for the agentic coding era.
One binary. One SQLite database (plus an attachments dir). MCP built in.
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:
- 29 MCP tools in 6,081 tokens. That's the measured size of the full
tools/listresponse at v2.0.0 (o200k tokenizer). Your entire tracker costs about as much context as one long file read. Bloated MCP servers are a real tax; this one isn't. - 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 connectwrites 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.
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 clientsThat'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:
lific doctor # green/yellow/red checks: config, database, server,
# OAuth discovery, and a real MCP round-tripdoctor exits nonzero if anything is actually broken, so agents and CI can gate on it.
- 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_planand resumes exactly where the last one left off. NoMEMORY.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_activityanswers "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_pagedo 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_issue,export_page,export_project: portable markdown, no lock-in.
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.
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
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 writingEach 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.
OAuth, if you'd rather auth as yourself
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:
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/mcpThe 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:
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 sessionTokens 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.
Manual configuration (any MCP client)
Remote (Streamable HTTP):
{
"lific": {
"type": "remote",
"url": "http://localhost:3456/mcp",
"headers": { "Authorization": "Bearer your-api-key" }
}
}Local (stdio, no server):
{
"lific": {
"type": "local",
"command": ["lific", "--db", "path/to/lific.db", "mcp"]
}
}Create keys anytime with lific key create --name my-key.
Web UI setup (if you prefer clicking)
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.
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_planbuilds a full nested tree at once;edit_plan_stepandupdate_plan_stepkeep 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.
lific agents-mdwrites an idempotent, marker-delimited block into your repo'sAGENTS.mdtelling every agent that this project uses Lific: project identifier, CLI examples, and the workflow conventions.lific connectoffers to do this automatically in project context.- Session instructions. The MCP server ships its conventions in the
initializeresponse, 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 | jqjust works, no--jsonneeded (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 also works directly against the database, no server or auth required:
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 APPAll 29, in 6,081 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 · export_page · export_project |
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.
| 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 | 29 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 | Full CRUD for all resources, search, board view |
| 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 | Full CRUD, TTY-aware JSON output, works with no server running |
| Single binary | No runtime dependencies, embedded SQLite, ~25 MB |
Honesty is cheaper than churn:
- You need enterprise team features. No SSO/SAML, no sprints, no estimates, no roadmap gantt charts. 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 diffon 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.
Lific has project-scoped, default-deny authorization: viewer / maintainer / lead membership enforced on every REST and MCP call, including reads. 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:
lific instance set --authz-enforced true # or falseWith 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):
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 LIFForgotten password? The operator can reset one from the shell (this signs out all of that user's sessions):
lific user set-password --username samAuth 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. 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:
lific key listPrefer 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.
lific.toml
lific init generates this:
[server]
host = "0.0.0.0"
port = 3456
cors_origins = []
[database]
path = "lific.db"
[backup]
enabled = true
dir = "backups"
interval_minutes = 60
retain = 24
[log]
level = "info"
[auth]
allow_signup = true
required = trueCLI 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.
Config is discovered in standard locations, first match wins:
--config <path>(used alone, no fallback)./lific.toml(current directory)- User config dir:
~/.config/lific/lific.tomlon Linux ($XDG_CONFIG_HOMErespected),~/Library/Application Support/lific/on macOS,%APPDATA%\lific\on Windows - System config dir:
/etc/lific/lific.tomlon 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.
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:
lific dump # → ./lific_20260703_141500.tar.gz
lific dump --out /mnt/backups # directory → default filename inside itEach 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:
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:
# e.g. restic pre-hook
lific dump --out /srv/backup-staging/lific.tar.gz && restic backup /srv/backup-staging- 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.
git clone https://github.com/VoidNullable/lific
cd lific
mkdir -p web/dist
cargo build --releaseThe 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.
git clone https://github.com/VoidNullable/lific
cd lific
cd web && bun install && bun run build && cd ..
cargo build --releaseThe 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.
Questions, feedback, or a setup worth showing off? Join the Lific Discord. Release announcements land there too, and support questions get answered fastest in #support.
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.
Lific ships a registry manifest (server.json) for the official MCP Registry. Its canonical registry name:
mcp-name: io.github.VoidNullable/lific
