# What Does the aqua install Command Do? A Technical Deep Dive into Aqua’s Core Installation Pipeline

> Learn what the aqua install command does. This deep dive explains how it reads aqua.yaml, resolves versions, verifies binaries, and links executables for seamless tool management.

- Repository: [aquaproj/aqua](https://github.com/aquaproj/aqua)
- Tags: deep-dive
- Published: 2026-02-25

---

**The `aqua install` command reads your declarative [`aqua.yaml`](https://github.com/aquaproj/aqua/blob/main/aqua.yaml) configuration, resolves tool versions from remote registries, cryptographically verifies downloaded binaries, and creates symbolic links in your local `bin` directory.**

The `aqua install` command (commonly aliased as `aqua i`) is the central operation of the [aquaproj/aqua](https://github.com/aquaproj/aqua) declarative CLI version manager. When executed, it transforms your configuration into concrete, runnable binaries on your system. This article examines the complete execution flow based on the v2 source code, detailing how the `aqua install` command handles CLI arguments, initializes controllers, resolves configurations, and securely installs packages.

## How the aqua install Command Works: Step-by-Step Execution Flow

The implementation in `aquaproj/aqua` follows a precise nine-step pipeline from CLI parsing to binary installation.

### 1. CLI Argument Parsing

In [`pkg/cli/install/command.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/install/command.go), the `install` sub-command extracts flags such as `--only-link`, `--all`, `-t/--tags`, and `--exclude-tags`. It constructs a `config.Param` struct that controls the installation behavior, including parallelism limits and tag filters.

### 2. Controller Initialization

The `controller.InitializeInstallCommandController` function in [`pkg/controller/install/controller.go`](https://github.com/aquaproj/aqua/blob/main/pkg/controller/install/controller.go) uses Google Wire to inject dependencies. This includes the registry installer, package installer, filesystem abstraction (`afero.Fs`), runtime detector, and policy reader, creating a fully wired `InstallController`.

### 3. Binary Directory and Proxy Setup

In [`pkg/controller/install/install.go`](https://github.com/aquaproj/aqua/blob/main/pkg/controller/install/install.go), the `mkBinDir` function creates the `<root>/bin` directory (and removes `<root>/bat` on Windows). Then `InstallProxy` fetches the tiny `aqua-proxy` binary that Aqua uses for self-updates and proxying tool execution.

### 4. Policy File Loading

Global and per-project policy YAML files are read and merged by the policy reader. These policies can enforce checksum validation, restrict allowed registries, or enable specific verification methods.

### 5. Configuration File Resolution

The `configFinder.Finds` method walks the working directory hierarchy to discover all [`aqua.yaml`](https://github.com/aquaproj/aqua/blob/main/aqua.yaml) files (or custom paths specified via flags) that need processing.

### 6. Registry and Package Installation Loop

For each discovered [`aqua.yaml`](https://github.com/aquaproj/aqua/blob/main/aqua.yaml), the controller reads and validates the configuration, opens or creates the checksum store for integrity tracking, installs remote registries (downloads registry definitions if needed), and triggers package installation for each tool defined.

### 7. Global Configuration Processing

When the `--all` flag is set, Aqua also processes global configuration files listed in `param.GlobalConfigFilePaths`, installing tools defined at the user or system level.

### 8. Individual Package Installation

The `installpackage.Installer.InstallPackages` method in [`pkg/installpackage/installer.go`](https://github.com/aquaproj/aqua/blob/main/pkg/installpackage/installer.go) handles the core logic for each tool. It creates symbolic or hard links in the `bin` directory (unless `--only-link` is false), downloads the asset from the source, verifies signatures using **cosign**, **SLSA**, **minisign**, or **GitHub Artifact Attestations**, validates checksums, extracts archives if necessary, and updates the vacuum timestamp for cleanup tracking.

### 9. Completion and Error Handling

If any package fails installation, the controller propagates `errInstallFailure`. Otherwise, the command finishes successfully, leaving all tools linked and ready for execution.

## Architectural Highlights of the aqua install Command

The `aqua install` implementation demonstrates several sophisticated design patterns that ensure reliability and security.

**Dependency Injection via Google Wire**

The controller layer uses Wire to assemble complex dependencies, making the codebase testable and modular. The `InstallController` receives interfaces for filesystem operations (`afero.Fs`), HTTP clients, and policy readers rather than concrete implementations.

**Filesystem Abstraction**

All file operations route through `afero.Fs`, enabling in-memory testing and platform-independent path handling across Linux, macOS, and Windows.

**Parallel Installation with Error Groups**

Package downloads execute concurrently using `errgroup.Group` with configurable parallelism (`param.MaxParallelism`), significantly speeding up bulk installations while maintaining error aggregation.

**Cryptographic Verification Pipeline**

Aqua supports multiple verification backends:
- **cosign** signatures (`pkg/cosign/`)
- **SLSA** provenance (`pkg/slsa/`)
- **minisign** signatures (`pkg/minisign/`)
- **GitHub Artifact Attestations** (`pkg/ghattestation/`)

**Policy-Driven Security**

Global and project-level policy files can enforce mandatory checksum verification, restrict allowed registries, or filter packages by tags, ensuring compliance in enterprise environments.

## Practical Usage Examples for aqua install

### Command-Line Usage

Install all packages defined in the nearest [`aqua.yaml`](https://github.com/aquaproj/aqua/blob/main/aqua.yaml):

```bash
aqua install

```

Install only packages tagged with `ci`:

```bash
aqua i -t ci

```

Create symbolic links without downloading (useful when binaries already exist in the cache):

```bash
aqua i -l

```

Install packages from both local and global configurations:

```bash
aqua i -a

```

### Programmatic Integration

Embed Aqua installation logic in your Go application:

```go
package main

import (
	"context"
	"net/http"
	"log/slog"
	"os"

	"github.com/aquaproj/aqua/v2/pkg/cli/util"
	"github.com/aquaproj/aqua/v2/pkg/config"
	"github.com/aquaproj/aqua/v2/pkg/controller"
	"github.com/aquaproj/aqua/v2/pkg/runtime"
)

func main() {
	ctx := context.Background()
	logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

	// Build parameters (equivalent to CLI flag parsing)
	param := &config.Param{}
	if err := util.SetParam(&cliargs.GlobalArgs{}, logger, param, "v2.6.0"); err != nil {
		panic(err)
	}
	param.OnlyLink = false
	param.All = true

	// Initialize the install controller with dependency injection
	ctrl, err := controller.InitializeInstallCommandController(
		ctx, logger, param, http.DefaultClient, runtime.New())
	if err != nil {
		panic(err)
	}

	// Execute installation
	if err := ctrl.Install(ctx, logger, param); err != nil {
		panic(err)
	}
}

```

### Conditional Download Logic

When `--only-link` is specified, the installer skips downloading as shown in [`pkg/installpackage/installer.go`](https://github.com/aquaproj/aqua/blob/main/pkg/installpackage/installer.go):

```go
if is.onlyLink {
    logger.Debug("skip downloading the package", "only_link", true)
    return nil // only linking was requested
}

```

## Key Source Files in the aqua install Pipeline

Understanding the repository structure helps when debugging or extending Aqua:

- **[`pkg/cli/install/command.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/install/command.go)** — Defines the `install` sub-command, parses flags, builds `config.Param`, and invokes the controller.
- **[`pkg/controller/install/controller.go`](https://github.com/aquaproj/aqua/blob/main/pkg/controller/install/controller.go)** — Wire-in for the install controller; holds references to registry and package installers.
- **[`pkg/controller/install/install.go`](https://github.com/aquaproj/aqua/blob/main/pkg/controller/install/install.go)** — Implements the high-level workflow: bin directory creation, proxy installation, policy loading, configuration discovery, and orchestrating registry and package installation.
- **[`pkg/installpackage/installer.go`](https://github.com/aquaproj/aqua/blob/main/pkg/installpackage/installer.go)** — Core installer that creates links, downloads assets, verifies signatures and checksums, extracts archives, and updates vacuum timestamps.
- **[`pkg/policy/reader.go`](https://github.com/aquaproj/aqua/blob/main/pkg/policy/reader.go)** — Reads and merges policy YAML files that influence verification behavior and security constraints.

## Summary

The `aqua install` command serves as the central mechanism for transforming declarative configuration into executable tooling. Key takeaways include:

- **Nine-step pipeline**: From CLI parsing in [`pkg/cli/install/command.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/install/command.go) through cryptographic verification to final link creation.
- **Dependency injection**: Google Wire assembles the `InstallController` with modular components for testing and flexibility.
- **Security-first design**: Built-in support for cosign, SLSA, minisign, and GitHub Attestations verification, plus policy-driven enforcement.
- **Flexible execution**: Supports `--only-link` for cache-only operations, tag filtering, and parallel downloads with configurable concurrency.

## Frequently Asked Questions

### What is the difference between `aqua install` and `aqua cp`?

The `aqua install` command installs packages into the central Aqua root directory (typically `~/.local/share/aquaproj-aqua`) and creates symbolic links in the `bin` directory. In contrast, `aqua cp` copies the actual binary files to a destination directory you specify, useful for Docker images or isolated environments where symlinks are not desired.

### How does the aqua install command verify package security?

During step 8 of the installation pipeline, `installpackage.Installer.InstallPackages` invokes multiple verification backends based on your [`aqua.yaml`](https://github.com/aquaproj/aqua/blob/main/aqua.yaml) configuration. It supports **cosign** signatures, **SLSA** provenance attestations, **minisign** signatures, and **GitHub Artifact Attestations**. Additionally, policy files can enforce mandatory checksum validation and restrict which registries are trusted.

### What does the `--only-link` flag do in aqua install?

When you run `aqua install --only-link` (or `aqua i -l`), the command skips the download and verification phases entirely. It only creates symbolic or hard links in the `bin` directory pointing to binaries that already exist in the Aqua cache. This is useful for restoring links after clearing your `bin` directory or when working offline with pre-populated caches.

### Where does aqua install store downloaded binaries?

Aqua stores downloaded binaries in a versioned cache directory within the Aqua root path (by default `~/.local/share/aquaproj-aqua`). The exact path follows the pattern `<root>/pkgs/<registry>/<package>/<version>/`. The `aqua install` command then creates symbolic links from `<root>/bin/<tool>` to the actual binary in the cache, keeping your system PATH clean while maintaining multiple versions.