This document describes the architecture of the Distrobox Go rewrite, explaining how different layers interact and the design decisions behind the codebase. It’s meant to help contributors understand the system and know where to make changes.
The Distrobox Go rewrite is designed with clear separation of concerns in mind. The rewrite followed these principles:
distrobox
├── cmd/distrobox/
│ └── main.go # Entry point
├── internal/
│ ├── cli/ # CLI layer (command definitions)
│ │ ├── root.go # Root command with global flags
│ │ ├── create.go, list.go, etc. # Individual commands
│ │ └── helpers.go
│ ├── config/ # Configuration management
│ ├── inside-distrobox/
│ │ └── assets/ # Embedded shell scripts
├── pkg/
│ ├── commands/ # Business logic layer
│ │ ├── create.go, list.go, etc. # Command implementations
│ ├── containermanager/ # Container abstraction
│ │ ├── containermanager.go # Interface definitions
│ │ └── providers/ # Implementations
│ │ ├── podman.go
│ │ └── docker.go
│ ├── ui/ # UI components
│ │ ├── progress.go
│ │ ├── printer.go
│ │ └── prompt.go
│ └── manifest/ # Manifest parsing
internal/cli/)The CLI layer handles command-line argument parsing, global flag processing, and command dispatch. It is also responsible for binding the application to the shell’s stdin/stdout, for loading the configuration, and for instantiating the components.
Among other things, the concerete ContainerManager implementation is selected and instantiated in the cli layer.
pkg/commands/)The command layer contains the business logic for each distrobox operation. Commands are independent of CLI specifics and can be tested and reused independently.
Commands should be pure orchestrators. They coordinate between the container manager abstraction and UI components, but don’t contain low-level implementation details.
Each command is implemented by a Execute method that takes a context and options struct.
pkg/containermanager/)The container manager is the abstraction over different container runtimes. This design allows distrobox to work with Docker, Podman, and other container managers without duplicating logic.
pkg/ui/)The UI layer provides simple components for user interaction and output formatting. These are instantiated in the CLI layer and passed to commands.
The architecture uses context-based dependency injection to pass the container manager from the root command to all subcommands.
Flow:
main()
↓
LoadConfig()
↓
NewRootCommand().Run()
↓
beforeAction() [global hooks]
↓ Creates container manager
↓ Stores in context
↓
Specific command action (e.g., createAction)
↓ Extracts container manager from context
↓ Creates UI tools
↓ Delegates to command layer
This pattern ensures:
Configuration is loaded once at startup in main(). Configuration sources (in order of precedence):
DBX_)~/.config/distrobox/distrobox.conf)This centralized approach makes it easy to understand where values come from and ensures consistency across commands.
When a container is created, part of the Distrobox application is loaded in the container
as it is meant to be executed inside it:
distrobox-init serves as the container entrypointdistrobox-export to expose binaries and applications to the hostdistrobox-host-exec to execute host’s commands from inside the distroboxSuch commands are POSIX shell scripts that are included as assets in internal/inside-distrobox/assets