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:
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, acl, files, entities, classification, discover, capacity, extractors, versions, dead-letters, support, mcp, users, warehouse, tenant ca-cert / access-mode / download-links / quotas / usage / features / source-policy / export, and source extract / security-trim / multichannel / schedule / progress. |
| Direct database | --postgres-url <URL> (+ KDBL_MASTER_KEY for secrets) |
Bootstrap and disaster-recovery, and the directory family, which is direct-DB only. |
crawl, status, source CRUD and most of the tenant family work in either
mode: when --api-url is set they go through the API (auth + audit), and fall
back to the database when it isn't.
For most work, set:
and pass --api-url "$KDBL_URL" --api-token "$KDBL_TOKEN". Both flags also read
the environment directly — export KDBL_API_URL and KDBL_API_TOKEN instead
and you can drop them from every command.
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 (--bucketis a shorthand for an S3 source)--prefix <PATH>— narrow the crawl to a subtree (default: full source)--mode <hierarchical|flat>— listing strategy (defaulthierarchical)--label <NAME>— tag the run for later auditing--force-reextract— re-extract every eligible file, even unchanged ones (default off)--only-ext <EXT,...>— with--force-reextract, re-extract only files with these extensions (comma separated, case-insensitive, leading dot optional). Other files keep their stored version and are skipped if unchanged. Requires--force-reextract.
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-reextractfor a deliberate full re-run, e.g. after upgrading the extractor or changing the extract policy. Thekdbl_extract_skipped_unchanged_totalmetric counts how many files a recrawl skips.
Re-extract only some file types — for example only CAD drawings and models after an upgrade improves how they are read, or only PDFs:
# Only CAD files
kdbl-control crawl --source-id 's3://my-bucket' \
--force-reextract --only-ext dxf,dwg,stp,step,p21,ifc
# Only PDFs
kdbl-control crawl --source-id 's3://my-bucket' --force-reextract --only-ext pdf
A file's extension is the text after the last . of its name (so
plan.v2.DWG counts as dwg; a file with no dot matches nothing). The crawl
still lists the whole source (or --prefix); only matching files are forced.
Combine with --prefix to narrow by path as well.
status¶
Show queue depth and per-source rollup.
With --include-cluster, the report also lists any index an upgrade added
that is still being built in the background (for example 15/16 parts
built). It is empty once every one is complete; see
Upgrading.
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 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 |
add-onedrive |
Register a OneDrive / SharePoint source (Microsoft Graph, app-only) |
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, --fail-closed, --well-known-open for shares that grant Authenticated Users / BUILTIN\Users) |
metadata-rules |
Document dates and keyword fields (ticker, sector, analyst, note type) read from each file's path, first page or running header: show / set / clear / backfill / preview. See Document dates and metadata fields. |
access-check |
Why one user can or cannot see one file: --source-id … --key … --user <email\|id\|principal> replays the trimming decision (tenant admin). See Why can't this user see this file? |
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 |
action |
Manage the Smart Actions pipeline: catalog / list / add / set / reorder / remove |
search |
Hybrid-search control for a source: enable / disable / inherit / show |
version-tracking |
Opt in to file version history, so files revisions / files diff can show what changed. Off by default |
exclude-extensions |
Crawl-time exclusion — matched files are never listed, indexed, or extracted |
redaction-summary |
What redaction has masked in a source: files run, files masked, per-rule totals |
set-backup-intent |
SMB backup-operator intent on an smbfs source |
See Sources for add-* examples and Enablement for extract / security-trim / schedule.
Tokens are managed through the REST API and the UI, not the CLI. Mint a PAT from the web console — AI access (MCP) › Tokens › Mint an MCP access token, or press ⌘K / Ctrl+K and type
>mint — (orPOST /api/tokens; creating a user withPOST /api/usersreturns that user's one-time token).
users¶
Tenant users and their roles. API mode only; a tenant-admin token acts on its
own tenant, a cluster-admin token names one with --tenant.
kdbl-control users list
kdbl-control users promote --user alice@acme.com # tenant admin
kdbl-control users demote --user alice@acme.com
kdbl-control users role --user bob@acme.com --role reader # or operator
kdbl-control users warehouse-admin --user data-team@acme.com # warehouse admin
kdbl-control users warehouse-admin --user data-team@acme.com --remove
A warehouse admin administers the tenant's data warehouses
and changes nothing else; combine it with --role reader for a warehouse-team
account. See Console sign-in and roles.
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 |
|---|---|
search [text] |
Find a person or group by name, and print the exact principal to grant |
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.
Nobody memorises those identifiers, so acl search finds them by name and
prints the value to pass to acl grant:
kdbl-control acl search sales --kind group
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'
Groups your directory already knows about are searchable before anyone in
them has signed in, which is the usual order when onboarding a team. A result
marked (not-grantable) is a principal K-Lake knows about that has no
source-level form — granting it would silently do nothing, so no value is
offered.
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 after one hour by default.
tenant access-mode¶
Whether a newly-added source is visible to everyone in the tenant, or to nobody until access is granted. See security trimming.
kdbl-control tenant access-mode --slug acme # read, with impact report
kdbl-control tenant access-mode --slug acme --set automatic # future sources only
kdbl-control tenant access-mode --slug acme --set automatic --apply-to-existing
Switching the mode deliberately does not touch sources that already exist —
that needs --apply-to-existing, because it changes what people can see today.
The read form reports how many sources that would open, and how many of those do
not use per-file permissions.
tenant download-links¶
Who may open a citation ("open original") link, and how long one sign-in keeps
opening them: anyone_with_link, signed_in, match_user, or default (follow
the identity provider). API mode; tenant admin for their own tenant, cluster
admin for any. See Citation links.
kdbl-control tenant download-links --slug acme # read
kdbl-control tenant download-links --slug acme --set match_user
kdbl-control tenant download-links --slug acme --set default # follow the IdP
kdbl-control tenant download-links --slug acme --session-ttl 4h # 30m, 8h, 7d, default
warehouse¶
Data warehouses. API mode, with a tenant-admin or
warehouse-admin token; model check and model schema need no API.
kdbl-control warehouse list [--json]
kdbl-control warehouse show risk
kdbl-control warehouse probe risk # connectivity + identity
kdbl-control warehouse catalog risk --schema risk [--columns]
kdbl-control warehouse queries risk --since 24h --limit 200 [--status error]
# The semantic model, kept as code
kdbl-control warehouse model pull risk -o risk.json # the published model (--draft: the draft)
kdbl-control warehouse model check risk.json # offline: parses + structural checks
kdbl-control warehouse model validate risk risk.json # against the warehouse
kdbl-control warehouse model diff risk risk.json # what would change
kdbl-control warehouse model push risk risk.json # save as the draft
kdbl-control warehouse model publish risk [risk.json] [--force]
kdbl-control warehouse model sync risk # read a model kept in the warehouse now
kdbl-control warehouse model schema > warehouse-model.schema.json
# Accuracy against a golden question set
kdbl-control warehouse eval risk golden.json --expected-only
The warehouse commands use exit codes chosen for pipelines: 0 success,
1 an error, 2 the warehouse check raised warnings, and for
warehouse eval, 3 accuracy below --min-accuracy. See
Model as code and
Measuring accuracy.
mcp¶
kdbl-control mcp config # what's set, where each value came from, what's missing
kdbl-control mcp set --enabled true --resource-uri https://kdbl.example.com/mcp
kdbl-control mcp set --clear resource_uri # revert to the deployment default
kdbl-control mcp validate # check the configuration against reality
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
Connecting an identity provider, so people can sign in with your organisation's accounts:
printf '%s' "$IDP_SECRET" | kdbl-control mcp set \
--idp-issuer https://login.microsoftonline.com/<directory-id>/v2.0 \
--idp-client-id <application (client) id> \
--idp-client-secret-stdin
Read the secret from stdin, not --idp-client-secret. An argument lands in
/proc/<pid>/cmdline, where every process on the host can read it, and in your
shell history.
mcp config then reports whether sign-in is available and, if not, which of
issuer / client id / client secret is missing. It also prints the redirect URI to
register at your IdP — take it from there rather than typing it, because it must
match character for character and a mismatch fails at the IdP with an error that
never mentions K-Lake. See OAuth and identity providers.
config / set / validate replace editing deployment configuration and
restarting. Every setting — the endpoint address included — takes effect on
save, with nothing to restart or re-deploy. set needs a cluster-administrator
token, because these settings are shared by every tenant.
validate is the one to reach for when a client "can't connect" — it checks
whether the endpoint address is reachable from outside, whether tenants have an
MCP audience, and whether any sources are actually exposed.
Tenants and clients:
kdbl-control mcp tenants # every tenant, its endpoint and sign-in leg
kdbl-control mcp tenant-apply --tenant acme # connect a tenant (bare /mcp if free, else /t/acme/mcp)
kdbl-control mcp tenant-clear --tenant acme # take it off MCP
kdbl-control mcp client create --preset copilot --tenant acme # a Copilot connector bound to acme's endpoint
kdbl-control mcp client create --name 'K-Lake console' --tenant acme \
--redirect-uri https://klake.example.com/authn/callback # a public client for console sign-in
kdbl-control mcp client list
kdbl-control mcp client delete --client-id <id> # also revokes its sessions
client create --tenant binds the client to that tenant's endpoint and prints
that endpoint's URLs; the client is refused everywhere else. --insecure-redirect
accepts a plain-http redirect URI for an evaluation console without TLS. See
Multi-tenant deployments and
Console sign-in and roles.
dead-letters¶
Failed work: tasks that ran out of retries on any queue (listing, metadata,
extraction, Smart Actions) — what failed, why, whether a later crawl or result
already superseded it — and retry or discard them. The same view is the
console's Failed work page (see System health).
API mode only; dlq is a short alias.
kdbl-control dead-letters summary # totals, open vs superseded, by cause, source and day
kdbl-control dead-letters list --state open --since 7d # entries, newest first
kdbl-control dead-letters show --stream kdbl:list --id <id>
kdbl-control dead-letters retry --reason source_unreachable # dry run: prints the plan
kdbl-control dead-letters retry --reason source_unreachable --yes # do it
kdbl-control dead-letters discard --source 'smbfs://fileserver/share' --yes
kdbl-control dead-letters export --format ndjson --out failed.ndjson
Filter with --family (list, subtree, meta, extract, action),
--source, --reason (source_unreachable, access_denied, not_found,
unreadable_content, out_of_memory, abandoned_by_worker,
retries_exhausted, other), --since / --until (30m, 8h, 7d),
--state (open, superseded, all) or -q for a substring. retry and
discard change nothing without --yes; retry skips entries a later result
already superseded unless you add --include-superseded, and re-crawls listing
work (one new crawl per source). A tenant token sees its own tenant; a
cluster-admin token adds --scope cluster, and an MSP's home tenant
--scope partner (narrow either with --tenant <slug>).
extractors¶
versions¶
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 System health 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 ORG
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.
entities aliases manages the tenant's ticker aliases
(reads: any tenant user; writes: tenant administrator):
kdbl-control entities aliases list -q cordwell
kdbl-control entities aliases set CORD --name "Cordwell Digital Infrastructure" --exchange XLON
kdbl-control entities aliases rm CORD --exchange XLON
kdbl-control entities aliases import universe.csv # ticker,name[,exchange]; --replace for the whole set
source enrich-coverage¶
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 # which key this build seals to
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 Estate insights.
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 (defaultadmin)--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.
onboardneeds--api-url/--api-tokenand the token must be theKDBL_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:80and--api-url http://localhost:18080.The token is shown once and is never recoverable. Re-running
onboardfor 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
In direct-database mode tenant list prints one line per tenant:
sso= says whether the console's login page can start single sign-on for the
tenant: ready (issuer, client ID and audience are all set), incomplete(needs
…) naming what is still missing once an issuer is set, or off. oidc= only
says that some sign-in or directory configuration exists. With --api-url the
command prints the tenants as JSON instead.
Lifecycle — suspend, resume, export and delete a tenant. See Hosting many customers.
kdbl-control tenant suspend --slug acme --reason "contract ended"
kdbl-control tenant resume --slug acme
kdbl-control tenant export --slug acme --out acme.ndjson # the offboarding export
kdbl-control tenant delete --slug acme --confirm acme # asynchronous; not undoable
kdbl-control tenant show --slug acme # status, and purge progress
suspend refuses the tenant's users on every sign-in path and stops its
ingest, keeping its data; resume undoes exactly that. delete locks the
tenant out at once and purges it in the background; export first if the data
must be handed back. export exits non-zero if the stream ended without its
closing record, so a zero exit means a complete file.
Quotas and usage (API mode; see Licensing):
kdbl-control tenant quotas --slug acme # read
kdbl-control tenant quotas --slug acme --max-bytes 2TB --max-users 200 # set (replaces)
kdbl-control tenant quotas --slug acme --clear # unlimited
kdbl-control tenant usage --from 2026-09-01 --to 2026-09-30 --csv # daily usage, every tenant
kdbl-control tenant usage --slug acme --from 2026-09-01 --to 2026-09-30 # one tenant
A tenant-admin token may run tenant usage for its own tenant.
Licensed features per tenant (API mode) — which of the licence's paid features a tenant may use. By default a tenant has every feature the licence grants. See Licensing.
kdbl-control tenant features --slug acme # show
kdbl-control tenant features --slug acme --enable structured_sources
kdbl-control tenant features --slug acme --disable structured_sources
kdbl-control tenant features --slug acme --all # every licensed feature
kdbl-control tenant features --slug acme --none # no paid features
Source policy — what a tenant's sources may borrow from the deployment (sources with no secret that authenticate as the deployment's own cloud identity; NFS sources, which connect as the worker's uid/gid) and which hosts they may reach. No flags reads it. On a deployment hosting many customers, new tenants start with both refused. See Hosting many customers.
kdbl-control tenant source-policy --slug acme
kdbl-control tenant source-policy --slug acme --allow-nfs true
kdbl-control tenant source-policy --slug acme --allow-ambient-credentials false
kdbl-control tenant source-policy --slug acme \
--allow-endpoint '*.acme.example' --allow-endpoint 10.20.0.0/16
kdbl-control tenant source-policy --slug acme --clear-allowlist
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).
secret reseal¶
Rewrite every stored secret still in the pre-0.1.44 format into the per-tenant,
row-bound format (see Security overview). The
worker does this once at startup after an upgrade, so you rarely need it; run it
to confirm an upgraded deployment is fully migrated, or after restoring an old
backup. It is idempotent. Direct-DB mode: connect as the database owner, with
KDBL_MASTER_KEY set.
kdbl-control --postgres-url "$KDBL_POSTGRES_URL" secret reseal --dry-run # count what would change
kdbl-control --postgres-url "$KDBL_POSTGRES_URL" secret reseal # rewrite; non-zero exit if any secret would not open
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¶
kdbl-control exits 0 on success and non-zero on any failure, with the reason
printed to stderr. Apart from the warehouse commands, there are
no distinct codes per failure class, so scripts should test for success rather
than switch on the value. doctor follows the
same rule: it exits non-zero when a critical check fails, and 0 otherwise
(warnings included).
Tips¶
- Pipe secrets in over stdin (
--secret-access-key-stdin,--sas-token-stdin,--account-key-stdin,--connection-string-stdin,--idp-client-secret-stdin) so they don't appear in shell history or process listings. SMB passwords have no flag at all —source add-smb/add-smbfsread them from the terminal without echo, or from stdin when piped. - Source IDs containing
/are fine in CLI flags — kdbl-control handles URL encoding for you when talking to the API. - Run
kdbl-control <command> --helpfor the full flag list on any subcommand.