Main Components and Packages in the Dive Repository: Architecture Guide
Dive is organized into three functional tiers: a CLI bootstrap layer in cmd/dive, a core analysis engine within the dive package (handling image resolution and file-tree diffing), and an interactive terminal UI built on gocui, supported by shared utilities in internal/*.
Dive is an open-source tool written in Go for exploring Docker and OCI container images. Understanding the main components and packages in the Dive repository is essential for developers who want to contribute to the project or integrate its analysis capabilities into their own workflows. The codebase follows a clean separation between command-line plumbing, image analysis logic, and terminal user interface rendering.
High-Level Architecture
The repository follows a three-tier architecture that cleanly separates concerns between user interaction, data processing, and system infrastructure.
The CLI entry point (cmd/dive/main.go) handles flag parsing and application bootstrap using the clio framework. The core analysis engine (rooted in the dive directory) resolves container images from various sources, extracts filesystem layers, and computes differential changes. Finally, the user interface layer (cmd/dive/cli/internal/ui/v1) renders the interactive terminal experience using the gocui library.
Core Package Breakdown
CLI Entry Point and Command Handling (cmd/dive)
The application lifecycle begins in cmd/dive/main.go, which constructs a cli.Application via the clio library. This package registers top-level commands and delegates execution to the CLI implementation found in cmd/dive/cli/cli.go.
Configuration structures defined in cmd/dive/cli/internal/options/—such as analysis.go, export.go, and ci.go—map command-line flags and configuration file settings onto the core engine. These option structures determine whether Dive runs in interactive UI mode, exports JSON, or executes CI validation rules.
Image Resolution and Layer Handling (dive/image)
The dive/image package defines the abstract Resolver interface in dive/image/resolver.go. This interface abstracts away the specifics of how container images are fetched and parsed.
Concrete implementations include:
- Docker Engine resolver:
dive/image/docker/engine_resolver.gofor live Docker daemon access - Docker Archive resolver:
dive/image/docker/archive_resolver.gofor tar archives - Podman resolver:
dive/image/podman/resolver.gofor Podman engine support
The factory function dive.GetImageResolver (in dive/get_image_resolver.go) determines the appropriate resolver based on the detected ImageSource. Each resolver returns image.Layer objects (defined in dive/image/layer.go) that encapsulate layer metadata including size, ID, command history, and a pointer to their filesystem representation.
File-Tree Analysis Engine (dive/filetree)
At the heart of Dive's analysis capabilities is the dive/filetree package. The FileTree type (in dive/filetree/file_tree.go) provides an in-memory representation of a filesystem snapshot, with FileNode structs representing individual files and directories.
This package handles:
- Diffing logic:
filetree/diff.goimplementsCompareAndMarkto annotate files as added, removed, or modified across successive layers - Efficiency calculations:
filetree/efficiency.gocomputes wasted space metrics used in the CI validation mode - Rendering utilities: Methods like
String(true)generate ASCII tree representations for the terminal UI
Terminal User Interface (cmd/dive/cli/internal/ui/v1)
The interactive experience is implemented in cmd/dive/cli/internal/ui/v1/app/app.go, which initializes a gocui GUI and enters the main rendering loop. The UI architecture separates concerns into distinct views:
- Layer view:
cmd/dive/cli/internal/ui/v1/view/layer.godisplays the list of image layers with size and command information - File-tree view:
cmd/dive/cli/internal/ui/v1/view/filetree.gorenders the browsable filesystem hierarchy - Layout manager:
cmd/dive/cli/internal/ui/v1/layout/manager.gocoordinates view positioning and resizing
The UpdateAndRender method continuously refreshes the display with the latest analysis state until the user triggers a quit event.
Supporting Infrastructure (internal/*)
Shared utilities live in the internal directory to prevent external import:
- Event bus:
internal/bus/bus.gowraps go-partybus to decouple the analysis engine from UI updates using publish-subscribe patterns - Structured logging:
internal/log/log.goprovides centralized logging facilities - Utilities:
internal/utils/contains argument helpers and common view components
How the Components Work Together
The execution flow demonstrates how the main components and packages in the Dive repository interact:
-
Startup:
cmd/dive/main.gocreates the clio application and loadsv1.Configfrom flags, environment variables, and configuration files viacmd/dive/cli/internal/options/. -
Image Resolution:
dive/get_image_resolver.gocallsDeriveImageSourceto detect whether the input is a Docker Engine reference, archive path, or Podman reference, then returns the concreteimage.Resolverimplementation. -
Layer Processing: The resolver produces
[]*image.Layer, where eachLayercontains a*filetree.FileTree. The analysis code builds these trees layer by layer. -
Diff Calculation: For each successive layer,
FileTree.CompareAndMarkannotates filesystem changes, allowing the UI to highlight added, removed, and modified files. -
Event Distribution: Long-running analysis operations publish progress events via
internal/busso the UI can remain responsive and display status updates. -
Rendering:
ui/v1/app/app.goruns the main event loop, calling the controller'sUpdateAndRenderto refresh the terminal display with current layer and file-tree state.
Working with the Dive API
Resolving an Image and Listing Layers
This example demonstrates the core API for image resolution:
import (
"fmt"
"github.com/wagoodman/dive/dive"
"github.com/wagoodman/dive/dive/image"
)
func listLayers(imageRef string) error {
// Detect the source (docker engine, archive, podman) and pick a resolver
src, ref := dive.DeriveImageSource(imageRef)
resolver, err := dive.GetImageResolver(src)
if err != nil {
return err
}
// Resolve the image – this pulls metadata and builds layer objects
img, err := resolver.Resolve(ref)
if err != nil {
return err
}
// The Image struct contains a slice of *image.Layer
for i, l := range img.Layers {
fmt.Printf("Layer %d: %s (%s) – %d bytes\n",
i, l.ShortId(), l.Command, l.Size)
}
return nil
}
Relevant files: dive/get_image_resolver.go, dive/image/resolver.go, dive/image/layer.go.
Accessing File-Tree Data
To inspect the filesystem of a specific layer:
func printLayerTree(img *image.Image, layerIdx int) error {
if layerIdx < 0 || layerIdx >= len(img.Layers) {
return fmt.Errorf("invalid layer index")
}
ft := img.Layers[layerIdx].Tree // a *filetree.FileTree
fmt.Println(ft.String(true)) // true → show file size attributes
return nil
}
Relevant file: dive/filetree/file_tree.go.
Programmatically Running the UI
To launch the interactive interface programmatically:
func runUI() error {
// The CLI package builds a v1.Config from flags / env vars
cfg, err := v1.LoadConfig()
if err != nil {
return err
}
// UI entry point – blocks until the user quits (Ctrl+C)
return v1.Run(context.Background(), cfg)
}
Relevant file: cmd/dive/cli/internal/ui/v1/app/app.go.
Summary
cmd/dive: Contains the application entry point (main.go), CLI command definitions, and configuration option structures that map flags to the core engine.dive/image: Provides pluggable image resolution through theResolverinterface, with concrete implementations for Docker Engine, Docker archives, and Podman.dive/filetree: Implements the in-memory filesystem model with diffing capabilities, efficiency calculations, and ASCII rendering for layer analysis.cmd/dive/cli/internal/ui/v1: Hosts the interactive terminal interface built on gocui, including views for layers, file trees, and the layout manager.internal/*: Supplies cross-cutting concerns including the event bus (internal/bus/bus.go), structured logging, and utility helpers.
Frequently Asked Questions
What is the main entry point of the Dive application?
The entry point is cmd/dive/main.go, which constructs a clio application and registers the top-level dive command. This file handles the initial bootstrap before delegating to cmd/dive/cli/cli.go for command execution and configuration loading.
How does Dive support multiple container engines?
Dive uses the Resolver interface defined in dive/image/resolver.go to abstract image fetching. The function dive.GetImageResolver (in dive/get_image_resolver.go) selects between Docker Engine, Docker archive, and Podman resolvers based on the image reference format detected by DeriveImageSource.
What library does Dive use for its terminal user interface?
Dive builds its interactive UI using gocui, a minimalist console UI library for Go. The main application logic resides in cmd/dive/cli/internal/ui/v1/app/app.go, which manages the event loop, keybindings, and view coordination through the layout manager.
How does Dive calculate differences between image layers?
The dive/filetree package handles differential analysis. Each image.Layer contains a *filetree.FileTree representing that layer's filesystem state. The FileTree.CompareAndMark method (in dive/filetree/diff.go or related files) compares successive trees to annotate nodes as added, removed, or modified.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →