Diagnostic packs¶
K-Lake has no call-home. It does not report crashes, does not send usage data, and has no channel to the vendor at all — that property is what makes air-gapped and regulated deployments possible, and nothing about support changes it.
The consequence is that when you open a support ticket, we cannot see your deployment. A diagnostic pack closes that gap on your terms: one command collects everything we would otherwise ask for over a week of email into a single archive that you generate, inspect, and send.
kdbl-control support pack \
--api-url https://kdbl.internal --api-token "$KDBL_API_TOKEN" \
--namespace kdbl
✓ diagnostic pack written
path kdbl-diag-20260811T183203Z.tar.gz
size 4.2 MiB
sha256 7626128ad87794a29caf181c5d8c8c4519486dff3c9c6c16c40a3e4d890a447c
Send that file to your KDBL support contact through whatever channel your policy allows. Nothing leaves your network until you do.
You can also generate a pack from the web console (Support → Diagnostics) or the REST API — the same collector backs all three.
What it contains¶
| Directory | Contents |
|---|---|
summary.txt |
Triage view — versions, licence, and every anomaly the collector noticed. Start here. |
MANIFEST.json |
What was collected, what failed, what was truncated, and a SHA-256 per file. |
in-band/ |
What K-Lake knows about itself: configuration, sources and their settings, queue depth, crawls, grouped errors, per-pod self-reports, database health. |
cluster/ |
What your orchestrator knows: pod state, events, node capacity, logs, Helm values. |
The two halves are collected differently, and deliberately so. K-Lake holds
no Kubernetes credentials — granting the API permission to read pod logs
would make an internet-facing component able to export your logs, just to save
you one command. So the cluster/ half is collected by the CLI using your
kubectl credentials, from your workstation.
The
cluster/half is deployment-wide. K-Lake's own data inin-band/is scoped to the token you generate with, but pod logs interleave every tenant a pod serves — so on a multi-tenant deploymentcluster/can name sources and paths belonging to other tenants. Nobody gains access they did not already have (reading those logs needs cluster credentials), but the archive carries them, which matters when deciding who may receive it. Use--no-kubectlfor a pack whose contents match your token's scope exactly.
Either half is optional. No kubectl, or --no-kubectl, gives an
in-band-only pack. An unreachable API gives a cluster-only pack — which
matters, because "the API won't start" is a common reason to need one.
Whichever half is missing is stated explicitly in MANIFEST.json, so nobody
mistakes a partial pack for a clean bill of health.
Runtime tests¶
Everything above describes how your deployment is configured. Add
--runtime-tests and the pack also records what actually works:
| Check | What it catches |
|---|---|
database.round_trip |
A database that reads but cannot write — disk full, write-ahead log wedged, a replica promoted by mistake — answers SELECT 1 perfectly and fails everything the product does. Also reports median and p95 latency. |
queue.dispatch |
A reachable queue that cannot hold an entry, and an eviction policy that silently discards queued work under memory pressure. |
search.embeddings |
An embedder that answers but returns a degenerate or wrong-sized vector, which makes hybrid search silently wrong rather than failing. Also flags CPU-speed embedding. |
search.rerank |
A reranker running on CPU, which leaves search correct and unusably slow. Projects the cost over your configured pool. |
fleet.clock_skew |
Clock drift between pods, which breaks token validity and licence evaluation in ways that never point at the clock. |
licence.enforcement |
Enforcement that is actively blocking operations, and capacity nearing its limit. |
storage.headroom |
Network mounts that have stopped responding, and volumes running out of space. |
extraction.plugin |
An extractor that heartbeats healthily but fails every document — an OCR model missing from an offline image, weights that never loaded. Pushes a test document through and checks the text comes back. |
enrichment.plugin |
An enricher that has stopped answering, so entity extraction silently produces nothing. |
sources.connectivity |
Expired credentials, a rotated key, a firewall change — all of which present as "the crawl finds nothing" and are otherwise only reported after the next crawl fails. |
mcp.configuration |
MCP enabled without a resource URI, which answers happily and then fails every client authorization. |
Every check is bounded by its own timeout, so a wedged dependency is reported as wedged rather than hanging the pack. Nothing mutates your data: the two checks that write at all use a rolled-back transaction and a throwaway key.
A check that could not run reports skipped, never pass — "we didn't look"
and "we looked and it's fine" are different answers.
Redaction levels¶
kdbl-control support pack --level metadata # most private
kdbl-control support pack --level operational # default
kdbl-control support pack --level full --acknowledge-full
| Level | Includes | Use when |
|---|---|---|
metadata |
Counts, configuration, versions, error signatures. Every file path, source name, user and tenant replaced by a pseudonym generated for this pack alone. | The pack will be seen by more people than your named support engineer. |
operational (default) |
Adds file paths, source identifiers and verbatim error messages. No document content, no chunk text, no search queries. | Almost every ticket. |
full |
Adds audit-trail file-key samples and search query strings — what your users searched for and which files came back. | Retrieval, extraction or redaction faults, where the query is the bug. |
full requires a cluster-admin token and an explicit acknowledgement. A
tenant-admin requesting it gets an error rather than a quietly downgraded
pack, because a pack that silently came back at a lower level would be read as
evidence the data was not there.
The metadata pseudonyms are salted per pack and the salt is never stored.
The same path appears identically throughout one pack — so we can still tell
that two errors concern the same file — and differently in every other pack.
Pseudonymisation covers
in-band/only. Thecluster/half (pod logs, events, describe output) goes through the credential scrubber and the leak gate, so it carries no secrets — but file paths, share names and pod names appear verbatim, because they cannot be reliably identified inside free-form log text. If you need a pack with no verbatim identifiers anywhere, use--no-kubectl, which omits thecluster/half entirely.
What is never included¶
- Document content, extracted text, or chunk text
- Source credentials in any form. The encrypted secret blobs are never read.
- The master key, licence signing material, or download signing secret
- API tokens, personal access tokens, or session cookies
Every text file in a pack passes through a credential scrubber, and the result is then checked by a separately written detector. If that detector matches anything, the pack is not written at all — there is no "generated with warnings" outcome.
Before you send it¶
Open it. Everything is plain text or JSON, and it is meant to be read.
The sha256 printed on generation is over the archive itself, so you and we
can confirm we are looking at the same file.
Sending it somewhere else¶
If attaching the file isn't convenient, --upload-url PUTs it to a presigned
URL you supply — your storage, your egress rule, your audit trail:
This is never a KDBL endpoint. There is no address in this product that uploads to us.
--encrypt-to-vendor writes an additional copy sealed to KDBL, so the file can
travel by email or as a ticket attachment without its contents being readable
in transit or at rest by anyone but us:
The plaintext pack is always written too — read that one, send the sealed one. Encrypting in place would take away the "inspect it before you send it" property this whole feature is built on.
Each sealed pack names the key it was sealed to, so KDBL can tell a pack that needs an older key from one that arrived damaged. To check which key a pack needs without opening it:
Comparing two packs¶
"It worked last week" is a common way for a problem to be described. If you have a pack from before and one from after, the difference is usually the answer:
kdbl pack diff
before last-week.tar.gz
after today.tar.gz
config
~ config.sources[source_id=smbfs://nas/finance].bulk_ingest: false → true
state
~ state.queue.failed: 3 → 4102 (+4099)
2 difference(s).
Timestamps, counters, latencies, memory readings and pod names are excluded —
they differ between any two packs of the same untouched deployment, and
including them would bury the two lines above in thousands. Named counters
like failed are reported as deltas instead of being dropped, because the
magnitude is the finding.
Works offline on packs someone sent you: it needs no cluster and no API.
Auditing¶
Generating a pack is a bulk export of operational data, and your own security team should be able to see that it happened:
{"packs": [{"at": "2026-08-11T18:32:03Z", "principal": "alice@example.com",
"level": "operational", "scope": "tenant",
"digest": "7626128ad877…", "bytes": 184320}]}
K-Lake records that a pack was generated, by whom, at what level, and its digest. It does not keep a copy — a cache of bulk exports sitting in your cluster would be a standing liability with no diagnostic value.
Seeing what a pack would contain¶
To review the scope before generating anything:
This lists every section, what it carries, and what is excluded, without collecting a single row. It is the same description the web console shows on its consent screen.
Useful flags¶
| Flag | Default | Notes |
|---|---|---|
--namespace |
kdbl |
Namespace holding the deployment. |
--log-lines |
2000 |
Per-container tail. Truncation is always recorded in the manifest. |
--since |
24h |
Only collect logs newer than this. |
--no-kubectl |
off | Skip cluster collection entirely. |
--kube-context |
current context | Which cluster to collect from. Worth setting explicitly if your kubeconfig holds more than one. |
--runtime-tests |
off | Also exercise the dependencies (see above). Adds a few seconds. |
--encrypt-to-vendor |
off | Also write a copy sealed to KDBL, alongside the readable one. |
--upload-url |
— | PUT the pack to a presigned URL you supply. |
--out |
kdbl-diag-<utc>.tar.gz |
Output path. |
If your deployment is in licence lockdown¶
The support endpoints stay reachable when an expired licence has blocked everything else. A deployment in lockdown is exactly the one you are most likely to need a pack from.