Portability and maintenance
Long-lived project memory needs a transport format, recoverable snapshots, and bounded cleanup. These operations intentionally cover different subsets of local state.
JSONL export and import
knowl export ./knowl-export.jsonl
knowl import ./knowl-export.jsonl --dry-run
knowl import ./knowl-export.jsonl --on-divergence newer
The checksummed JSONL export is at format version 2. It includes complete item objects in
every status, with originRepo, visibility and lifecycleHash written on import as well as
read on export. It also includes assertions, evidence and links, file-backed skill files, and
tombstones. It excludes knowledge commits, access telemetry, sessions, code indexes, vector
embeddings, project configuration, workspace manifests, and workspace membership.
This build reads format versions 1 and 2. A version-1 file imports with ownership defaulted —
originRepo null and visibility repo — which is what a file written before those fields
existed means. A version it does not recognise is refused rather than imported with the unknown
fields dropped.
Import only JSONL that you created or otherwise trust. The checksum detects corruption; it does
not authenticate the source or make malicious skill-package paths safe. --dry-run checks the
database import plan but returns before skill-package files are validated or written.
After checking the checksum, header, and item records, import supports four divergence policies:
newercompares an incoming item with the same ID and differentcontentHash, then chooses byupdatedAt, by version when timestamps tie, and otherwise keeps the local tie.skipkeeps the local divergent item.theirsselects the incoming item.failaborts the import when divergence is found.
Use --dry-run to see wouldApply counts without applying records. Only fail treats a
divergence as an import-wide abort condition; the other policies select or skip individual
records. When newer or theirs selects an incoming item, Knowl writes supported content and
history columns with the incoming contentHash, version, and timestamps without normal
reconciliation.
Content and lifecycle diverge independently. contentHash covers title, content, reasoning,
source and paths; lifecycleHash covers status, freshness, supersession, originRepo and
visibility. An item whose content matches but whose lifecycle does not is metadata-divergent,
resolved by the same policy and applied to the lifecycle columns only, leaving contentHash
untouched so the next round classifies as identical instead of trading updates. A promotion,
retirement or supersession therefore propagates; before this it did not, because content-only
comparison called it identical and skipped it. Promotion also advances updatedAt, since newer
has nothing to order by otherwise.
After checksum, header, and item validation, non-dry-run database changes apply in one SQL transaction and roll back together on failure. Imported skill-package files are filesystem writes and are not covered by that database rollback.
Tombstones are monotonic. deletedAt only moves forward, whether written locally or received in
an import, so replaying an older delete cannot rewind a newer one. Import also consults local
tombstones before inserting: an item whose export predates a local delete is not reinstated, and
the count is reported as blockedByTombstone rather than folded into identical. A tie favours
the item, matching the delete path, so knowledge deliberately re-recorded after a delete still
lands. Best-effort vector indexing after import uses only the locally available model.
Garbage collection
knowl gc previews by default:
knowl gc
knowl gc --apply
The default policy:
- removes exact active duplicates only in
fact,state, andgoal; - archives non-hot active
stateolder than 60 days; - protects an item as hot after at least three retrievals or a retrieval within 21 days;
- compresses archived content after 30 days when it is at least 180 bytes; and
- removes tombstones after 90 days.
Flags adjust these thresholds, and --ignore-access disregards access heat for stale-state
archival. Review the preview before applying it; GC does not infer semantic equivalence across
different content.
The forget log
Every destroyed item leaves one append-only row recording what was true at the instant of destruction: the policy that fired, its reason in words, and the retrieval evidence that policy decided against. That makes a collection threshold checkable after the fact — you can ask which items were taken while they were still being retrieved.
knowl forget-log
knowl forget-log --limit 100 --json
knowl forget-log --repo web # only items owned by that workspace repo
knowl forget-log --prune-days 365
This is a separate table from knowledge_tombstones and it never leaves the machine. A tombstone
rides in every portable export and merges by upsert on import, so retrieval numbers there would
both publish local telemetry and let a peer's import overwrite this machine's audit trail.
Tombstones are pruned after 90 days on every GC run; forget-log rows are kept until
--prune-days asks for them to go, because the question they answer arrives months late.
Snapshots, audit, and doctor
knowl snapshot create
knowl snapshot restore .knowl/snapshots/<snapshot>.db --confirm
knowl audit
knowl doctor
Snapshot creation uses SQLite VACUUM INTO and writes a checksum manifest. The manifest is
required on restore, not optional: restore verifies its schema version, byte size and
SHA-256, copies the snapshot out of the snapshot directory, and re-verifies the copy it is
about to read — then checks that copy's own SQLite integrity_check and user_version before
any destructive statement runs. Restore also takes a pre-restore snapshot first, and refuses to
delete the snapshot it was asked to restore from.
Restore is a partial operation, and which half is which is a decision recorded in
src/store/snapshot-tables.ts rather than a side effect of the schema. Restored: knowledge
items, assertions, evidence and its links, access telemetry, knowledge commits and the
commit-to-item index, skill rows, and embeddings. Preserved at their current values: memory
sessions and their events, host bindings, tombstones, MCP call watermarks, the code index, and
the drift watermark — each describes the machine and working tree you are on now, not the
knowledge. Full-text search indexes rebuild themselves as rows land. A test fails if any table
in the schema is missing from that registry, so restore behaviour cannot drift by accident.
A restore runs an audit after committing the restored data. An audit failure is diagnostic and
does not roll back that commit. knowl audit itself is read-only and performs a limited set of
secret, JSON/status, dangling-row, and FTS checks; it is not a complete reconstruction of every
history or evidence invariant.
knowl doctor checks initialization, configuration, guidance, .gitignore, integrity, schema,
stale sessions, retrieval, the MCP inventory, agents and lifecycle registration, vector
coverage, and workspace health. Warnings mean the project is not reported ready; they should not
be treated as an all-clear.
Startup diagnostics
knowl diagnose-startup reports why knowl serve startups were slow: per-phase timings, SQLite
contention, stalls and host kills.
knowl diagnose-startup
knowl diagnose-startup --since 12
knowl diagnose-startup --clear
The log lives under the Knowl home rather than in any repository, because the question it answers spans every project on the machine. Each record holds a boot id, PID, elapsed and per-phase timings, Node version, hostname, load average, free memory, and a 16-character hash of the project root — never the path itself, and never environment variables or command-line arguments. On a shared host the path would have told every local account which projects you work on and when; the hash still answers "was this the same project?" and "were several servers stalling together?".
The file is capped at 4MB, created owner-only (0600, in a 0700 directory), and removable
with --clear. Set KNOWL_DISABLE_STARTUP_TRACE=1 to turn it off entirely.
Update checks
knowl status and knowl doctor ask the npm registry whether a newer Knowl exists and print a
line when there is one. status caches the answer for a day; doctor always asks, because a
diagnostic that reports a stale version is worse than one that takes an extra moment. The request
times out after two seconds and fails silently, so being offline costs nothing.
Only those two commands check. Hooks, MCP, and knowl serve never do.
knowl config set updateCheck.enabled false # this repository
KNOWL_NO_UPDATE_CHECK=1 # one invocation, or exported
NO_UPDATE_NOTIFIER=1 # the cross-tool convention, also honoured