# `doctor`

`kix doctor` inspects the Kix runtime that Kix uses to run Nix, Helm, `kubectl`, and `sops`, and checks the project's builder setting in [`kix.toml`](/docs/reference/kix-toml/). It confirms that every program in the Kix runtime matches the version compiled into Kix, checks the SHA-256 hash of every file and symlink target in `manifest.json`, and, when the project uses `builder = "nix"`, verifies the machine's `nix` installation. It also reports the Kix version the project names in `kix.toml` and the versions of Kix installed for [`kix self`](/docs/reference/cli/self/).

:::note
This page is hand-maintained from `cli/kix-cli/src/cli.rs`, `cli/kix-cli/src/commands/doctor.rs`, `cli/kix-core/src/runtime.rs`, `cli/kix-core/src/project.rs`, `cli/kix/src/installs.rs`, `cli/kix-core/src/system_nix.rs`, and `cli/kix-core/runtime/programs.json`. When flag definitions or runtime behavior change in the Rust source, update this page in the same change.
:::

## Synopsis

```text
kix doctor [--flake <FLAKE>]
```

`kix doctor` takes no command-specific flags, reads [`--flake`](/docs/reference/cli/global-flags/) (default `.`) to find [`kix.toml`](/docs/reference/kix-toml/), and ignores `-o` / `--output`.

## How Kix locates its runtime

When `kix` starts, it resolves its runtime directory (`libexec/kix`) using the first match in this order:

1. **`KIX_RUNTIME_DIR`**, when set to a non-empty value. (An empty `KIX_RUNTIME_DIR=""` is ignored.) `KIX_RUNTIME_DIR` is used even if the directory has no `manifest.json`, so you can test custom tool builds.
2. **`../libexec/kix` next to the `kix` command** (after resolving symlinks to the binary), when that directory contains `manifest.json`. Because Kix resolves symlinks on the `kix` executable first, symlinking `~/.local/bin/kix` to an unpacked `bin/kix` still finds the sibling `libexec/kix/`.
3. **The compiled-in runtime path** (`KIX_BUILTIN_RUNTIME_DIR`), when that directory contains `manifest.json`. This is used when `kix` is built through Nix (`nix run` or `nix profile install`).

On normal commands (`kix check`, `kix diff`, `kix deploy`), `kix` reads `<runtime>/manifest.json` (when present) and verifies that the program set and version strings match what the binary expects. `kix doctor` goes further and hashes every file and symlink target listed in `manifest.json` to detect corrupted or incomplete installations.

## Programs in the Kix runtime

Each build of Kix is compiled against the exact tool versions in `cli/kix-core/runtime/programs.json`:

| Program | Version(s) in the Kix runtime | Used by Kix for |
| --- | --- | --- |
| `nix` | `2.35.4` | Evaluating cluster flakes and storing rendered manifests in Kix's local store (`$XDG_CACHE_HOME/kix/store` or `~/.cache/kix/store`, overridden by `KIX_STORE_ROOT`). |
| `helm` | `4.3.1` (default), `3.19.1` | Fetching Helm chart tarballs (`fetchHelmChart`) and rendering templates (`importChart`). |
| `kubectl` | `1.37.2` | Streaming workload logs (`kix logs`) and forwarding local ports (`kix pf`). |
| `sops` | `3.12.1` | Decrypting SOPS-encrypted secrets (`kix.mkSopsSecret`) during `kix deploy`. |

### Version update policy

- **Patch and minor updates:** A patch update of a program in the Kix runtime (for example, `2.35.3` to `2.35.4`) can ship in a Kix patch release. A minor update of a program in the Kix runtime ships in a Kix minor release.
- **Helm version alignment:** Kix ships both Helm 4 (`4.3.1`, the default) and Helm 3 (`3.19.1`, upstream security fixes until 10 February 2027). Because `helm template` output feeds into resource identity hashes (`kix.run/identity-hash`), the Kix library and Kix both name the exact Helm versions. A Kix minor release can change a Helm version in the Kix runtime. A patch release can only add one, so it still renders charts for a Kix library of an earlier patch in the same minor. After you upgrade Kix to a new minor release, run [`kix lib update`](/docs/reference/cli/lib/) to align `flake.lock` with your installed Kix. See [Helm and imports](/docs/reference/helm-and-imports/#helm-versions).

## Third-party licenses and LGPL components

The release archive ships `libexec/kix/THIRD_PARTY_LICENSES` alongside the programs in the Kix runtime, and each GitHub release publishes a companion `kix-<version>-third-party-sources.tar.zst` archive containing the exact upstream source tarballs and `nixpkgs` patches for the LGPL components (`nix`, `libseccomp`, `libidn2`, `libunistring`, `acl`, `attr`, and `gettext` / `libintl`, plus the `nix-*-c` shared libraries on macOS).

On Linux, the `nix` in the Kix runtime is statically linked against `musl`, so the runtime contains no shared libraries. On macOS, `nix` links against private `.dylib` files in `libexec/kix/nix-<version>/lib/` via `@loader_path`.

To run `kix` with your own build of an LGPL component:

1. Copy `libexec/kix/` to a writable directory and replace the binary (or on macOS, the `.dylib` file inside `nix-<version>/lib/`).
2. Point `KIX_RUNTIME_DIR` at your modified directory.

Normal `kix` commands check only the program names and versions in `manifest.json` (or skip the check if you remove `manifest.json`), so your modified build will run as expected. `kix doctor` reports the replaced file as changed if `manifest.json` is still present with the original hashes.

## Kix version checks

The first line names the running Kix and its path. When another Kix chose it for a project (`KIX_SELECTED_BY` is set), the line also names that Kix. See [the `kix` setting](/docs/reference/kix-toml/#the-kix-setting).

After the builder checks, `kix doctor` reports the Kix version the project names and the versions installed in `~/.local/share/kix/` (or `$XDG_DATA_HOME/kix/`). Only the `✗` line changes the exit status:

| Line | Meaning |
| --- | --- |
| `✓ Project needs Kix 0.8 (<path>/kix.toml)` | `kix.toml` sets `kix = "0.8"`, and the running Kix matches. |
| `✗ Project needs Kix 0.9 (<path>/kix.toml), and this is Kix 0.8.4` | The running Kix does not match. Kix normally runs a matching version in its place, so this appears only when `KIX_SELECTED_BY` is set. |
| `! This project has no kix.toml. Add one with kix = "0.8" so everyone runs a matching Kix` | The directory has a `flake.nix` and no `kix.toml`. |
| `! kix.toml sets no kix version. Add kix = "0.8" so everyone runs a matching Kix` | `kix.toml` exists and does not name a Kix version. |
| `✓ Installed versions: 0.8.4, 0.8.0 (<directory>)` | The versions that [`kix self list`](/docs/reference/cli/self/) shows. |
| `! No versions installed under <directory>` | The running Kix was not installed there, for example because it was installed with Nix or unpacked from a release archive by hand. |
| `! No versions installed. Kix cannot find a home directory to install into. Set HOME or XDG_DATA_HOME.` | Neither `HOME` nor `XDG_DATA_HOME` is set. |

The two `!` lines that suggest a `kix` value name the running Kix's minor. A Kix older than 0.8 prints neither, since the setting cannot name it.

## Project builder checks

After checking the Kix runtime, `kix doctor` reads [`kix.toml`](/docs/reference/kix-toml/) in the directory named by `--flake` (default `.`) and reports which builder the project uses (`kix` or `nix`) and where that setting came from.

If `kix.toml` cannot be read, contains invalid TOML, sets an unknown key or builder name, or sets `builder = "remote"`, `kix doctor` prints a `✗` line and exits `1`.

When the project uses `builder = "nix"`, `kix doctor` also inspects the `nix` installation on `PATH`:

1. **Nix version (`✓` or `✗`):** Runs `nix --version` and verifies that `nix` is upstream Nix (not Lix) at version `2.24` or later. A missing `nix`, a version older than `2.24`, or Lix prints `✗` and exits `1`.
2. **Helm charts (`✓` or `!`):** Runs `nix config show` and checks that `allow-import-from-derivation` is `true`, so Nix can render Helm charts. If it is `false` or cannot be read, `kix doctor` prints a `!` line with the fix, and the exit status does not change.

## Examples

Verify a healthy installation in a project using the default `kix` builder:

<Command commands={["kix doctor"]} />

```text
✓ Kix 0.8.0 (/home/me/.local/share/kix/0.8.0/bin/kix)
✓ Kix runtime: helm 3.19.1, helm 4.3.1, kubectl 1.37.2, nix 2.35.4, sops 3.12.1
✓ Kix runtime is intact
✓ Builder: kix (default)
✓ Project needs Kix 0.8 (/home/me/project/kix.toml)
✓ Installed versions: 0.8.0, 0.7.0 (/home/me/.local/share/kix)
```

Verify a project that sets `builder = "nix"` in `kix.toml`:

<Command commands={["kix doctor"]} />

```text
✓ Kix 0.6.0
✓ Kix runtime: helm 3.19.1, helm 4.3.1, kubectl 1.37.2, nix 2.35.4, sops 3.12.1
✓ Kix runtime is intact
✓ Builder: nix (set in ./kix.toml)
✓ Nix 2.28.3 (from PATH)
✓ Nix can render Helm charts
```

A changed or missing file fails the check, and the fix follows the list:

```text
✓ Kix 0.6.0
✓ Kix runtime: helm 3.19.1, helm 4.3.1, kubectl 1.37.2, nix 2.35.4, sops 3.12.1
✗ Kix runtime is damaged: 1 file changed since install
    sops-3.12.1/bin/sops (contents changed)
  Reinstall Kix from its release archive, unless you changed these files yourself.
✓ Builder: kix (default)
Error:   × 1 check failed
```

Each line starts with `✓` (passed), `!` (a warning that does not fail the command), or `✗` (failed). The runtime's location appears only when `KIX_RUNTIME_DIR` sets it.

## Exit status

- `0` when the runtime directory and `kix.toml` are valid (including when `KIX_RUNTIME_DIR` points to a custom runtime directory with no `manifest.json`, which prints a `!` line) and, for `builder = "nix"` projects, `nix` on `PATH` is upstream Nix `2.24` or later. Warnings (`!` lines, such as `allow-import-from-derivation` being off) do not change the exit status.
- `1` when the runtime directory cannot be found, `manifest.json` is unreadable or disagrees with the programs Kix expects, any file listed in `manifest.json` is missing or fails its SHA-256 or symlink check, `kix.toml` is invalid or sets an unsupported builder, or (when `builder = "nix"`) `nix` on `PATH` is missing, older than `2.24`, or Lix.