Without ZINN_DIR, Zinn reads the global ~/.zinn/settings.json and the nearest optional zinn.local.json in a codebase. They can choose the data directory, a default project, how long a write waits for a busy database and the telemetry mode. The global file can also set the telemetry limits and configure hooks. With ZINN_DIR, Zinn reads only settings.json inside that selected directory. Note that storage.directory does not cause settings from its target directory to be read.
For the files and paths that make up a store, see Data and backups.
Choose the data directory
The first configured location wins:
ZINN_DIRselects that directory for the operation.storage.directoryin the nearestzinn.local.jsonselects a directory for the current codebase.storage.directoryin~/.zinn/settings.jsonselects the global directory.- If none is set, Zinn uses
~/.zinnas its directory.
If a ZINN_DIR is provided, Zinn reads only settings.json inside that directory. It does not read the home or codebase settings files. While the selected settings.json can set defaultProject, ZINN_DIR itself selects the directory.
Without ZINN_DIR, Zinn looks for zinn.local.json from the current directory up to the Git root. Outside a Git repository, it checks only the current directory. It reads the global ~/.zinn/settings.json too, then merges the local file over it field by field. A local setting overrides the same global setting. You can omit fields in the local setting to inherit global values. The local settings file is not meant to be committed, as it can cause unexpected behavior on others’ Zinn setups who install the same repo with the tracked local setting.
storage.directory is relative to the folder containing the file that sets it. A leading ~ or ~/ expands to your home directory. It names the Zinn directory, which contains data/zinn.sqlite, backups/, logs/, and telemetry/.
The selected directory holds one database shared by all its projects. Choose a local directory outside network shares and synchronized folders. See safe storage and backups.
A folder selected through ZINN_DIR or storage.directory must already exist. Create it with mkdir -p <path> before using it. Creating the first project or label creates the database inside an existing empty folder. With no explicit location, it also creates the default ~/.zinn folder. Until then, other commands fail with code not_found and entity database and create no database. In an existing folder, the failed run is still recorded in telemetry/ unless telemetry is off. An empty ZINN_DIR is refused. Help, version requests, and zinn info work with a missing folder.
For example, this zinn.local.json selects a shared data directory and its default project:
{
"$schema": "https://zinn.sh/schemas/settings/v1.json",
"defaultProject": "MDR",
"storage": {
"directory": "~/work/zinn-data"
}
}
The $schema field is optional and gives editors a schema for autocomplete and validation. Zinn ignores it. Settings files are JSON without comments. Unknown keys, invalid JSON, and values of the wrong type stop normal commands with an error naming the file. Zinn does not fall back to defaults. A settings version changes only when a change would make existing settings files invalid. When ZINN_DIR is set, malformed home and codebase settings are not read.
Choose a default project
defaultProject names the project used when a command supports omitting its project key. Project keys are matched case-insensitively. An explicitly typed key takes precedence, then zinn.local.json, then the global settings file. With ZINN_DIR, only the selected directory’s settings.json can provide the default.
The default applies to task create, task list, task search, project view, project column list, project edit, and project delete when their project key is omitted. A null value is not accepted. Without a default, task list and task search cover every project; the other commands require a key. A configured default that names a missing project causes a command that uses it to fail and identify the key and settings file. --all-projects makes task list and task search cover every project even when a default is configured; it works with --all or --archived and cannot be combined with a typed project key.
The defaultProject setting applies only to commands that support an omitted key. For example, task-key commands use the project in the task key, and column creation, editing, and ordering require an explicit project key. See the CLI reference for command details.
Wait for a busy database
One command at a time can write to a database. A command that needs to write while another holds the lock waits, then fails with a storage error with reason busy if the lock is still held. busyTimeoutMs sets how long it waits, in milliseconds:
{
"busyTimeoutMs": 60000
}
It is a whole number from 0 to 2147483647 and defaults to 30000. 0 fails at once instead of waiting. zinn.local.json can set it, and the nearest one overrides the global file.
Run hooks after changes
hooks in the global settings file (~/.zinn/settings.json, or settings.json inside ZINN_DIR) lists commands that Zinn runs after each saved change:
{
"hooks": {
"notify": { "command": "sh ~/.zinn/hooks/notify.sh", "timeout": 10 }
}
}
Hooks run commands, so they are read only from the global settings file. A hooks key in zinn.local.json is a settings error like any other invalid setting, and hooks never run from the local settings file. See Hooks for what a hook receives and how failures are handled.
Control local telemetry
Zinn records local usage by default and sends nothing off the machine. See Telemetry for the records and their limits. The telemetry key has three fields, and a file can set any of those it is allowed to:
{
"telemetry": {
"mode": "off",
"maxMegabytes": 50,
"maxAgeDays": 365
}
}
modeislocaloroff.offwrites no new records and keeps existing files. Without a setting,modeislocal, oroffwhen theDO_NOT_TRACKenvironment variable is1, following donottrack.sh. Only the value1counts. Atelemetry.modein a settings file overridesDO_NOT_TRACK, whichever way it is set.maxMegabytesis a positive whole number of megabytes, each 1,048,576 bytes. It limits the total size of the record files and defaults to100.maxAgeDaysis a positive whole number of days to keep records. It defaults to90.
zinn.local.json can set mode, and the nearest one overrides the global file. A mode applies only to records written by commands run in that codebase. maxMegabytes and maxAgeDays are read only from the global file, because the records they delete can come from any codebase. Either limit in zinn.local.json is a settings error like any other invalid setting. With ZINN_DIR, only that directory’s settings.json is read, and it can set all three fields.
Inspect the selected store
Use zinn info to inspect the selected store and settings without initializing or upgrading the database:
zinn info
zinn info --json
The report includes the selected store directory and source, settings files and their state, default project, telemetry mode and folder, database path and schema state, and CLI, core, and runtime versions. Settings states are found, missing, error, or not-read when ZINN_DIR selects a different settings file. Database state is absent, empty, unmarked, current, older, newer, or unavailable.
zinn info --json returns one object with stable top-level keys:
settingsFileslists each inspected settings file with itspath,source(env,global, orlocal),state, and either its parsedsettingsor anerror. Unread home and codebase files withZINN_DIRhave statenot-readand reasonZINN_DIR.storagecontains the selecteddirectory, itssource(env,local,global, ordefault), thesettingsPaththat chose it (ornullforenvanddefault), whether it exists, and anerror.defaultProjectcontains itskey, thesettingsPaththat supplied it, and anerror; the key and path arenullwhen there is no configured default.telemetrycontains the resolvedmode(offorlocal), itssource(default,DO_NOT_TRACK, orsettings), thesettingsPathof the file that set the mode (nullunlesssourceissettings),maxMegabytes,maxAgeDays, thedirectoryof the telemetry folder, whether itexists, thesizeBytesof the files directly inside it, and anerror. A file that sets onlymaxMegabytesormaxAgeDaysdoes not changesource. The size is0when the folder is missing, becauseinfonever creates it. A path that is not a folder is reported withexistsfalse,sizeBytesnulland anerror.databasecontains the databasepath, itsstate, itsversion, thelatestVersionsupported by this CLI, and anerror. The version is0forabsent,empty, andunmarkedstates.versionscontainscli,core, andruntime(nameandversion).
database.path resolves existing symbolicabsolute links and relative paths to an absolute destination. storage.directory records the configured directory. If database-path resolution fails, the report keeps the requested database path with state unavailable and an error.
For example, when settings and the database can be inspected, the report has this shape:
{
"settingsFiles": [
{
"path": "/home/me/.zinn/settings.json",
"source": "global",
"state": "found",
"settings": { "defaultProject": "DEMO" }
},
{ "path": "/work/app/zinn.local.json", "source": "local", "state": "missing" }
],
"storage": {
"directory": "/home/me/.zinn",
"source": "default",
"settingsPath": null,
"exists": true,
"error": null
},
"defaultProject": { "key": "DEMO", "settingsPath": "/home/me/.zinn/settings.json", "error": null },
"telemetry": {
"mode": "local",
"source": "default",
"settingsPath": null,
"maxMegabytes": 100,
"maxAgeDays": 90,
"directory": "/home/me/.zinn/telemetry",
"exists": true,
"sizeBytes": 21345,
"error": null
},
"database": {
"path": "/home/me/.zinn/data/zinn.sqlite",
"state": "current",
"version": 2,
"latestVersion": 2,
"error": null
},
"versions": {
"cli": "<installed CLI version>",
"core": "<installed core version>",
"runtime": { "name": "Node", "version": "<runtime version>" }
}
}
When settings are unusable or a database cannot be inspected, info reports the errors and exits with code 0. unavailable means settings left the store undecided (path and version are null), or the selected file could not be read as a database (path is set, version is null, and error explains why). Every error is null when there is no problem. A settings error is carried on storage, defaultProject, telemetry, and database as well as its settingsFiles entry, so the same string can appear several times. The fields a settings file could have set are null. With ZINN_DIR, an unusable selected settings.json sets only defaultProject.error and telemetry.error, since the store is already chosen. SQLite may perform normal file housekeeping and crash recovery while info inspects a database.