1
0
mirror of synced 2026-03-31 22:04:20 +00:00
Files
flux2/rfcs/xxxx-cli-plugin-system
Stefan Prodan 8ac91c49e4 [RFC] Flux CLI Plugin System
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2026-03-30 12:13:21 +03:00
..
2026-03-30 12:13:21 +03:00

RFC-XXXX Flux CLI Plugin System

Status: provisional

Creation date: 2026-03-30

Last update: 2026-03-30

Summary

This RFC proposes a plugin system for the Flux CLI that allows external CLI tools to be discoverable and invocable as flux <name> subcommands. Plugins are installed from a centralized catalog hosted on GitHub, with SHA-256 checksum verification and automatic version updates. The design follows the established kubectl plugin pattern used across the Kubernetes ecosystem.

Motivation

The Flux CLI currently has no mechanism for extending its functionality with external tools. Projects like flux-operator and flux-local provide complementary CLI tools that users install and invoke separately. This creates a fragmented user experience where Flux-related workflows require switching between multiple binaries with different flag conventions and discovery mechanisms.

The Kubernetes ecosystem has a proven model for CLI extensibility: kubectl plugins are executables prefixed with kubectl- that can be discovered, installed via krew, and invoked as kubectl <name>. This model has been widely adopted and is well understood by Kubernetes users.

Goals

  • Allow external CLI tools to be invoked as flux <name> subcommands without modifying the external binary.
  • Provide a flux plugin install command to download plugins from a centralized catalog with checksum verification.
  • Support shell completion for plugin subcommands by delegating to the plugin's own Cobra __complete command.
  • Support plugins written as scripts (Python, Bash, etc.) via symlinks into the plugin directory.
  • Ensure built-in commands always take priority over plugins.
  • Keep the plugin system lightweight with zero impact on non-plugin Flux commands.

Non-Goals

  • Plugin dependency management (plugins are standalone binaries).
  • Cosign/SLSA signature verification (SHA-256 only in v1beta1; signatures can be added later).
  • Automatic update checks on startup (users run flux plugin update explicitly).
  • Private catalog authentication (users can use $FLUXCD_PLUGIN_CATALOG with TLS).
  • Flag sharing between Flux and plugins (--namespace, --context, etc. are not forwarded; plugins manage their own flags).

Proposal

Plugin Discovery

Plugins are executables prefixed with flux- placed in a single plugin directory. The flux-<name> binary maps to the flux <name> command. For example, flux-operator becomes flux operator.

The default plugin directory is ~/.fluxcd/plugins/. Users can override it with the $FLUXCD_PLUGINS environment variable. Only this single directory is scanned.

When a plugin is discovered, it appears under a "Plugin Commands:" group in flux --help:

Plugin Commands:
  operator    Runs the operator plugin

Additional Commands:
  bootstrap   Deploy Flux on a cluster the GitOps way.
  ...

Plugin Execution

On macOS and Linux, flux operator export report replaces the current process with flux-operator export report via syscall.Exec, matching kubectl's behavior. On Windows, the plugin runs as a child process with full I/O passthrough. All arguments after the plugin name are passed through verbatim with DisableFlagParsing: true.

Shell Completion

Shell completion is delegated to the plugin binary via Cobra's __complete protocol. When the user types flux operator get <TAB>, Flux runs flux-operator __complete get "" and returns the results. This works automatically for all Cobra-based plugins (like flux-operator). Non-Cobra plugins gracefully degrade to no completions.

Plugin Catalog

A dedicated GitHub repository (fluxcd/plugins) serves as the plugin catalog. Each plugin has a YAML manifest:

apiVersion: cli.fluxcd.io/v1beta1
kind: Plugin
name: operator
description: Flux Operator CLI
homepage: https://fluxoperator.dev/
source: https://github.com/controlplaneio-fluxcd/flux-operator
bin: flux-operator
versions:
  - version: 0.45.0
    platforms:
      - os: darwin
        arch: arm64
        url: https://github.com/.../flux-operator_0.45.0_darwin_arm64.tar.gz
        checksum: sha256:cd85d5d84d264...
      - os: linux
        arch: amd64
        url: https://github.com/.../flux-operator_0.45.0_linux_amd64.tar.gz
        checksum: sha256:96198da969096...

A generated catalog.yaml (PluginCatalog kind) contains static metadata for all plugins, enabling flux plugin search with a single HTTP fetch.

CLI Commands

Command Description
flux plugin list (alias: ls) List installed plugins with versions and paths
flux plugin install <name>[@<version>] Install a plugin from the catalog
flux plugin uninstall <name> Remove a plugin binary and receipt
flux plugin update [name] Update one or all installed plugins
flux plugin search [query] Search the plugin catalog

Install Flow

  1. Fetch plugins/<name>.yaml from the catalog URL
  2. Validate apiVersion: cli.fluxcd.io/v1beta1 and kind: Plugin
  3. Resolve version (latest if unspecified, or match @version)
  4. Find platform entry matching runtime.GOOS / runtime.GOARCH
  5. Download archive to temp file with SHA-256 checksum verification
  6. Extract only the declared binary from the archive (tar.gz or zip), streaming directly to disk without buffering in memory
  7. Write binary to plugin directory as flux-<name> (mode 0755)
  8. Write install receipt (flux-<name>.yaml) recording version, platform, download URL, checksum and timestamp

Install is idempotent -- reinstalling overwrites the binary and receipt.

Install Receipts

When a plugin is installed via flux plugin install, a receipt file is written next to the binary:

name: operator
version: "0.45.0"
installedAt: "2026-03-30T10:00:00Z"
platform:
  os: darwin
  arch: arm64
  url: https://github.com/.../flux-operator_0.45.0_darwin_arm64.tar.gz
  checksum: sha256:cd85d5d84d264...

Receipts enable flux plugin list to show versions, flux plugin update to compare installed vs. latest, and provenance tracking. Manually installed plugins (no receipt) show manual in listings and are skipped by flux plugin update.

User Stories

Flux User Installs a Plugin

As a Flux user, I want to install the Flux Operator CLI as a plugin so that I can manage Flux instances using flux operator instead of a separate flux-operator binary.

flux plugin install operator
flux operator get instance -n flux-system

Flux User Updates Plugins

As a Flux user, I want to update all my installed plugins to the latest versions with a single command.

flux plugin update

As a Flux user, I want to use flux-local (a Python tool) as a Flux CLI plugin by symlinking it into the plugin directory. Since flux-local is not a Go binary distributed via the catalog, I install it with pip and register it manually.

uv venv
source .venv/bin/activate
uv pip install flux-local
ln -s "$(pwd)/.venv/bin/flux-local" ~/.fluxcd/plugins/flux-local
flux local test

Manually symlinked plugins show manual in flux plugin list and are skipped by flux plugin update.

Flux User Discovers Available Plugins

As a Flux user, I want to search for available plugins so that I can extend my Flux CLI with community tools.

flux plugin search

Plugin Author Publishes a Plugin

As a plugin author, I want to submit my tool to the Flux plugin catalog so that Flux users can install it with flux plugin install <name>.

  1. Release binary with GoReleaser (produces tarballs/zips + checksums)
  2. Submit a PR to fluxcd/plugins with plugins/<name>.yaml
  3. Subsequent releases are picked up by automated polling workflows

Alternatives

PATH-based Discovery (kubectl model)

kubectl discovers plugins by scanning $PATH for kubectl-* executables. This is simple but has drawbacks:

  • Scanning the entire PATH is slow on some systems
  • No control over what's discoverable (any flux-* binary on PATH becomes a plugin)
  • No install/update mechanism built in (requires a separate tool like krew)

The single-directory approach is faster, more predictable, and integrates install/update directly into the CLI.

Design Details

Package Structure

internal/plugin/
  discovery.go        # Plugin dir scanning, DI-based Handler
  completion.go       # Shell completion via Cobra __complete protocol
  exec_unix.go        # syscall.Exec (//go:build !windows)
  exec_windows.go     # os/exec fallback (//go:build windows)
  catalog.go          # Catalog fetching, manifest parsing, version/platform resolution
  install.go          # Download, verify, extract, receipts
  update.go           # Compare receipts vs catalog, update check

cmd/flux/
  plugin.go           # Cobra command registration, all plugin subcommands

The internal/plugin package uses dependency injection (injectable ReadDir, Stat, GetEnv, HomeDir on a Handler struct) for testability. Tests mock these functions directly without filesystem fixtures.

Plugin Directory

  • Default: ~/.fluxcd/plugins/ -- auto-created by install/update commands (best-effort, no error if filesystem is read-only).
  • Override: $FLUXCD_PLUGINS env var replaces the default directory path. When set, the CLI does not auto-create the directory.

Startup Behavior

registerPlugins() is called in main() before rootCmd.Execute(). It scans the plugin directory and registers discovered plugins as Cobra subcommands. The scan is lightweight (a single ReadDir call) and only occurs if the plugin directory exists. Built-in commands always take priority.

Manifest Validation

Both plugin manifests and the catalog are validated after fetching:

  • apiVersion must be cli.fluxcd.io/v1beta1
  • kind must be Plugin or PluginCatalog respectively
  • Checksum format is <algorithm>:<hex> (currently sha256:...), allowing future algorithm migration without schema changes

Security Considerations

  • Checksum verification: All downloaded archives are verified against SHA-256 checksums declared in the catalog manifest before extraction.
  • Path traversal protection: Archive extraction guards against tar traversal.
  • Response size limits: HTTP responses from the catalog are capped at 10 MiB to prevent unbounded memory allocation from malicious servers.
  • No code execution during discovery: Plugin directory scanning only reads directory entries and file metadata. No plugin binary is executed during startup.
  • Retryable fetching: All HTTP/S operations use automatic retries for transient network failures.

Catalog Repository CI

The fluxcd/plugins repository includes CI workflows that:

  1. Validate plugin manifests on every PR (schema, name consistency, URL reachability, checksum verification, binary presence in archives, no builtin collisions)
  2. Regenerate catalog.yaml when plugins are added or removed
  3. Automatically poll upstream repositories for new releases and create update PRs

Known Limitations (v1beta1)

  1. No cosign/SLSA verification -- SHA-256 only. Signature verification can be added later.
  2. No plugin dependencies -- plugins are standalone binaries.
  3. No automatic update checks -- users run flux plugin update explicitly.
  4. No private catalog auth -- $FLUXCD_PLUGIN_CATALOG works for private URLs but no token injection.
  5. No version constraints -- no >=0.44.0 ranges. Exact version or latest only.
  6. Flag names differ between Flux and plugins -- e.g., --context (flux) vs --kube-context (flux-operator). This is a plugin concern, not a system concern.

Implementation History

  • 2026-03-30 PoC plugin catalog repository with example manifests and CI validation workflows available at fluxcd/plugins.