Skip to content

CLI reference

kdbl-control is the command-line interface to KDBL Context Lake (K-Lake). Most commands call the same REST API as the UI (so they respect tenant isolation and the audit trail), but a few run in direct-DB mode — see the per-command notes below.

Installing

The CLI ships as a static binary inside the K-Lake container image at /usr/local/bin/kdbl-control. Pull it from the image you run in your cluster, or ask your administrator for a host binary.

Once on PATH:

kdbl-control --help

Authentication

The CLI has two modes:

Mode Flags When to use
API --api-url <URL> --api-token <PAT> Day-to-day. Goes through the REST API and respects tenant isolation. Required by init, onboard, doctor, source extract / security-trim / multichannel / schedule, mcp, and status.
Direct database --postgres-url <URL> (+ KDBL_MASTER_KEY for secrets) Bootstrap, disaster-recovery, and the directory and tenant families (the API process holds no master key, so storing encrypted directory secrets is direct-DB only).

For most work, set:

export KDBL_URL=https://kdbl.example.com
export KDBL_TOKEN=kdblpat_...

and pass --api-url "$KDBL_URL" --api-token "$KDBL_TOKEN" (or rely on the environment if your build supports it).

Subcommands

crawl

Enqueue a crawl for a source. Workers pick up the work and expand it from the root prefix.

kdbl-control crawl --source-id 's3://my-bucket'
kdbl-control crawl --source-id 's3://my-bucket' --prefix 'reports/2026/'

Flags:

  • --source-id <ID> or --bucket <NAME> — one is required (--bucket is a shorthand for an S3 source)
  • --prefix <PATH> — narrow the crawl to a subtree (default: full source)
  • --mode <hierarchical|flat> — listing strategy (default hierarchical)
  • --label <NAME> — tag the run for later auditing
  • --force-reextract — re-extract every eligible file, even unchanged ones (default off)

Recrawls skip unchanged content. By default a recrawl only extracts files that are new or changed since they were last extracted — unchanged content is not re-run, because extraction is GPU-heavy. "Changed" is decided per file by its etag (S3) or, for sources without one (nfs/smbfs/smb), its mtime+size. Use --force-reextract for a deliberate full re-run, e.g. after upgrading the extractor or changing the extract policy. The kdbl_extract_skipped_unchanged_total metric counts how many files a recrawl skips.

status

Show queue depth and per-source rollup.

kdbl-control status
kdbl-control status --include-cluster   # cluster-wide, requires admin

doctor

Health-check a deployment end-to-end and print a // checklist: API liveness (/healthz) and readiness (/readyz), token validity + kind, the queue/source rollup, the extractor fleet, and whether the MCP endpoint is enabled. Use it as the "you're done" gate right after scripts/bootstrap.sh, or any time to triage. API mode only.

kdbl-control --api-url "$KDBL_URL" --api-token "$KDBL_TOKEN" doctor
kdbl-control --api-url "$KDBL_URL" --api-token "$KDBL_TOKEN" doctor --mcp   # also smoke-test /mcp
kdbl doctor — https://kdbl.example.com

  ✓ API reachable — /healthz 200
  ✓ API ready (DB reachable) — /readyz 200
  ✓ token valid (cluster-admin)
  ✓ queue reachable — pending=0 running=0 done=0 failed=0
  • no sources indexed yet — next: add one with `source add-*` + `crawl`
  ✓ extractor fleet healthy (2/2)
  ✓ MCP endpoint enabled — PRM 200

✓ all checks passed

marks a critical failure (unreachable, not ready, bad token, MCP smoke failure) and makes doctor exit non-zero — so it's CI/scriptable. flags something degraded (failed queue tasks, sources with errors, a stale extractor heartbeat); is informational and expected on a fresh cluster (no sources yet, extraction not deployed, MCP off) — neither fails the gate. Works with a cluster-admin token (cluster-wide rollup) or a user PAT (tenant-scoped).

init

Interactive first-source wizard — the guided Day-1 path that does what source add-* + source extract enable + crawl do, but walks you through it and surfaces the bit newcomers miss: search needs extraction. It connects a source (S3 / SMB / SMBFS), offers to enable content extraction, and offers to start the first crawl. Runs as a tenant user PAT (a cluster-admin token has no tenant — onboard one first and use the PAT it prints).

kdbl-control --api-url "$KDBL_URL" --api-token "$KDBL_TOKEN" init
kdbl init — connecting your first source as alice@acme.com.

What kind of source do you want to connect?
  1) S3 / object store (s3://)
  2) SMB / Windows share — kernel mount (smbfs://)
  3) SMB / Windows share — userspace (smb://)
choice [1]: 1
S3 bucket: acme-docs
source id [s3://acme-docs]:
✓ source created
Enable content extraction? (full-text search needs extracted text) [Y/n]: y
✓ extraction enabled — PDFs/Office docs etc. are parsed as they're crawled
Start the first crawl now? [Y/n]: y
✓ crawl started (task 1042)

It prompts on a TTY (passwords are read without echo); for scripting you can pipe answers on stdin in order. NFS sources aren't offered here yet — register those with source add-nfs (direct-DB mode). When you'd rather not be prompted, the non-interactive equivalents are source add-*, source extract enable, and crawl.

source

Manage the source registry.

Subcommand What it does
add-s3 Register an S3 source
add-azblob Register an Azure Blob source
add-smb Register an SMB (userspace) source
add-smbfs Register an SMB source mounted via the kernel
add-nfs Register an NFS source
list List sources in your tenant
show Show a single source in detail
enable / disable Toggle the enabled flag
remove Delete a source and its indexed files
bulk-ingest Toggle the bulk-ingest fast path
meta-caps Choose which optional enrichments to gather (incl. ntfs_acl / nfs4_acl ACL capture)
backfill-meta Enqueue enrichment for files indexed before a cap was enabled
meta-coverage Show how many files have each enrichment populated
subtree Tune how large directory trees are split for parallel crawling (depth, split threshold)
security-trim Set the per-file trimming policy (--mode per_file\|source_only\|open)
multichannel SMB3 multi-channel on an smbfs source
extract Content-extraction control: enable / disable / show / backfill / progress
schedule Scheduled crawls/backfills: add / list / rm / pause / resume / run
progress Live crawl/extract progress for a source

See Sources for add-* examples and Enablement for extract / security-trim / schedule.

Tokens and users are managed through the REST API and the UI, not the CLI — there is no kdbl-control tokens or users command. Mint a PAT and manage tenant users from the web console (or POST /api/users, the user-create response returns the one-time token).

acl

Manage a source's access grants — who can see the source and at what role. This is the source-visibility gate; per-file trimming (source security-trim) is separate. API mode only; the token scopes grants to its tenant.

Subcommand What it does
list <source> List the grants on a source
grant <source> <principal> <role> Grant (or update) a principal's role — owner / editor / viewer
revoke <source> <principal> Revoke a principal's grant

<principal> is user:<id> or group:<id> — prefer group: grants (from directory enrichment) so membership drives access.

kdbl-control acl grant  's3://my-bucket' 'group:sales' viewer
kdbl-control acl list   's3://my-bucket'
kdbl-control acl revoke 's3://my-bucket' 'group:sales'

Grants have full parity across the CLI, the web console's Access grants card, and the REST API (/api/sources/:id/acl).

directory (direct-DB)

Build the alias graph that ties IdP identities to file-native principals (for per-file trimming). Runs in direct-DB mode and needs KDBL_MASTER_KEY to encrypt secrets; secrets come from env vars, never flags. See Directory enrichment.

kdbl-control directory set-graph --tenant acme --entra-tenant <id> --client-id <id>   # KDBL_GRAPH_CLIENT_SECRET in env
kdbl-control directory set-ldap  --tenant acme --url ldaps://dc:636 --bind-user 'CORP\svc' --base-dn 'DC=corp' --upn-rewrite corp.local=corp.com
kdbl-control directory sync-graph --tenant acme   # one-shot run
kdbl-control directory sync-ldap  --tenant acme

files

Browse a source's files and mint signed links that open the original file (re-fetched on demand from the source, access re-checked, audited). API mode only; the calling token's tenant scopes what's visible.

kdbl-control files list --source-id s3://docs-bucket --limit 50          # paginated by key
kdbl-control files show --source-id s3://docs-bucket --key reports/q3.pdf # detail + signed links
kdbl-control files link --source-id s3://docs-bucket --key reports/q3.pdf # print an inline preview link
kdbl-control files link --source-id s3://docs-bucket --key reports/q3.pdf --download   # attachment link

files link prints just the URL (so it's pipe-friendly); files show returns the full detail including preview_url / download_url. The links require the server to have signed downloads enabled (KDBL_DOWNLOAD_SIGNING_SECRET / KDBL_API_PUBLIC_URL / KDBL_INTERNAL_FETCH_TOKEN); they expire in ~15 minutes.

mcp

kdbl-control mcp smoke   # fetch PRM + run initialize + tools/list against /mcp
kdbl-control mcp audit --tool search_content --limit 50   # query the audit trail

extractors

kdbl-control extractors list   # extractor pods and their health

versions

kdbl-control versions          # what version each component is running

Every pod of a consistent deployment reports the same version, so a row that disagrees means an upgrade did not finish. The same view is on the web console's Fleet page.

entities

Browse the knowledge graph. Every query is trimmed to what the token can see, so documents returns exactly the files a content search would. API mode only.

kdbl-control entities search --query "acme" --kind ORGANISATION
kdbl-control entities show 17
kdbl-control entities related 17 --predicate WORKS_FOR
kdbl-control entities co-occurring 17
kdbl-control entities documents 17
kdbl-control entities intersect --ids 12,48,301
kdbl-control entities by-file --source 's3://docs' --key 'contracts/msa.pdf'
kdbl-control entities merge 482 --into 17     # fold a duplicate away
kdbl-control entities unmerge 482             # put it back
kdbl-control entities merged 17               # what was folded into this one

related is an edge the model asserted from the text; co-occurring only means the two entities appear in the same documents. Merges are reversible and reads are merge-aware, so an id list assembled before a merge keeps working.

enrich-coverage

kdbl-control enrich-coverage --source-id 's3://docs'

How many extracted files have been enriched, what is still pending, and how many the per-document work budget cut short. Use it after enabling the enrichment action to watch the backlog drain.

support

Generate and inspect diagnostic packs.

kdbl-control support pack --namespace kdbl          # the archive you send us
kdbl-control support pack --runtime-tests           # also test what actually works
kdbl-control support pack --level metadata          # most private
kdbl-control support preview                        # what a pack would contain
kdbl-control support diff before.tar.gz after.tar.gz
kdbl-control support history                        # who generated packs, and when
kdbl-control support key-id pack.tar.gz.sealed      # which key a sealed pack needs

pack collects K-Lake's own data through the API and the cluster half through your kubectl credentials — K-Lake holds no Kubernetes credentials of its own. --no-kubectl skips the cluster half. Nothing is transmitted anywhere: the pack is a file you inspect and send.

discover (tenant admin)

Data-estate analytics — "what data do we have?" Prints JSON (pipe to jq). See Discover.

kdbl-control discover overview                       # totals + searchability coverage
kdbl-control discover breakdown extension            # by dimension (rank by files)
kdbl-control discover breakdown size --metric bytes --limit 10
kdbl-control discover files 's3://docs-bucket' extension pdf   # files behind a bucket

breakdown <dimension> accepts extension | mime | language | size | age | owner | storage_class | source | exposure with optional --source, --metric {files|bytes}, --limit (max 100). files <source> <dimension> <value> pages with --limit (max 1000) / --after <cursor>. Cluster admins can add --include-cluster for a cross-tenant overview / source breakdown.

capacity

Storage cost and query-latency breakdown for the tenant — the base / lexical / hybrid storage split, chunk and embedding counts, retained-vector bytes, and per-mode query latency (the same figures as the console's Capacity & cost panel). See hybrid search cost.

kdbl-control capacity                    # tenant storage split + query latency
kdbl-control capacity --include-cluster  # cluster-wide (cluster admin)

onboard (cluster admin)

Stand up a new customer in one step: create the tenant (idempotent — reuses an existing one), create its first tenant-admin user, and mint that user's initial PAT. The token is printed once — hand it to the customer over a secure channel. This is the secure self-service path: it goes through the API's auth + audit boundary, so you don't need direct DB or kubectl access to onboard.

kdbl-control --api-url "$KDBL_URL" --api-token "$KDBL_CLUSTER_ADMIN_TOKEN" \
  onboard acme --name "ACME Corp" --admin-name "Jane Admin" --admin-email jane@acme.com
✓ created tenant acme (id=019eb528-…)
✓ created tenant-admin user Jane Admin (id=375e848c-…)

Admin PAT for acme (shown ONCE — store it securely):
kdblpat_…

Flags:

  • <slug> — tenant slug (positional); also the default display name. Keep it DNS/URL-safe.
  • --name <NAME> — tenant display name (defaults to the slug)
  • --admin-name <NAME> — the first tenant-admin user's display name (default admin)
  • --admin-email <EMAIL> — the admin user's email (optional)
  • --oidc-config-json <JSON> — inline OIDC config so the customer can federate sign-in from day one (optional)

API mode only, with a cluster-admin token. onboard needs --api-url/--api-token and the token must be the KDBL_CLUSTER_ADMIN_TOKEN. The admin API is sealed from the public MCP tunnel by the path-scoping gateway, so run it against the in-cluster API — e.g. kubectl -n kdbl port-forward svc/kdbl-api 18080:80 and --api-url http://localhost:18080.

The token is shown once and is never recoverable. Re-running onboard for an existing tenant is safe — it reuses the tenant and mints a new admin user.

tenant (cluster admin)

For the common case (new tenant + first admin + token in one shot) use onboard. The tenant subcommands below are the lower-level CRUD primitives.

kdbl-control tenant create --slug acme --name "ACME Corp"
kdbl-control tenant list
kdbl-control tenant show --slug acme
kdbl-control tenant retention --slug acme            # read the current override
kdbl-control tenant retention --slug acme --days 90  # set
kdbl-control tenant retention --slug acme --clear    # revert to the cluster default

TLS trust anchors — install an internal CA so sources can verify certificates a public trust store doesn't know. See TLS trust anchors.

kdbl-control tenant ca-cert add --name "Acme Internal Root" --file acme-root.pem
kdbl-control tenant ca-cert list      # subject, issuer and validity per anchor
kdbl-control tenant ca-cert remove <cert-id>

secret keygen

Generate a fresh master key for credential encryption (used during cluster bootstrap).

kdbl-control secret keygen

bench, bench-sink, bench-queries

Operator-side benchmarking helpers. See --help for each. These are diagnostic — they exercise the live system. Run them against a non-production tenant.

Exit codes

Code Meaning
0 Success
1 Generic error
2 Authentication failed
3 Authorization denied
4 Resource not found
5 Validation error (bad arguments)

Tips

  • Pipe secrets in over stdin (--secret-access-key-stdin, --password-stdin) so they don't appear in shell history or process listings.
  • Source IDs containing / are fine in CLI flags — kdbl-control handles URL encoding for you when talking to the API.
  • Run kdbl-control <command> --help for the full flag list on any subcommand.