v2.0.0
Lific 2.0 is three releases in one. The web UI moves from complete to fast, personal, and pleasant: a real home page, analytics, saved views, undo, a peek panel, full keyboard control, and a theming system. Underneath it, Lific gets real authorization. Project-scoped membership and roles are enforced identically across the REST API and every MCP tool, on by default for fresh installs and opt-in for instances upgrading from 1.x (existing setups keep working bit-for-bit). And around it, a new CLI onboards the whole thing in two commands: lific init builds a running, boot-persistent instance and lific connect wires any of 11 AI clients to it, with health checks, device-flow login, and per-tool agent identities.
The CLI got a facelift - clack-style sessions and real selectors
Human-facing CLI output moved from bare println! walls to a proper prompt UI (via cliclack, the Rust implementation of the @clack/prompts look): a ┌ lific init session header, a gutter connecting ◇ completed steps, boxed notes for things you must actually read (API keys, next steps, manual snippets), and a └ outro.
lific connecthas a real picker now. The "type comma-separated numbers" prompt is gone: an arrow-key multi-select lists every client with installed ones marked(detected)and preselected - space toggles, enter confirms. The AGENTS.md question is a proper confirm prompt.- Sessions everywhere:
init,connect,doctor(◇/▲/■per check severity, summary as the outro),login(code in a note block, a live spinner while waiting for approval),service,restore,dump, plus key and user management output. lific user create's password prompt is masked now - it previously echoed the password in plaintext.- Agents see zero change. JSON output (explicit
--jsonor piped stdout), non-TTY fail-fast prompts, and every machine-readable shape are byte-for-byte untouched; the pretty layer renders only for humans at a terminal.
lific connect can no longer wire your tools to the wrong instance silently
Running connect from the wrong directory used to be a quiet disaster: it would silently create a brand-new empty lific.db in whatever directory you happened to be in, mint keys against it, and rewrite every selected client's lific entry to point there - replacing their config for your real instance without a word about which instance it was targeting.
- Connect refuses to run where no instance exists. If the resolved database file isn't there, it errors with directions (
run from the instance directory, pass --config/--db, or lific init) instead of conjuring a fresh one. - The target is announced up front: the session opens with
Instance: <url> (keys minted in <db path>), and the client picker itself asks "Which clients should connect to ?" - wiring tools to the wrong instance now requires ignoring two explicit statements of it. --configworks from anywhere now. A relativedatabase.pathin a config file resolves against the config file's directory, not the process cwd - previouslylific --config /srv/lific/lific.toml <cmd>run from elsewhere would look for (or create) the database in your cwd. Backups anchor the same way.
lific init now sets up everything - including a service that survives reboot
The 60-second setup used to end with a server tied to an open terminal: close it (or log out) and your agents' "missing memory" was gone. lific init is now the whole onboarding story:
- One command: writes
lific.toml(kept if present), creates and migrates the database, mints and prints your initial API key, installs a background service, starts it, and verifies the server actually answers before claiming success. Re-running is safe and repairs whatever is missing. - Real service integration: a systemd user unit on Linux (
~/.config/systemd/user/lific.service, with best-effortloginctl enable-lingerso it outlives logout) or a launchd LaunchAgent on macOS (~/Library/LaunchAgents/dev.lific.plist). Starts on boot, restarts on failure. lific service install | uninstall | status | stop | restartmanages the service afterward;statusexits nonzero when the service isn't running, so scripts and agents can gate on it.- Honest failure modes: no service manager (containers, WSL without systemd) falls back to clear
lific startinstructions; a port squatted by another process is diagnosed as such instead of reported as success (init cross-checks the unit's own state against the health probe, so a stranger answering on :3456 can't fake a working install). - The API key prints during
init, in your terminal - not into a service journal nobody reads. The old box-drawing around the key (which rendered broken) is gone;lific startremains for foreground use (lific init --no-serviceskips service setup entirely). - The README's 60-second setup now matches reality, and its
lific user promote <username>example is corrected to the actual--usernamesyntax.
Authorization on by default for fresh installs
Project-scoped authorization (detailed below) would be pointless shipped dark: a brand-new install with authz_enforced off has no authorization at all - any valid bearer token could read, mutate, or delete every project. So fresh installs enforce by default, without breaking the zero-user init → connect flow; instances upgrading from 1.x keep enforcement off until an admin opts in.
- Install-dependent seed. On the first run that creates the settings row,
authz_enforcedis seeded from whether the database has any users yet: a fresh install (zero users) enforces by default; an instance upgraded from an earlier version (users already exist) stays off. The row is authoritative once written - later starts never re-evaluate or flip it, and an admin who turns enforcement off stays off. - Operator-key trust rule. The agent-first flow runs on user-unbound API keys, which resolve to no effective user and would be default-denied under enforcement. Such keys can only be minted with shell access to the server (
lific start's auto-key,lific key create,connect's fresh-install path), so in enforced mode they are now treated as operator-trusted (admin-equivalent). The signal is credential-type-specific and set only on the unbound-API-key auth path - a legacy pre-binding OAuth token also resolves to no user but is not granted operator power and stays default-denied (covered by explicit regression tests on both REST and MCP). - Unbound API keys bypass authorization by design. Audit them with
lific key list. Prefer per-tool bot identities (whatlific connectmints once you have a user account), which inherit their owner's project access and are attributed by name.
Project membership and roles
Until now, authentication was a door with no rooms behind it: any logged-in account - and any connected agent - could read, edit, or delete content in every project. 2.0 adds project-scoped membership and roles, so an agent holds exactly the authority its owner granted it and nothing more.
- Three roles per project:
viewer(read + comment),maintainer(full content and structure CRUD), andlead(everything, plus settings, membership, and project deletion). Multiple leads per project are supported; global admins override everything as the break-glass path. - Default-deny, reads included. With enforcement on, a non-member sees nothing - projects vanish from lists and search, and direct reads are refused. There is no implicit access floor.
- One enforcement layer, two transports. REST handlers and all 29 MCP tools call the same
authzmodule, so the web UI and agents can never drift apart. Cross-project operations (issue relations, plan-step issue links) require the role on every project touched. - Agents inherit their owner. A bot acts with its owning user's memberships and can never exceed them; OAuth-token requests resolve to their real user end to end. A token-backed agent that is a member keeps working under default-deny - verified by explicit lockout-regression tests on both transports.
- Safe, reversible rollout. Enforcement is a runtime instance setting (
authz_enforced, seeded on for fresh installs and off for upgrades; flip it anytime in Instance Settings orlific instance set --authz-enforced true). Legacy mode preserves pre-2.0 behavior bit-for-bit; existing project leads are backfilled asleadmembers automatically. - Membership management in Project Settings: list members with role badges, add by name, change roles inline, remove with confirmation - lead-gated, with last-lead protection so a project can't be orphaned. Every membership change lands in the audit log with actor attribution.
- Enumeration-derived coverage. The test suite extracts every REST route and every MCP tool and fails if any surface is missing an authorization classification, so future endpoints can't ship ungated. The suite now stands at 931 tests.
Connect an agent in one command
lific connect <tool>writes working MCP config into 11 AI clients - OpenCode, Claude Code, Claude Desktop, Cursor, VS Code, Codex, Zed, Gemini CLI, Windsurf, Goose, and Crush - globally or per-project, over stdio or HTTP. Each connected tool gets its own bot identity, so the audit log shows which agent did what;--oauthconnects native-auth clients without minting a key.lific doctorhealth-checks config, database, backups, server reachability, OAuth, and MCP wiring, with actionable fix hints.lific login/logout: two-step device-flow auth (RFC 8628) with keyring-backed credential storage - no pasting API keys.lific agents-mdwrites a maintained Lific section into a repo's AGENTS.md so agents learn the house conventions.- Terminal citizenship: shell completions for bash/zsh/fish, TTY-aware output (auto-JSON when piped, prompts never hang non-interactive runs), and piped output can no longer panic on SIGPIPE.
- For agents over MCP: the server's instructions now teach Lific workflow conventions, cold read tools nudge self-onboarding on a zero-project instance, and the repo ships an MCP Registry manifest and publish runbook.
Agent tooling (MCP)
- Edit and delete comments over MCP: new
edit_commentanddelete_commenttools, enforcing the same author-or-admin ownership rules as their REST counterparts. - Batch issue edits in one call:
bulk_updateapplies a status/priority/module change to every issue matching a filter (capped at 500) and returns the affected count - triage that was N round-trips is now one. - Schedule issues over MCP:
create_issueandupdate_issuenow acceptstart_dateandtarget_date, which already existed everywhere but the MCP layer. - Clear fields, not just set them: MCP can unassign an issue's module, move a page back to the folder root, and set or clear project and module emoji (empty string clears; omitted still skips).
- Find what's stuck:
list_issuesgains ablocked=truefilter - the inverse ofworkable- surfacing each blocked issue's unresolved blockers. - Comments join full-text search: comment threads are now indexed alongside issues and pages across search, MCP, and the web UI, with hits linking back to their parent issue or page.
- Duplicate relations are visible: issues linked as
duplicatenow show that relation inget_issue, MCP output, and markdown export - it was previously write-only. - Page listings paginate:
list_resources(page)honors thelimit/offsetit always documented, with the same over-fetch has-more hint as issue listings.
Account and instance settings
- Account settings: profile editing (display name, email), change password, and sign-out-everywhere. Changing your password revokes every other session - a stolen token dies the moment you rotate - while your current browser stays signed in.
- Instance settings: a DB-backed, admin-gated settings surface - name your instance, open or close signup, toggle authorization enforcement, and enable single-user auto-login (skip the login screen entirely on a personal single-account instance). Editable in the UI or via
lific instance set. - Connected-tools flow redesigned: a stepped connect modal with per-OS config paths, masked keys, copyable command chips, and real brand logos for every supported client.
A place to land
- My Work home dashboard: the new default landing page - your active issues grouped by project, recently viewed items, pinned pages, a cross-project activity digest, and quick actions. Login and signup land here now.
- Insights: a per-project analytics tab - created-vs-closed weekly trends (hand-rolled SVG, reopen-aware closure counting), current status/priority/module distributions, and most-active actors, with a 4/12/26/52-week window.
A faster issue surface
- Saved views: persist any filter/group/sort/layout combo as a named per-user view, switchable from the topbar, with a default view that auto-applies per project. Private to each user, project-visibility enforced.
- Board v2: swimlanes by module or priority (drag across a lane updates both status and the lane field in one move), collapsible columns that stay valid drop targets, and proper scroll-snap columns on mobile.
- Issue peek panel: preview an issue in a slide-over (bottom sheet on mobile) without leaving the list or board - quick status/priority/module edits included. Cmd/ctrl-click a board card or use the row's hover affordance.
- Keyboard-first navigation: j/k focus that survives refetches, x to select, enter to open, space to peek, s/p/m open pickers on the focused row (shift+S/P keep the old quick-cycle), and a
?help overlay generated from a single shortcut registry so it can't drift from reality. - Undo: status, priority, and module changes (from the list, board drags, detail view, and bulk operations) now confirm with a toast carrying a real Undo action. One unified toast system across the app (accessible live regions, hover/focus pauses dismissal).
- Undo-able deletes. Deleting issues (single or bulk) is deferred: rows vanish instantly, a toast offers Undo, and the actual delete only fires once the toast closes. Closing the tab flushes the pending delete instead of silently cancelling it.
Everywhere else
- Issue references come alive: bare identifiers (LIF-42, PROJ-DOC-3) auto-link in all rendered markdown (code blocks correctly excluded), show rich hover preview cards, and autocomplete in every editor via
#or an identifier prefix at the caret. Issue chips learned tricks too: shift-click opens the peek panel, right-click offers preview and open-in-new-tab. - Path-style deep links: plain URLs like
/LIF/issues/LIF-42resolve into the app at boot, so links from dashboards, chats, and agents land directly on the right view. - Appearance system: six accent presets (all AA-verified in both modes, including a fix to the stock indigo dark-mode contrast), comfortable/compact density, three font scales, and a reduced-motion preference that every animation in the app honors - applied before first paint, no flash.
- Motion & loading polish: content-shaped skeletons replace spinners on every heavy route, list rows and board cards glide on reorder, routes fade in quietly, and transition durations are normalized app-wide.
- Markdown formatting toolbar: bold, italic, headings, lists, checklists, code, links, and quotes in every editor, with Cmd+B / Cmd+I / Cmd+Shift+K shortcuts. Transforms toggle cleanly and play nice with native undo.
- Live timestamps: relative times ("2m ago") tick as time passes instead of going stale, and hovering any of them shows the exact date.
- Consistent breadcrumbs: issue, page, module, and plan detail views share one breadcrumb trail (PROJ > Issues > LIF-42) instead of ad-hoc back arrows.
- No silent failures: saves, deletes, comments, and clipboard copies that used to fail without a word now surface an error toast; copy actions confirm.
- Edit and merge labels. Labels can now be renamed and recolored in place, and duplicate labels can be merged (issues and pages re-tagged, source label removed) - with a full label manager and color picker in Project Settings.
- Pinned pages stay at the top of the page list.
Design and mobile
- Login and signup redesigned around the brand - and meet Lizzy, the mascot who now staffs the empty states, error pages, and the sign-in screen.
- Real error pages: a 404 and a global error boundary that recover gracefully without leaking internals.
- Light theme contrast overhaul and a typography token system (display through micro) replacing ad-hoc pixel sizes app-wide.
- Mobile pass: off-canvas navigation drawer, reflowed topbars, issue rows, and detail views, board snap-scroll columns, and touch-reachable actions.
- Topbar filters consolidated into a single Filter popover; projects reorder by drag in the sidebar, with collapsible per-project sub-navigation.
Security fixes
- Password changes revoke all other sessions - a stolen session token no longer survives a password rotation.
- The session cookie's
Secureflag is now gated on the request scheme, fixing broken logins on plain-http and localhost deploys. - OAuth approval CSRF tokens are bound to the approving session (previously forgeable across users), the CSRF MAC comparison is constant-time, and token revocation validates its bearer before acting.
- API key expiry is now enforced.
expires_atexisted in the schema and was shown bylific key list, but the auth path never checked it - an expired key authenticated forever. Both key lookups now reject expired keys, andlific key creategains--expires.
Performance
- Issue list label hydration is O(1) - one query instead of one per row.
- Hot read paths cache prepared statements.
list_plansis 2x faster via page-then-aggregate.
Upgrading
- The database upgrades itself automatically on first launch. Upgrading from any 1.x is safe and needs no manual steps.
- Fresh installs enforce authorization by default; upgrades from 1.x keep it off. An instance that already has users behaves exactly as before until an admin flips
authz_enforcedin Instance Settings or runslific instance set --authz-enforced true. Project leads are backfilled as members automatically, so flipping it on does not lock anyone out of their own projects. - Unbound API keys are operator-trusted and bypass authorization in enforced mode. Review them with
lific key listand revoke any you don't recognize.