Upgrade from 2.0 through 2.10
Back up a Lific 2.x instance, read the notes for each newer version, replace the binary, and check the result, with release notes from 2.10 back to 2.1.
This guide covers upgrading an existing installation of Lific 2.0 or later to 2.10. You can go straight to the newest version: the new binary applies every pending database migration when it starts, so you do not need to install the versions in between. Upgrading from before 2.0 is not covered here.
Upgrade procedure
1. Back up
Stop the service, or make sure nothing is writing, then take a full backup:
lific instance info # note the configuration and database paths in use
lific dump --out ./lific-before-upgrade.tar.gzThe dump holds a consistent copy of the database, the attachments and a manifest.json. Also copy the configuration file and your service definition. A Markdown export or a project archive is not a backup: neither includes accounts or permissions.
Keep this backup until the upgrade is verified. Once a newer version has migrated the database, an older binary refuses to open it, so going back means restoring the backup.
2. Read the notes for every newer version
Read each section under Release notes for versions newer than the one you run, and act on what applies to you. The changes most likely to need a decision:
| If you run | Check |
|---|---|
| A reverse proxy | server.trusted_proxies, at v2.2 and again at v2.7. From 2.7 a proxy on the same host (nginx, Tailscale Serve or Funnel) must be listed explicitly. |
A hand-edited lific.toml | From v2.6, a file that does not parse, or has unknown keys, stops startup. Run lific doctor first. |
| Scripts or agents with API keys | v2.7 moved key and account creation to browser sessions and the local CLI, and made password resets revoke keys. |
lific login on other machines | Before a server moves to v2.8 or later, upgrade those CLIs first. |
| Multi-instance MCP | From v2.9, every backend must have identical tool definitions. Upgrade them together before restarting the proxy. |
Agents running lific mcp over stdio | From v2.11, stop every process that opens the database, agent sessions included, and upgrade them all together. |
lific doctor reports pending migrations without applying them. lific doctor --repair does apply them, so only run it after taking the backup.
3. Replace the binary
- Stop the service.
- Install the new binary where the service runs it from.
- Start the service with the same configuration, database path, attachment directory and public URL.
Migrations run on this first start.
4. Verify
-
Run
lific --versionusing the executable the service actually runs, not another copy on yourPATH. -
Run
lific doctorand fix any configuration, database, backup, server, OAuth or MCP failure before letting users back in. -
Run
lific instance infoand confirm the database, public URL, sign-up policy andauthz_enforcedstate are what you expect. -
Review keys and memberships:
lific key list lific member list --project <PROJECT>API keys not bound to a user act as an administrator, because only someone with shell access can create them. Revoke any you no longer need, and give agents user-owned keys when they should be limited to project membership.
-
Sign in to the web UI and open a representative project. Check issues, pages, plans, comments, attachments and saved views before making changes.
-
Test one API client and one MCP client. Refresh MCP tool discovery. With multi-instance MCP, confirm every backend passed discovery and check
list_instancesbefore writing. Restart any device login started before the upgrade. -
If you use a proxy, confirm the server logs real client addresses and does not trust forwarded headers from anywhere else.
-
Confirm existing projects are still private. If you publish one, open its public address while signed out and check that ordinary routes still require sign-in.
-
Keep the backup until the web UI, API, MCP, scheduled backups and a service restart have all been checked.
Release notes
Newest first. Each section lists what changed for someone upgrading past that version.
v2.11
Seven migrations run on first start. One of them rebuilds the attachments table, so a database with many attachments takes a little longer to open the first time. Take a backup first: a 2.10 binary refuses the upgraded database, so going back means restoring it.
- Stop everything that opens the database. That includes
lific mcpprocesses your agents started, not only the service. API keys created, rotated or used under 2.11 switch to a SHA-256 verifier that older binaries cannot check, so a 2.10 process left running would start rejecting them. Upgrade the server and every CLI that shares the database together. - Very old unused keys are revoked. Keys created before Lific 1.0 that have not authenticated since are revoked by the upgrade. Every other key keeps working and is converted on its next use. Run
lific key listafterwards and replace any revoked key you still need. - MCP tools reject unknown parameters. Every tool now answers an unrecognized field with an error that suggests the right name, where it used to ignore it. Automation that sent extra fields must drop them.
- Backups run from stdio and the CLI. With
[backup]enabled,lific mcpover stdio and local data commands now write archives on the same schedule as the server, so an instance used only by agents starts getting backups. Restarting the server no longer forces one. cargo install lificworks without--lockedagain. 2.10.0 failed to build that way.
v2.10
No migrations. A 2.9 database opens unchanged, and a 2.9 binary can still open it afterwards. Take the usual backup anyway.
edit_commentis stricter. It gains exact string replacement throughold_stringandnew_string(withreplace_all), matchingedit_issueandedit_page;contentstill replaces the whole comment. A call that mixes the two modes, supplies neither, or passes an unknown parameter is now rejected. Update automation that sent extra fields and relied on them being ignored.- Newer MCP clients can connect over stdio. The stdio server answers a
server/discoverrequest sent beforeinitializewith a method-not-found error and keeps running, instead of exiting. - Optional authenticated client address from a proxy. A reverse proxy listed in
server.trusted_proxiescan send the client address inX-Lific-Client-IP, authenticated byX-Lific-Proxy-Secretagainst theLIFIC_TRUSTED_PROXY_SECRETenvironment variable.X-Forwarded-Forhandling is unchanged. If a trusted proxy sends either header without a matching secret, the rate-limited routes (sign-in, sign-up, password confirmation and change, OAuth client registration, device authorization and public reads) answer503instead of guessing. If you adopt this, set the variable on the server before the proxy starts sending the headers, and make the proxy strip client-supplied copies of both. - Firmer public-project limits. Public file downloads have their own limit of four at once, and public thumbnails and previews are generated one at a time. Excess requests get
503withRetry-Afterrather than waiting. - No framing. Every response now forbids being shown in a frame (
X-Frame-Options: DENYandframe-ancestors 'none'). A page that embedded Lific in an iframe stops working. - Builds. The Windows release binary is now built for
x86_64-pc-windows-msvc; the file name is unchanged. Release builds from source fail unless the web UI has been built first (web/dist/index.htmlmust exist). Debug builds are unaffected.
v2.9
Four migrations run when upgrading a 2.8 database:
| Migration | Change |
|---|---|
| 049 | Repository bindings and their identity aliases. |
| 050 | Project archive attribution and provenance. |
| 051 | Per-project publication, off by default. |
| 052 | Personal sidebar project ordering. |
Existing projects remain private. Sidebar ordering uses the existing ranks until a user reorders projects; that user's changes no longer rearrange other users' sidebars. To roll back after migration, stop the service and restore the pre-upgrade whole-instance backup with a compatible binary. Do not point a 2.8 binary at the migrated database or edit migration records to make it start.
Initialization is deliberate. Ordinary local commands, including bare
lific mcp and lific start, refuse a missing database and report its expected
path. An automation job must initialize with lific init, restore a backup,
or use the guarded first-boot path below. HTTP-backed commands and remote MCP
proxies still need no local database. If a previously working service suddenly
reports a missing database, check its path and mounted volume before creating
anything; a new empty instance is not recovery of the old one.
lific init --db records the actual absolute database path in its configuration
and reports conflicts with an already configured path. It can update an existing
writable config file even when the parent directory is read-only, as with a
container-mounted config. This does not make an unwritable file writable.
The supplied container image starts with start --init-if-missing. On an empty
volume, this requires a deliberately selected database location, an existing
writable parent directory and both environment variables:
LIFIC_INIT_ADMIN_NAMELIFIC_INIT_ADMIN_PASSWORD
Provide the password through your deployment's secret mechanism, not a committed configuration file or image layer. Bootstrap creates a password-protected administrator with signup and browser auto-login off, and does not print an operator key. Interrupted bootstrap can resume. An initialized instance that already has its admin ignores these variables; changing them does not reset that account's password. Remove bootstrap credentials from the deployment environment after confirming first boot. See self-hosting for the current container setup.
Diagnostics and credentials. lific doctor is read-only with respect to
database migration unless you pass --repair. Configuration errors remain
visible instead of selecting a default database. The CLI also refuses malformed
credential storage and unsafe filesystem paths rather than treating them as
an empty store or overwriting them. Preserve the failing file for diagnosis,
correct the reported format or filesystem problem, then retry login. Do not
silently replace it with an empty credential store in automation.
Account recovery through the CLI now stops protected WebSocket delivery without waiting for the idle revalidation timer. Credentials are revalidated before protected live deliveries, cached activity reads and replay frames. Reconnect affected agents and API clients after a reset using the 2.7 recovery procedure. Enabling browser auto-login through the CLI now applies the exposure checks used by the web API; disabling it remains available for recovery.
Comment clients must follow continuation metadata. Interactive comment responses have a 2 MiB byte budget as well as a row limit. A short page no longer means that a thread ended. REST exposes:
| Header | Meaning |
|---|---|
x-comment-has-more | Whether another page remains. |
x-comment-returned | The number of comments returned in this response. |
x-comment-next-offset | The next offset for offset-based pagination. |
x-comment-next-created-at, x-comment-next-id | The continuation pair for cursor-based pagination. |
Use the continuation for the paging mode you requested, rather than adding the
requested limit to your offset. For cursor paging, pass the returned pair as
before_created_at and before_id. A newest-first offset response can also
provide that pair so you can switch to cursor paging. MCP counts the encoded
response, including formatting, and reports continuation instructions too. An
individually oversized comment fails explicitly rather than disappearing from
the thread. Handle that error and its instructions; do not mark the thread
complete. Comment writes allow at most 256 distinct mention tokens and 256
distinct attachment IDs. Legacy oversized bodies can be edited without growing.
Imported comments can report user_id: -1. This means there is no destination
author account, not a user ID to resolve. Their displayed authors are inert
names marked (imported). A destination account with a matching username does
not gain authorship or permission over those comments.
Repository context and multi-instance routing. Repository bindings supply defaults only for the documented project-scoped MCP tools. Search remains cross-project when unscoped, workspace page creation remains available, and destructive tools do not inherit an implicit project. Explicit projects win. Restart stdio sessions after setting or changing a binding.
Multi-instance MCP requires complete tool definitions
to match across every backend at startup, including descriptions, annotations
and schemas. Equal version numbers or compatible input schemas alone are not
enough. Upgrade the backends together before restarting the proxy. With several
backends, writes require an explicit instance; only allowlisted reads may use
the configured default. Calls never fall back to another tracker after failure.
Resources and prompts are outside this routing mode.
Archives and publication are opt-in. A project archive creates a new private destination; it does not merge with an existing project, delete the source or transfer memberships and credentials. It includes historical text removed from current descriptions, so treat the file as private. If an import loses its connection, its outcome is unknown. Check the destination before retrying, since the server may already have committed it.
Publishing a project exposes its existing current issues, pages, comments and linked files to anyone. Review them before publication. Plans, history and trash stay outside the public read surface. Unpublishing stops subsequent requests but cannot recall downloaded material. Require instance authentication and disable browser auto-login before exposing the server; publication does not secure other routes for you.
v2.8
Upgrade CLI device-login clients before upgrading the server. Lific 2.8 requires a registered OAuth client for device authorization; a pre-2.8 CLI sends the old request and cannot log in to a 2.8 or newer server. A current CLI can still log in to an older server, so CLI-first is the compatible order.
OAuth clients and approvals. Automated clients using
POST /oauth/device_authorization must register at /oauth/register, then
send the returned client_id. Free-text client_name is no longer sufficient.
An omitted scope defaults to mcp; an explicitly different scope is refused.
lific login stores the registration in ~/.config/lific/clients.json, keyed
by server URL, and reuses it on later logins. This file contains no secrets.
Deleting it causes a new registration on the next login; do not delete it as
routine upgrade maintenance and consume registration slots unnecessarily.
Device codes issued before the server upgrade must be restarted. They have no
registered client attached and token exchange returns invalid_grant; codes
expire after 15 minutes. Approval now first shows the registered client and
requested access, then requires an explicit approval. Entering a device code
alone does not approve it. Authorization-code approval also requires an explicit
Approve or Deny choice, bound to the request displayed.
Both approval flows require a signed-in browser. Keep the recent-session rule from 2.7: sign out, sign back in and start a new connection if your approval session is too old. Existing account-recovery revocation rules still apply. PKCE clients must send valid S256 challenges and verifiers with the required 43 to 128 characters. Registered redirect URIs may not contain fragments.
Live sync and retained deletes. Issues, pages and comments now carry sync
sequence numbers. WebSocket clients can resume by cursor; sync_required
asks the client to backfill through the project's /changes endpoint. Fresh
clients bootstrap through /index. Existing clients may ignore the added
protocol fields, but custom clients adopting replay should persist and advance
the returned cursor rather than infer ordering from timestamps.
Deleting an issue, page or comment now retains a tombstone. The default
retention.trash_days is 30 days; the retention sweep permanently purges expired
rows. Maintainers can restore issues and pages through REST. There is no trash
UI in this release, and soft deletion does not replace a backup. A restore emits
ordinary create or update events, which older clients can receive.
Updates may include expected_seq to reject a stale write with HTTP 409 and the
current state. Clients omitting it retain last-writer-wins behavior. Adopting
sequence-based conflict checks is optional, not a new requirement on every
existing update.
Other client changes. SVG attachments download instead of rendering inline.
Module status writes must use backlog, planned, active, paused, done
or cancelled; previously stored free-text values are not a supported write
format. Attachment uploads and deletes can answer HTTP 503 during backup,
dump or restore, with Retry-After: 2; retry after that delay. Restore now
validates metadata, schema integrity and attachment bytes before installing an
archive, so a damaged archive that previously appeared usable may be refused.
v2.7
Go to 2.7.1, not 2.7.0. In 2.7.0 every issue page in the web UI failed to render, on every instance. 2.7.1 fixes it and changes nothing else. The two releases are otherwise identical, so everything below applies to both.
The largest upgrade since 2.0. Three things need an operator decision before you roll it out: server.trusted_proxies stops defaulting to loopback, an older binary will refuse to open the database once 2.7 has touched it, and account recovery revokes much more than it used to. Take the backup first; on this release it is the only way back.
Account recovery changed shape. Read this part before rolling out if anything automated holds an API key.
- API-key callers lose credential-minting permission.
POST /api/auth/keysandPOST /api/auth/botsnow require a browser session token created within the last 15 minutes and answer403 recent authentication requiredto anything else, API keys included. This is deliberate: a key that could mint another key outlived the revocation of the key it came from. A script that provisioned keys by presenting an existing key must move tolific key createon the server, which writes to the database directly.lific connectis unaffected for the same reason. - A password change or sign-out-everywhere now revokes far more than sessions. For the account and every connected-tool bot it owns: sessions, API keys, OAuth access tokens, unexchanged authorization codes, and uncollected device approvals.
lific user set-passworddoes the same. Plan for reconnecting tools and reissuing any API key a script depends on after a reset. Unbound operator keys are not affected. - A password change returns a replacement session in
token, and it is the only credential left that works. A REST client that changes a password on a user's behalf must store that token before its next request. The web UI does this for you. - A running stdio MCP agent stops at its next tool call once its key is revoked.
LIFIC_TOKENis revalidated on every tool call rather than only at startup. Expect agents to needlific connectplus a client restart after any recovery. - OAuth access tokens can no longer approve an authorization request or a device code, and the browser session that approves must have signed in within the last 15 minutes. Approve from a signed-in browser, signing in again first if prompted. A tool that scripted its own re-approval through a token it already held will stop working.
- Every access-expanding admin endpoint requires a recent browser session.
POST /api/users,POST /api/users/{id}/promote,POST /api/users/{id}/reactivate,PATCH /api/instance/settings,POST /api/projects/{id}/members, role increases throughPATCH /api/projects/{id}/members/{user_id},PUT /api/projects/{id}when it setslead_user_id(which grants a lead membership), andPOST /api/projectswhen it names a lead other than the caller. Creating a project with no lead, or with yourself as lead, is unchanged, so automation that creates its own projects keeps working. Each answers403 recent authentication requiredto an API key or OAuth token, because each leaves access behind that a lockdown aimed at the calling credential cannot reach. Scripted user provisioning moves tolific user createandlific user promoteon the server. Reductions are untouched:demote,deactivate, role downgrades and member removal all still work from an API key, so containment stays fast. - Instance settings are gated as a whole, not field by field. A patch that only changes
instance_nameis refused from an API key just like one that turns onweb_auto_login. Classifying each field as expanding or not is a rule that has to be re-derived every time a field is added, and one miss reopens the hole. - An OAuth grant that is not bound to an identity can no longer be exchanged. Authorization codes and device approvals stored before 2.1 have no user attached; exchanging one produced an access token that named nobody and was therefore treated as the operator, and which no account recovery could revoke. Both exchange paths now return
invalid_grantfor these. Codes expire in ten minutes and device codes in fifteen, so in practice this only affects a client mid-flow across the upgrade: have it authorize again. Access tokens already issued are unchanged; revoke any unbound one you find withDELETE FROM oauth_tokens WHERE user_id IS NULLand reconnect the tool.
Proxies must be listed explicitly.
server.trusted_proxiesnow defaults to trusting nothing. In 2.2 through 2.6 it trusted loopback peers by default. A forwarded client address is now believed only when the connecting peer is in the list. If your proxy reaches Lific over loopback, which includes Tailscale Serve and Funnel and a reverse proxy on the same host, add it to the list, or every client behind it shares one rate-limit bucket. List only proxy networks you control.
Rolling back is now a restore, not a binary swap.
- Five migrations run automatically on first start: bot-identity uniqueness (which merges any duplicate agent identities into the oldest, keeping memberships, groups, and saved views), case-insensitive project identifiers, the account active flag, attachment dimensions and alt text, and the attachment search index. The
_migrationstable also gains a checksum column, backfilled from the migrations this binary carries. No manual steps. - An older Lific will refuse to open a 2.7 database. Migrations only run forwards, so an older binary used to find the version already stamped, apply nothing, and serve a schema it was never compiled against. Startup now fails with an error naming the database's schema version and the highest the binary supports. To go back, restore the pre-upgrade backup.
- A hand-edited migration file is now a startup failure. Applied migrations are checksummed, and a stored hash that no longer matches names the migration and both digests. If you have ever patched a migration in place, restore the original before upgrading and put the correction in a new migration.
[backup] retain = 0andinterval_minutes = 0no longer do what they said. Zero retention deleted every archive seconds after writing it, and a zero interval panicked the backup task on its first tick, silently ending backups while the server kept serving. Both now warn and fall back to the defaults, 24 archives every 60 minutes. Setenabled = falseto turn backups off.
Bounded responses. Every surface that could return an unbounded result is now paged or capped. A client that relied on getting everything in one call needs to page.
- Comment lists default to 50 and cap at 500.
GET /api/issues/{id}/commentskeeps itsorder=ascdefault, so an unqualified read is now the oldest 50; page withoffset, or passorder=desc. The MCPlist_commentstool andlific comment listdefault to the 50 newest instead, so a script that expectedlific comment listto print a whole thread oldest-first needs--order ascand its own offset loop.get_issuewithinclude_comments='all'returns at most the 500 most recent. - A comment body is capped at 256 KiB, measured after newline and tab normalization. Existing longer comments stay readable.
- Search is bounded and scoped. Page size defaults to 20 and caps at 500, offsets clamp at 100,000, a full-text query over 4 KiB or a literal query over 256 bytes returns 400, and a literal search stops at 10,000 matches. Results from projects the caller cannot see are gone, so counts may legitimately drop; filtering by an invisible project now returns empty rather than an error that revealed it exists.
- Exports are bounded. 10,000 files, 1,000 comments per issue, 100,000 per project, 50,000 metadata items, 8 MiB per file and 128 MiB total. Previously an export asked for at most 10,000 issues and then loaded every comment on each with no limit, so a large project's export was bounded only by available memory; one past the new ceilings now fails with an explicit error. Two run at a time; the rest get 429 with
Retry-After: 30, which an export client should honour. The HTTP download is dropped after 30 seconds idle or 30 minutes in total. - Websocket clients are capped at 16 sockets per user and 1,024 per instance, with 16 KiB messages, 4 KiB frames, 64 messages per 10 seconds, and a 5-second send timeout. Clients that only listen are better off: the server now sends protocol pings every 30 seconds, so they are no longer dropped after 120 seconds for skipping an application heartbeat.
Other behavior changes.
- OAuth access tokens are refused on every credential-management route, which covers API key create/list/revoke, connected-tool management, password change, session revocation, profile edits, user administration, and OAuth client and token management. A connected tool that called those endpoints gets a 403 and needs a browser session or a local CLI command. Ordinary reads and writes through MCP are unchanged.
- Editing or deleting a comment now requires access to its project, not just authorship, on both REST and MCP. Someone removed from a project loses the ability to mutate the comments they left in it.
- Project identifiers resolve case-insensitively across CLI, REST, and MCP, matching modules, folders, and usernames. Creating
abcwhenABCexists is now rejected, and a database that already holds a case collision (only reachable by raw SQL) has them renamed deterministically on upgrade. - Passwordless startup and enabling browser auto-login require a loopback bind and an absent, empty, loopback, or private-network
public_url. The guard used to look only at the bind host, which passed a loopback bind published through Tailscale Funnel or a same-host proxy. To recover an exposed instance, runlific instance set --auto-login falseand set[auth] required = truebefore restarting. - The CLI refuses to send an API key over plaintext HTTP to a remote host. What was a warning is an error. Use
https://or a loopback target.LIFIC_TOKENis now sent only to the origin named inLIFIC_URL, so a token plus a--urloverride, or a directory holding alific.tomlfor a different server, falls back to stored credentials for that host. - Uploads are validated by magic bytes rather than the declared content type, capped at 10 MiB by default, and rate-limited to 30 per user per 10 minutes. A client uploading a type outside the allowlist is refused.
- MCP tool errors are generic. Raw database text, which could carry table and column names and the database path, no longer reaches the client; it stays in the server log. A script matching on that text needs another signal.
v2.6
- Two migrations run automatically on first start. They add the per-tool identity plumbing behind connected agents, and there are no manual steps.
- A configuration file that exists but cannot be read or parsed is now a fatal startup error, and unknown keys are rejected. A missing file still starts on the built-in defaults. Check that
lific.tomlparses before the rollout;lific doctoris the tool for it. - A server with
web_auto_loginenabled refuses to start when[server] hostis not loopback. v2.7 also checksserver.public_url. - REST authentication failures are uniformly
403with the messageauthentication required. Endpoints for profile, password, sessions, API keys, connected tools, and comments previously answered400for this case. A client that reads400from those endpoints as "signed out" must key on403instead. - Remove
secure_cookiesfrom[auth]if your config sets it. It has never been read from the file, and unknown keys are now an error rather than being ignored. lific initpresents an auth-mode choice on a fresh install and creates the first administrator directly. An existing instance with users skips this entirely.- Connected AI tools have identities of their own. Audit entries for their writes are attributed to the tool rather than to the operator who connected it.
v2.5
- No migrations. Identifiers render as Markdown links in MCP, CLI, and REST output;
server.public_urlpins the link base, and Lific otherwise derives it from an allowlisted request host.
v2.4
- One migration runs automatically on first launch. Project groups start empty for every user, so the sidebar renders as before until you create one.
v2.3
- The CLI can run data commands against a running server with
--backend http, using--urlorLIFIC_URLand--api-keyorLIFIC_API_KEY. The direct SQLite backend remains the default. - OAuth discovery works on an instance with no
server.public_url. An explicitpublic_urlis still authoritative.
v2.2.1
- The three export MCP tools are now one
exporttool. Replace calls toexport_issue,export_pageorexport_projectwithexport. It dispatches by identifier:PRO-42for an issue,PRO-DOC-3for a page, and barePROfor a project. - The MCP surface is 27 tools. Clients that assumed the older 29-tool count or the removed export tool names must refresh their discovery data.
v2.2
- Browser views use credentialed realtime invalidation. Sessions are revalidated, connections are capped per user, and reconnects trigger a resync.
server.trusted_proxiescontrols when forwarded client-IP headers are trusted for rate limiting. In this release it defaults to trusting loopback peers; v2.7 changes that default.- MCP output is compact by default. Use the documented opt-in fields such as
include_closed,include_comments, orecho_treewhen an agent needs the full view. - MCP search supports literal mode, comments are searchable, and list/comment operations expose pagination hints.
- The web UI adds list sub-tabs, sidebar recents, touch page movement, PWA install metadata, and improved command-palette results.
- Auth-optional mode now reaches the browser through the instance auto-login flow; with no accounts, the signup screen still appears.
v2.1
lific membermanages project membership and roles from the CLI.lific user set-passwordprovides an operator password reset and invalidates the user's sessions.--configis honored byinitand service installation, and configuration/database discovery follows standard OS directories.- Private instances may set
[auth] required = false; this is a shell-controlled operator setting and must remain local or firewalled.
Authorization rollout
An existing 2.0 instance keeps its persisted authorization setting during the upgrade. Inspect it before changing anything. With enforcement on, viewers can read and comment, maintainers can mutate project content and structure, and leads manage settings, membership, and deletion. Cross-project relations and plan-step links require the appropriate role in every project involved.
To change the setting deliberately:
lific instance set --authz-enforced trueBefore enabling it, ensure every human and bot that needs access has a project membership, and keep an operator-trusted key available for recovery. To roll back the setting on a private instance, use --authz-enforced false and then investigate the missing membership or role rather than distributing a broader key.
For a private local deployment, [auth] required = false is a separate setting from project authorization. It grants credential-less operator access and is unsafe anywhere others can reach it. Startup requires a loopback bind and an absent, empty, loopback, or private-network public_url. Treat both settings as operator changes that require shell access and a verification step.
If the upgrade fails
- Stop the service and keep its logs and the failed database as they are. Do not keep restarting it against your only copy of the data.
- Restore the backup with a binary of the version you were running before: into a separate data directory, or in place with
lific restore <ARCHIVE> --forceonce you are sure of the archive and the target.--forcemoves the current database aside rather than deleting it. - Run
lific doctor, compare the configuration and client credentials with what you recorded, and only then send traffic back.