Skip to content

CLI

khiipd is the primary way to drive Khiip. Most commands talk to a running daemon, so start khiipd serve first. khiipd <command> --help is always authoritative.

The daemon address

The client commands (capture / recall / refetch) resolve which daemon to talk to in this order (first match wins):

  1. explicit --host / --port flags
  2. KHIIP_DAEMON_URL (a full base URL, e.g. http://127.0.0.1:8500; passing either flag suppresses it entirely)
  3. [daemon] host/port in config.toml
  4. 127.0.0.1:8478

khiipd serve binds from the same chain minus the env varKHIIP_DAEMON_URL is a client dial address, never a bind address. See Configuration for the dual-use key and the allow_non_loopback gate.

khiipd serve

Run the FastAPI daemon.

FlagDefaultPurpose
--host[daemon] host in config.toml, else 127.0.0.1bind address
--port[daemon] port in config.toml, else 8478port
--log-levelinfodebug / info / warning / error
--reloadoffauto-reload on code changes (dev only)

A non-loopback host from config.toml additionally requires [daemon] allow_non_loopback = true — without it, serve binds 127.0.0.1 and warns. An explicit --host flag needs no key. See Configuration → binding beyond localhost.

khiipd capture <url>

Capture a URL via the running daemon.

FlagDefaultPurpose
--source-hintforce a source: x / web / wiki / pdf / youtube
--host / --portresolveddaemon address
--timeout120request timeout (seconds; slow sources like YouTube need headroom)

khiipd recall "<query>"

Semantic recall over the embedded corpus. Quote multi-word queries.

FlagDefaultPurpose
--limit10max results
--min-scoreno filterdrop hits below a cosine floor in [-1, 1] — see score interpretation
--viewleanhit shape: lean (flat id/score/title/url/excerpt rows) or full (nested {capture, score} records — the pre-0.2.4 output). Note: the CLI defaults to lean, but the daemon REST default is full (so the shipped Obsidian plugin keeps working)
--host / --portresolveddaemon address
--timeout30request timeout (seconds)

The lean view prints one flat row per hit — score, title, the capture id, the url, and a short excerpt (head of the primary text). full restores the pre-0.2.4 two-line score/url layout. A pre-0.2.4 daemon that doesn’t understand view=lean returns full nested hits regardless; the CLI renders those correctly.

khiipd refetch <capture_id> [dimension flag]

Re-run one dimension of a capture. The four dimension flags are mutually exclusive; with none of them, refetch re-fetches from the network and creates a new superseding capture (append-only). See the refetch guide for what each dimension does.

FlagBehaviour
(none)network re-fetch → a new superseding capture
--re-extractoffline: re-derive the typed payload from preserved source bytes, in place (no network)
--re-renderoffline: re-render the Markdown body from the existing typed payload, in place
--mediare-walk the media-fetcher registry in place
--waybackre-submit to the Wayback Machine in place
--forceoverwrite the vault note even if you hand-edited it since Khiip last wrote it (the edit guard otherwise refuses in-place rewrites with a 409); with --media it also re-attempts already-successful downloads. Only valid with an in-place dimension (--re-extract / --re-render / --media / --wayback) — --force with no dimension flag (network re-fetch) is rejected by the daemon with a 400 and the CLI exits 4
--timeoutrequest timeout (seconds; default 120 — re-extraction can be as slow as the original capture)

--media --force re-downloads only photo-class (httpx-fetched) media in full; video / gallery downloads (yt-dlp, gallery-dl) are NOT re-downloaded when the file already exists on disk, even under --force — those fetchers are pinned skip-if-exists because their writers aren’t atomic. --force still lets the media walk re-attempt items that previously failed or were skipped.

When a forced photo re-download returns changed bytes, the old binary is not destroyed — it is kept alongside the new one under a versioned name (<stem>.khiip-v<UTCstamp><ext>) and the fresh bytes land back at the original filename. Identical bytes are a no-op (the file is left untouched, zero churn).

khiipd validate

Check vault ↔ SQLite invariants — see the validate guide for what’s checked. Read-only unless you pass --fix --apply (below). Exits 0 when clean, 1 on violations.

FlagDefaultPurpose
--vault-pathfrom config.tomlvault root override
--db-pathfrom config.tomlSQLite path override
--jsonoffemit a machine-readable report instead of text
--fixoffpreview the re-link plan for vault_path_stale rows (notes you renamed/moved in your vault). Writes nothing without --apply
--applyoffwith --fix: apply the plan. Daemon-first (per-capture relink API); direct-SQLite fallback when the daemon is down. --apply without --fix is an argparse error

--fix only re-links vault_path_stale rows that have exactly one matching note by capture id in the vault. Rows with 0 matches (the note is gone — vault_missing) or ≥2 matches (duplicate ids — vault_id_duplicate) are counted as skipped and left untouched. After --fix --apply, validate re-runs and the exit code reflects the post-fix state (0 clean / 1 residual violations).

khiipd auth show | rotate

Manage the daemon API key (stored in ~/.config/khiip/auth.toml, mode 600):

  • khiipd auth show — print the key fingerprint
  • khiipd auth rotate — generate a new key

khiipd version

Print the package version and the active tier.

Exit codes

Scriptable across all commands:

CodeMeaning
0success (including a recall with zero results, and a clean validate)
1validate found invariant violations
2cannot reach the daemon (connection refused / connect timeout); malformed config.toml; validate given a missing vault/db path. (Mistyped flags also exit 2 — argparse’s convention.) Exception: validate --fix --apply does not exit 2 when the daemon is unreachable — it falls back to direct-SQLite and exits 0/1 on the post-fix result.
3API key mismatch (daemon returned 401) — try khiipd auth show
4any other daemon error (e.g. 404 capture not found, 409 conflict) — the detail is printed
5refetch given more than one dimension flag
6the request timed out after connecting — the daemon may still be finishing server-side; raise --timeout for slow sources
7any other transport error

The relink calls made by validate --fix --apply use the same exit codes: 3 (daemon 401), 4 (unexpected relink error — a non-200/404/409 status), 6 (timed out after connecting), 7 (other transport error). Per-link 404/409 during relink are skipped and reported, not fatal — the post-fix re-validate surfaces any residue.

Daemon-unreachable errors (exit 2) print the address they tried (noting when it came from KHIIP_DAEMON_URL) and link the Autostart page — so a rebooted machine stops costing you captures.