# dive Admin & Developer Guide

This guide is for operators, packagers, security reviewers, and developers who need to understand `dive` beyond surface usage: how it parses container images, how the file-tree comparison and efficiency scoring work, every configuration field and its precedence, the runtime and security posture, and where to extend the codebase. If you only want to install and use `dive`, start with the User Guide and come back here when you need the internals.

---

## 1. Executive Summary

| Aspect | Detail |
|---|---|
| Project | `wagoodman/dive` |
| Purpose | Explore Docker/OCI image layers and quantify wasted space |
| Language | Go (module declares `go 1.24`) |
| License | MIT |
| Binary | Single static executable, `cmd/dive` |
| CLI framework | `spf13/cobra` via Anchore's `clio` configuration framework (`anchore/clio`, `anchore/fangs`, `spf13/viper`) |
| TUI framework | `awesome-gocui/gocui` |
| Image sources | Docker engine, Podman engine (Linux/macOS), Docker/OCI tar archive |
| Runtime services | **None** — no network listener, no daemon, no persistent state |
| Distribution | GoReleaser → deb/rpm, Homebrew, Arch, Nix, Choco/Scoop/winget, Docker images on `docker.io` and `ghcr.io` (amd64/arm64) |
| Privilege surface | Requires access to a container engine socket (effectively root-equivalent) |

`dive` is a read-mostly developer tool. The "product" is the **efficiency analysis** of an image's layers: it stacks each layer's tar contents into a virtual filesystem, diffs adjacent stacks, and computes how many bytes are wasted by files that are duplicated, moved across layers, or deleted in later layers. It exposes that analysis interactively (a gocui TUI) and headlessly (a CI gate with a pass/fail exit code).

> [!NOTE]
> There is no server to operate. "Operations" here means packaging the binary, wiring it into CI, and configuring engine access — not running a service.

---

## 2. Architecture

### 2.1 High-level flow

```
                          dive <image>  /  dive build ... / CI=true dive <image>
                                         │
   cmd/dive/main.go ── clio.Application ─┤  (anchore/clio: cobra + viper config)
                                         │
   cmd/dive/cli/cli.go  create()         │  builds root cmd + subcommands:
       ├── command.Root(app)             │     dive [IMAGE]      (fetch + analyze)
       ├── command.Build(app)            │     dive build ...    (docker build + analyze)
       ├── clio.VersionCommand           │     dive version
       └── clio.ConfigCommand            │     dive config
                                         │
   internal/command/{root,build}.go      │  resolve options.Application, pick UI
                                         ▼
   dive.GetImageResolver(source) ──► image.Resolver
       ├── docker.NewResolverFromEngine()    (Docker daemon)
       ├── docker.NewResolverFromArchive()   (tar on disk)
       └── podman.NewResolverFromEngine()    (Podman, linux/darwin)
                                         │
   adapter.ImageResolver(r).Fetch/Build  │  observer wrappers emit progress events
                                         ▼
   image.Image{ Trees []*filetree.FileTree, Layers []*image.Layer }
                                         │
   adapter.NewAnalyzer().Analyze ──► image.Analyze()
       └── filetree.Efficiency(trees) ─► (score, EfficiencySlice)
                                         │
                 ┌───────────────────────┼───────────────────────┐
                 ▼                        ▼                       ▼
   bus.ExploreAnalysis()        adapter.Evaluator (CI)    adapter.Exporter (--json)
   → gocui TUI (ui/v1)          → ci rules pass/fail      → JSON file
```

All cross-component progress ("Loading image", "Analyzing image", "Evaluating image", "Exporting details") flows over an in-process event bus (`wagoodman/go-partybus`) rather than direct calls, so the UI and the analysis pipeline stay decoupled.

### 2.2 Package map

| Package (path) | Responsibility |
|---|---|
| `cmd/dive` | `main()` — wires build-time identification into the clio app |
| `cmd/dive/cli` | Builds the clio `Application` and assembles the cobra command tree |
| `cmd/dive/cli/internal/command` | `root.go` (analyze), `build.go` (build+analyze) command definitions and the shared `run()` pipeline |
| `cmd/dive/cli/internal/command/adapter` | Observer wrappers around resolve/analyze/evaluate/export that publish progress events |
| `cmd/dive/cli/internal/command/ci` | CI rule definitions, evaluator, and report rendering |
| `cmd/dive/cli/internal/command/export` | JSON export model (`Export`, `Layer`, `Image`, `FileReference`) |
| `cmd/dive/cli/internal/options` | All configuration structs: `Analysis`, `CI`, `CIRules`, `Export`, `UI*` — the source of truth for flags **and** config-file keys |
| `cmd/dive/cli/internal/ui` + `ui/v1/**` | The gocui TUI: app loop, controller, layout manager, views, viewmodels, keybindings |
| `dive` | `GetImageResolver`, `ImageSource` enum, `source://` scheme parsing |
| `dive/image` | `Image`, `Layer`, `Analysis`, `Resolver`/`ContentReader` interfaces, `Analyze()` |
| `dive/image/docker` | Docker engine resolver, archive resolver, OCI/Docker tar parsing, build wrapper |
| `dive/image/podman` | Podman engine resolver and build wrapper (Linux/macOS only) |
| `dive/filetree` | The core data model: `FileTree`, `FileNode`, `FileInfo`, diff/compare, layer stacking, and the `Efficiency()` algorithm |
| `internal/bus` | partybus event bus singleton + helpers (`StartTask`, `Report`, `Notify`, `ExploreAnalysis`) |
| `internal/bus/event` | Event type constants and payload parsers |
| `internal/log` | Logging singleton (Anchore `go-logger`) |
| `internal/utils` | Small helpers (`CleanArgs`, `IsNewView`, formatting) |

### 2.3 Command construction (clio, not plain cobra)

`dive` does **not** register subcommands the typical Cobra way (an `init()` tree of `AddCommand` calls). Instead `cmd/dive/cli/cli.go` builds an `anchore/clio` application:

```go
clioCfg := clio.NewSetupConfig(id).
    WithGlobalConfigFlag().   // persistent -c <path> to load an application config
    WithGlobalLoggingFlags(). // persistent -v / -q tied to the logging config
    WithConfigInRootHelp().   // --help renders the full resolved config
    WithUI(ui.None()).
    WithInitializers(/* wire bus + logger into clio state */)

app := clio.New(*clioCfg)
rootCmd := command.Root(app)
rootCmd.AddCommand(
    clio.VersionCommand(id),
    clio.ConfigCommand(app, nil),
    command.Build(app),
)
```

The practical consequence for maintainers and for documentation tooling: **flags and config keys are declared on option structs, not on the commands.** Each struct in `internal/options` implements one or more clio interfaces — `clio.FlagAdder` (`AddFlags`), `clio.FieldDescriber` (`DescribeFields`), `clio.PostLoader` (`PostLoad`). clio merges defaults → config file → environment → flags, then calls `PostLoad()` for validation and derived values.

---

## 3. Requirements

| Component | Requirement | Notes |
|---|---|---|
| OS | Linux, macOS, or Windows | Podman source is gated to Linux/macOS via build tags (`//go:build linux \|\| darwin`) |
| Container engine | Docker **or** Podman | Default is Docker; engine selected by `container-engine` / `--source` |
| Engine socket access | Read access to the daemon socket | Default `/var/run/docker.sock`; overridable via `DOCKER_HOST`/`DOCKER_CONTEXT` |
| Client binary on PATH | `docker` (or `podman`) for `dive build` | `build.go` shells out via `exec.Command` |
| Terminal | A real TTY for interactive mode | Not required for `--ci` / `CI=true` |
| Build toolchain (from source) | Go matching the module's `go 1.24`; CGO not required (`CGO_ENABLED=0`) | `go install` works but omits version stamping |

### Engine connection environment

The Docker engine resolver honors the standard Docker client environment (read in `dive/image/docker/engine_resolver.go`):

| Variable | Purpose |
|---|---|
| `DOCKER_HOST` | Daemon endpoint (TCP or unix socket) |
| `DOCKER_CONTEXT` | Named Docker context to resolve the host from |
| `DOCKER_TLS_VERIFY` | Enable TLS verification of the daemon |
| `DOCKER_API_VERSION` | Pin the negotiated API version (client/daemon mismatch) |

---

## 4. Configuration Reference

### 4.1 Resolution order and locations

Configuration is resolved by clio/viper from (lowest to highest precedence): built-in defaults → config file → environment → command-line flags. After binding, each option struct's `PostLoad()` runs to validate and compute derived state.

Config files are auto-discovered (`.yaml` or `.yml`) at:

- `$XDG_CONFIG_HOME/dive/*.yaml`
- `$XDG_CONFIG_DIRS/dive/*.yaml`
- `~/.config/dive/*.yaml`
- `~/.dive.yaml`

Explicit selection: `-c/--config <path>` or `DIVE_CONFIG=<path>`. Inspect the merged result with `dive config`.

### 4.2 Analysis options (`options/analysis.go`)

| YAML key | Flag | Default | Validation (`PostLoad`) |
|---|---|---|---|
| `container-engine` | `--source` | `docker` | Must be one of `docker`, `podman`, `docker-archive`; otherwise a warning is logged and it resets to `docker` |
| `ignore-errors` | `-i`, `--ignore-errors` | `false` | — |

> [!IMPORTANT]
> `--source` binds to the **same field** as `container-engine`. The image source is resolved in `PostLoad()`: if the image argument carries a `scheme://` prefix (`DeriveImageSource`), that wins; otherwise the source is parsed from `container-engine`/`--source` (`ParseImageSource`). `docker-tar` is accepted as an alias for `docker-archive`.

### 4.3 CI options (`options/ci.go`, `options/ci_rules.go`)

| YAML key | Flag | Default | Notes |
|---|---|---|---|
| `ci` | `--ci` | `false` | Also enabled when env `CI` is `true`/`1`/`yes` |
| `ci-config` | `--ci-config` | `.dive-ci` | If the file exists, its `rules:` block is loaded over the defaults |
| `rules.lowest-efficiency` | `--lowestEfficiency` | `0.9` | Ratio 0–1, or `disabled`/`off`/`false`/empty |
| `rules.highest-wasted-bytes` | `--highestWastedBytes` | `disabled` | Human size (`B`,`kB`,`MB`,`GB`), or `disabled` |
| `rules.highest-user-wasted-percent` | `--highestUserWastedPercent` | `0.1` | Ratio 0–1, or `disabled` |

Legacy camelCase keys (`lowestEfficiency`, `highestWastedBytes`, `highestUserWastedPercent`) are still accepted and mapped onto the snake-case fields, with a deprecation warning logged in `PostLoad()`.

> [!NOTE]
> The built-in CI defaults (`0.9` / `disabled` / `0.1`) differ from the illustrative values shown in the project README (`0.95` / `20MB` / `0.20`). The README values are an example `.dive-ci`, not the defaults. When no `.dive-ci` is present, the built-in defaults above apply.

### 4.4 Export options (`options/export.go`)

| YAML key | Flag | Default | Notes |
|---|---|---|---|
| `json-path` | `-j`, `--json` | *(empty = disabled)* | Writes the analysis JSON; `PostLoad` errors if the target directory does not exist. Setting this skips the TUI. |

### 4.5 UI options (`options/ui*.go`)

| YAML key | Default | Validation |
|---|---|---|
| `diff.hide` | *(none)* | Each entry must be `added`/`removed`/`modified`/`unmodified`; invalid entries warn and are ignored |
| `filetree.collapse-dir` | `false` | — |
| `filetree.pane-width` | `0.5` | Must be `>0` and `<1`; otherwise warns and resets to `0.5` |
| `filetree.show-attributes` | `true` | — |
| `layer.show-aggregated-changes` | `false` | — |
| `keybinding.*` | see below | Comma-separated multi-key; first key is the UI hint |

Keybinding fields (`options/ui_keybindings.go`) are grouped:

- **Global:** `quit`, `toggle-view`, `filter-files`, `close-filter-files`
- **Navigation:** `up`, `down`, `left`, `right`, `page-up`, `page-down`
- **Layer:** `compare-all`, `compare-layer`
- **Filetree:** `toggle-collapse-dir`, `toggle-collapse-all-dir`, `toggle-added-files`, `toggle-removed-files`, `toggle-modified-files`, `toggle-unmodified-files`, `toggle-filetree-attributes`, `toggle-sort-order`, `toggle-wrap-tree`, `extract-file`

> [!NOTE]
> The keybinding model uses reflection (`getUIBindingValues` / `createKeyBindings`) to convert between the YAML-friendly `UIKeybindings` struct and the runtime `key.Bindings`. Adding a binding means adding a field with matching `yaml`/`mapstructure` tags and registering a default in `key.DefaultBindings()`.

---

## 5. The Analysis Engine (core internals)

### 5.1 Image ingestion

Each image is normalized into `image.Image{ Trees []*filetree.FileTree, Layers []*image.Layer }`. For the archive path (`dive/image/docker/image_archive.go`), `NewImageArchive` reads the tar in one pass:

- Layer blobs are detected by extension (`.tar`, `.tar.gz`/`.tgz`) for the legacy Docker format, and by **content sniffing** for the OCI format (Docker 25+) — entries under `blobs/` are tried as gzip/estargz, then zstd, then plain tar, then JSON. This is how `dive` supports uncompressed, gzip, estargz, and zstd layer compression.
- `manifest.json` drives layer ordering when present; for pure OCI images (no `manifest.json`) the config blob is located via `isConfig()` and a synthetic manifest is built.
- Each layer tar becomes a `filetree.FileTree` via `processLayerTar` → `AddPath` for every entry; missing config/manifest yields `could not find image config` / `could not find image manifest`.

`ToImage()` walks history entries (skipping `EmptyLayer` history items) to attach the originating `CreatedBy` command to each content-bearing layer.

### 5.2 The file tree model (`dive/filetree`)

- **`FileInfo`** holds tar metadata plus an **xxhash** content hash (`getHashFromReader`). `Compare()` returns `Unmodified` only when type, hash, mode, UID, and GID all match — otherwise `Modified`.
- **`FileNode`** is a tree node with lazily computed size, children map, and a memoized slash path. Whiteouts use the OverlayFS convention: `.wh.<name>` marks a removed file; `.wh..wh..opq` marks an opaque directory and is rejected at add time.
- **`DiffType`** is `Unmodified | Modified | Added | Removed`; `merge()` collapses differing child diffs into `Modified` so a directory's diff reflects its subtree.
- **Stacking** (`StackTreeRange`, `Stack`) overlays an upper tree onto a copy of a lower tree: whiteouts remove paths; everything else is added. This produces the "filesystem as of layer N" view.
- **Comparison** (`CompareAndMark`) marks each node in the lower tree with its `DiffType` relative to the upper layer, visiting leaves-first so parent diffs derive correctly from children.

### 5.3 Layer comparison modes (`comparer.go`)

The `Comparer` caches stacked/diffed trees keyed by a four-index `TreeIndexKey` and offers two index strategies:

- **Natural indexes** — the bottom tree grows as you descend layers while the top tree is the single selected layer. This backs the default "what changed *in this layer*" view (`compare-layer`, <kbd>Ctrl</kbd>+<kbd>L</kbd>).
- **Aggregated indexes** — the bottom is fixed at the base and the top spans all layers up to the selection, backing the "aggregated changes" view (`compare-all`, <kbd>Ctrl</kbd>+<kbd>A</kbd>).

### 5.4 Efficiency scoring (`efficiency.go`)

The headline metric. For every leaf path across all layers, `Efficiency()` records each occurrence's size, tracking the **cumulative** size (sum across layers) and the **minimum discovered** size. Whiteout entries contribute the size of the previously stacked file/dir they remove.

```
score = Σ(minDiscoveredSize over all paths) / Σ(cumulativeSize over all paths)
```

- A path seen once contributes equally to both sums → no penalty.
- A path seen in ≥2 layers (duplicated/moved) or removed via whiteout adds to the cumulative sum but not proportionally to the minimum → score drops. Such paths are recorded as `EfficiencyData` and surfaced as the **inefficient files** list (sorted by cumulative size).
- `WastedBytes` = Σ cumulative size of inefficient files. `WastedUserPercent` = `WastedBytes / userSizeBytes`, where `userSizeBytes` excludes the base (index 0) layer.
- Empty/scratch images score `1.0`.

> [!NOTE]
> The score is explicitly described in-code as an **estimate/heuristic**. Issue #684 notes a double-counting edge case in the wasted-space report; treat the number as a strong signal, not an exact accounting.

### 5.5 Output paths

After `Analyze()`, the shared `run()` (in `root.go`) branches:

1. `--json` set → `adapter.NewExporter(afero.NewOsFs()).ExportTo(...)` writes the `export.Export` model and returns.
2. CI enabled → `adapter.NewEvaluator(rules).Evaluate(...)`; non-pass returns an error (non-zero exit).
3. Otherwise → `bus.ExploreAnalysis(*analysis, content)` hands off to the gocui TUI.

### 5.6 CI evaluation (`command/ci`)

Each rule implements `Rule{ Key(), Configuration(), Evaluate(*image.Analysis) (RuleStatus, string) }`:

- `LowestEfficiencyRule` fails when `threshold > analysis.Efficiency`.
- `HighestWastedBytesRule` fails when `analysis.WastedBytes > threshold` (threshold parsed via `humanize.ParseBytes`).
- `HighestUserWastedPercentRule` fails when `analysis.WastedUserPercent > threshold`.

Disabled values (`""`, `disabled`, `off`, `false`) produce a `DisabledRule` that always reports `SKIP`. Rule constructors validate ranges (ratios must be 0–1) and return errors for malformed config — surfaced as a misconfiguration. The evaluator renders a three-section report (Analysis, Inefficient Files table, Evaluation) with a `PASS/FAIL [pass:n fail:n warn:n skip:n]` summary and sets the process exit accordingly.

---

## 6. The TUI (gocui)

`cmd/dive/cli/internal/ui/v1` implements the interactive interface on `awesome-gocui/gocui`:

- `app/app.go` `Run()` is the entry point. It sleeps ~100ms before `gocui.NewGui` to dodge a documented termbox init race when `dive` is the first process in a container (issue #513 territory), then enters `MainLoop`.
- `app/controller.go` coordinates view updates/renders and pane switching, emitting wrapped errors (`controller failed update`, `controller unable to switch to next pane`, etc.).
- `layout/manager.go` + `layout/compound` arrange the footer (status, filter) and the column views (the compound layer/layer-details/image-details column, plus the file tree). The compound layout constrains real-estate below ~90 columns.
- `view/*` are the individual panes (layer, filetree, layer_details, image_details, filter, status, debug); `viewmodel/*` hold their state (layer selection, layer-set state, filetree view model).
- `key/binding.go` binds configured keys to actions and renders the help hints; the first configured key per action is shown.

The TUI is intentionally isolated behind the event bus and the `ui.UI` interface (`ui.None()` is used for non-interactive runs), so analysis logic never depends on gocui.

---

## 7. Security Considerations

> [!CAUTION]
> **Engine socket access is the dominant risk.** `dive`'s default mode talks to the Docker/Podman daemon. Granting socket access (or running the container with `-v /var/run/docker.sock:/var/run/docker.sock`) is effectively granting root-equivalent control of the host. Treat any environment where `dive` runs against a live daemon as privileged.

| Area | Posture |
|---|---|
| Network | None. `dive` opens no listening port and runs no server. |
| Authentication / secrets | None handled by `dive` itself. Registry pulls/auth are delegated to the underlying engine. |
| Subprocess execution | `dive build` and the podman/docker `build` paths run `exec.Command("docker"/"podman", ...)` with **`DisableFlagParsing: true`**, so all build args pass straight through to the engine. Do not feed untrusted strings into `dive build`. |
| Filesystem writes | `--json` writes one file (directory must pre-exist). The TUI "extract file" action writes extracted file contents to disk — review issues #620/#687 (extraction crashes) before relying on it on untrusted images. |
| Untrusted images | Analysis reads attacker-controllable tar archives. Malformed archives are reported as errors (or skipped with `--ignore-errors`); content sniffing is bounded by a small buffer. There is no sandboxing of analyzed content beyond Go's tar reader. |
| Supply chain | Dependencies are license-gated at build time via `bouncer` (`.bouncer.yaml` permits BSD/MIT/Apache/MPL/ISC/WTFPL/Unlicense). Issue #645 tracks CVEs surfacing in published images — keep base images current. |

Hardening guidance:

- In CI, prefer `--source docker-archive <tar>` so `dive` never needs the live daemon socket.
- If the socket is required, scope the runner's daemon access and avoid mounting the socket into long-lived or multi-tenant containers.
- Pin the `dive` image by digest and rebuild regularly to absorb base-image CVE fixes.

---

## 8. Operations & Packaging

### 8.1 There is no service to run

`dive` is a CLI invoked on demand. "Deployment" means distributing the binary and configuring engine access. There is no start/stop, no health endpoint, no logs to rotate beyond the optional `log.path` file (`log.enabled`, `log.level`).

### 8.2 Release pipeline (`.goreleaser.yaml`, `Taskfile.yaml`, `.binny.yaml`)

- **GoReleaser** builds static binaries (`CGO_ENABLED=0`, `-extldflags '-static'`) for `windows/darwin/linux` × `amd64/arm64/ppc64le`, stamping `main.version`, `main.gitCommit`, `main.buildDate`, `main.gitDescription` via ldflags.
- Artifacts: `tar.gz` (zip on Windows), `deb` + `rpm` (nfpms), a Homebrew tap (`wagoodman/homebrew-dive`), and multi-arch Docker manifests on both `docker.io/wagoodman/dive` and `ghcr.io/wagoodman/dive`.
- **Toolchain** is pinned by `binny` (`.binny.yaml`): golangci-lint, goreleaser, chronicle (changelog), glow, bouncer, task, gh.
- **Task targets** (`Taskfile.yaml`): `task` (validations = static-analysis + test), `task lint`, `task unit`, `task cli`, `task build`/`task snapshot` (goreleaser snapshot), `task changelog`, `task release`.

### 8.3 Tests

- **Unit tests** cover the filetree model and efficiency math (`filetree/*_test.go`), CI rules/evaluator (`command/ci/*_test.go`), and export (`command/export/*_test.go`); coverage threshold is enforced at 25% (`Taskfile.yaml`).
- **CLI/snapshot tests** (`cmd/dive/cli/cli_*_test.go`) drive the assembled root command with `go-snaps` golden files, exercising build, CI gate, JSON output, config loading, and image loading from docker/podman/archive sources. They use `DIVE_CONFIG` to control config and a captured stdout/stderr harness.
- **Acceptance tests** (`ci-test-*` tasks) install the deb/rpm and run the docker image against a known test image across compression algorithms (uncompressed/gzip/estargz/zstd).

---

## 9. Extending dive

| Goal | Where to work |
|---|---|
| Add a new image source | Add an `ImageSource` constant + `String()`/`Parse`/`Derive` cases in `dive/get_image_resolver.go`, implement `image.Resolver` (and `ContentReader`) in a new `dive/image/<src>` package, and wire it into `GetImageResolver`. Issue #406 requests a `go-containerregistry` resolver as exactly this kind of extension. |
| Add a CI rule | Implement `ci.Rule` in `command/ci/rules.go`, register it in `Rules(...)`, and add the corresponding field + tags in `options/ci_rules.go`. |
| Add a config option | Add the field with `yaml`/`mapstructure` tags to the relevant `options/*` struct, set its default in the `Default*()` constructor, describe it in `DescribeFields`, and validate in `PostLoad`. Flags go in `AddFlags`. |
| Add a TUI keybinding/action | Add the field in `options/ui_keybindings.go`, a default in `key.DefaultBindings()`, and the `BindingInfo`/action in the relevant view or `app.go`. |
| Change the efficiency heuristic | `dive/filetree/efficiency.go` (`Efficiency`) with tests in `efficiency_test.go`. |

> [!TIP]
> Because flags and keys are reflection/interface-driven on the option structs, the surest way to enumerate the *current* CLI/config surface is to read `cmd/dive/cli/internal/options/*.go` — not the command files. A purely text/AST scan of the cobra commands will under-report, because the commands declare almost no flags directly.

---

## 10. Known Issues & Community Signals

Drawn from the project's open issues — useful for triage and roadmap context:

| Issue | Theme |
|---|---|
| #546 | Snap install conflicts with apt-installed Docker; missing interface connections |
| #649 | Wrong default Docker host detected on Windows |
| #605 | Diving fails on Windows 10 with Rancher Desktop |
| #513 | Hang immediately after the main view opens (TUI init race) |
| #452 | `could not find image config` on certain images/builders |
| #681 | Severe slowdown when many layers modify the same files (comparison cost) |
| #684 | Double-counting in the potential-space-savings report |
| #620 / #687 | Crash / failure when extracting a file from a layer |
| #647 | Image Details window scroll not working |
| #645 | High CVEs surfaced in a published build |
| #406 / #695 / #680 | Feature requests: `go-containerregistry` source, color theme/config in `.dive.yaml` |

Architectural notes for maintainers:

- The single-image-argument constraint is enforced in `root.go` (`exactly one argument is required`); multi-image comparison is not supported.
- Layer comparison builds and caches stacked trees per index; the #681 slowdown is inherent to repeatedly stacking/diffing large identical subtrees and would need algorithmic work in `comparer.go`/`file_tree.go`.
- `--source` overloading `container-engine` is a known ergonomic wrinkle (the flag name and the config key differ); preserve the `PostLoad` derivation if refactoring.

---

## 11. Quick Reference

**Commands:** `dive [IMAGE]` · `dive build [docker build args]` · `dive version` · `dive config`

**Key flags:** `--source {docker|docker-archive|podman}` · `-i/--ignore-errors` · `--ci` · `--ci-config <path>` · `-j/--json <file>` · `-c/--config <path>` · `-v` / `-q`

**Image source forms:** `dive <image> --source <src>` ≡ `dive <src>://<image>` (sources: `docker`, `docker-archive`/`docker-tar`, `podman`)

**Config search:** `$XDG_CONFIG_HOME/dive/*.yaml` → `$XDG_CONFIG_DIRS/dive/*.yaml` → `~/.config/dive/*.yaml` → `~/.dive.yaml` (or `-c` / `DIVE_CONFIG`)

**CI rule defaults:** `lowest-efficiency: 0.9` · `highest-wasted-bytes: disabled` · `highest-user-wasted-percent: 0.1` (default rules file path `.dive-ci`)

**Engine env:** `DOCKER_HOST` · `DOCKER_CONTEXT` · `DOCKER_TLS_VERIFY` · `DOCKER_API_VERSION` · `CI` · `CLICOLOR_FORCE`

**Efficiency formula:** `score = Σ min-size-per-path / Σ cumulative-size-per-path`; `wastedBytes = Σ cumulative size of paths seen in ≥2 layers or removed`

---

_Generated June 2026 from wagoodman/dive at d6c6919. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
