The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Lific listing page.
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:
tools/list response (o200k tokenizer). Your entire tracker costs about as much context as one long file read.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.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.
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:
doctor exits nonzero if anything is actually broken, so agents and CI can gate on it.
get_briefing(project="APP") returns active plans with their next step, blocked issues and what blocks them (issues, people, or date windows), date waits that have come due, the top workable and active issues, and the pages you name, in about 6,000 characters. Pass the since cursor it gave you last time and it leads with what changed.list_issues(project="APP", workable=true) returns only issues with every blocker resolved. Dependency-aware triage without a graph query.link_issues(target="APP-9", relation_type="blocks", user="blake") parks an issue until someone clears it; from="2026-09-28", until="2026-10-02", note="filing office" parks it until a window opens, then shows it as due and later overdue.get_plan and resumes exactly where the last one left off. No MEMORY.md, no re-priming ritual.blocks, relates_to, duplicate), group them into modules, and mirror plan steps to real issues with two-way done/close sync.get_activity answers "what changed while I was gone": who changed what, when, and through which tool. Pass since to read forward from where you stopped. Every agent's work is attributed (more below).update_issue(status="done", evidence="cargo test: 412 passed") saves the evidence as a verification comment in the same write as the close. get_issue marks it [verification] and the web UI badges it, so whoever reviews the close can see what was actually checked. If the description has a - [ ] task list, get_issue and list_issues show its progress (3/5), and closing with boxes still unchecked says how many.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.get_page(section="Current state") fetches just that part. Nothing is silently cut off by the harness. Coming back to a page later, get_page(since_seq=...) returns only the lines that changed. Writes that push a page past the budget say so, with a nudge to split it.export turns an issue, a page, or a whole project into 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:
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
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.
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:
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:
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.
Remote (Streamable HTTP):
Local (stdio, no server):
Create keys anytime with lific key create --name my-key.
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.
create_plan builds a full nested tree at once; edit_plan_step and update_plan_step keep it current.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-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.initialize response, so connected agents know how Lific wants to be used without you explaining it.create a project first: manage_resource(...)) instead of returning an empty list.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.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:
--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.
All 31, in 7,626 tokens:
| Family | Tools |
|---|---|
| Issues | list_issues · get_issue · create_issue · update_issue · bulk_update · edit_issue · get_board |
| Relations & waits | 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 |
| Attachments | upload_attachment · get_attachment · list_attachments |
| Search & history | get_briefing · 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.
| 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 | 31 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 |
Honesty is cheaper than churn:
git diff on your task list, a markdown-native tracker fits better.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 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:
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):
Forgotten password? The operator can reset one from the shell (this signs out all of that user's sessions):
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:
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.
API-key verifier upgrades are a coordinated data-format change. The first successful use of an indexed legacy key rewrites its Argon2 verifier as sha256:v1; keys created or rotated by the new binary are also stored as sha256:v1. Older Lific binaries cannot authenticate those rows. Upgrade every server, CLI, and long-lived stdio process that shares the database together, and do not roll back to a binary that only understands Argon2 after any key has been created, rotated, or migrated. Active legacy rows without an indexed ID are revoked during upgrade. lific key list marks those rows UNSUPPORTED FORMAT / unsupported_format; rotate before reuse only for integrations that still need credentials. Rotation preserves the key's existing expiry and owner.
lific.tomllific init generates this:
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 none. Add only isolated proxy IPs/CIDRs you operate, and prevent direct clients from reaching that ingress.
Config is discovered in standard locations, first match wins:
--config <path> (used alone, no fallback)./lific.toml (current directory)~/.config/lific/lific.toml on Linux ($XDG_CONFIG_HOME respected), ~/Library/Application Support/lific/ on macOS, %APPDATA%\lific\ on Windows/etc/lific/lific.toml on Linux/BSD, /Library/Application Support/Lific/ on macOS, %ProgramData%\lific\ on WindowsA 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:
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:
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:
The repository provides Rust, Bun, and the release toolchains through devenv. Approve the checkout, enter the shell, and use the task graph:
To activate Devenv automatically when changing into the checkout, add the
Devenv hook for your shell once (for zsh, eval "$(devenv hook zsh)"), then
run devenv allow in this checkout.
Release builds always build and embed the Svelte 5 web UI:
Use release-darwin for macOS targets and release-windows-msvc for the
Windows MSVC cross-build on Linux. devenv build outputs.lific builds the
native, Nix-packaged release; it builds the locked frontend in an isolated
derivation and embeds it before compiling Rust. SQLite is bundled via
rusqlite; no system SQLite is required.
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:
The database lives at /data/lific.db, so mount a volume there to keep it. The image runs start --init-if-missing, which creates and migrates that file on first boot and does nothing on every boot after.
Both environment variables are required for that first boot, and they create the first admin. Leave either one out and the container refuses to start instead of creating an empty instance: an instance with no users has signup open, makes whoever loads the page first its administrator, and prints an unbound operator API key into the container log. An existing database ignores both variables, so they can stay in your compose file or machine config.
Authentication is required either way. First-boot init creates a database; it never turns auth off.
If an instance somehow ends up with no administrator, create one from inside the container (docker exec <container> ..., or fly ssh console on Fly):
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