Skip to content

farmisen/zuul

Repository files navigation

Zuul

CI Quality Gate Status License: MIT

Zuul, Gatekeeper of Secrets

"Are you the Keymaster?" — Zuul, Ghostbusters (1984)

A CLI tool for managing secrets across multiple environments, with pluggable backend storage.

Installation

Homebrew (macOS/Linux):

brew install farmisen/tap/zuul-secrets

Shell installer (macOS/Linux):

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/farmisen/zuul/releases/latest/download/zuul-secrets-installer.sh | sh

From source:

cargo install zuul-secrets

Pre-built binaries are available for macOS (Intel + Apple Silicon) and Linux (x86_64, ARM64, musl) on the Releases page.

Features

  • Multi-environment — Manage secrets across dev, staging, production, and any custom environments
  • Pluggable backends — GCP Secret Manager for teams, encrypted local file for solo/offline use
  • Export formats — Output secrets as dotenv, direnv, JSON, YAML, or shell exports
  • Run with secrets — Inject secrets into any subprocess via zuul run
  • Import — Bulk-import from .env, JSON, or YAML files
  • Local overrides — Override backend values locally via .zuul.local.toml (never leaves your machine)
  • Metadata — Attach key-value metadata (owner, rotate-by, description) to secrets
  • Crash recovery — Batch operations are journaled; zuul recover resumes interrupted work

Available Backends

Backend Config type Best for Docs
File file Local dev, small projects, offline use backend-file.md
GCP Secret Manager gcp-secret-manager Teams, CI/CD, IAM-based access control backend-gcp.md

Quick Start

File backend (zero dependencies)

cargo install --path .

zuul init --backend file
# Choose: identity file (recommended) or passphrase

zuul env create dev
zuul secret set DATABASE_URL --env dev "postgres://localhost/mydb"
zuul run --env dev -- cargo run

GCP backend

cargo install --path .

# Provision infrastructure
cd terraform
cp terraform.tfvars.example terraform.tfvars  # edit with your values
terraform init && terraform apply
cd ..

# Initialize and authenticate
zuul init --project my-gcp-project-123
zuul auth

# Manage secrets (environments created by Terraform)
zuul secret set DATABASE_URL --env dev "postgres://localhost:5432/mydb"
zuul run --env dev -- cargo run

Configuration

.zuul.toml

Created by zuul init. Committed to version control.

File backend:

[backend]
type = "file"

[defaults]
environment = "dev"

GCP backend:

[backend]
type = "gcp-secret-manager"
project_id = "my-gcp-project-123"

[defaults]
environment = "dev"

.zuul.local.toml

Local overrides for development. Added to .gitignore automatically.

# Per-developer backend overrides (e.g., different credential path)
[backend]
credentials = "~/.zuul/my-personal-sa.json"

# Secret value overrides for local development
[secrets]
DATABASE_URL = "postgres://localhost:5432/mydb_local"
REDIS_URL = "redis://localhost:6379"

The [backend] section overrides fields from .zuul.toml — useful when team members store credentials at different paths. Supports: project_id, credentials, path, identity.

Secret overrides from [secrets] are not applied by default. Use --overrides with zuul export or zuul run to merge them.

direnv Integration

Add this to your .envrc for automatic secret loading:

eval "$(zuul export --env dev --export-format direnv)"

See .envrc.example for a ready-to-use template.

Commands

Command Description
zuul init Initialize a new project
zuul auth Set up backend authentication
zuul env list|create|show|update|delete|copy|clear Manage environments
zuul secret list|get|set|delete|info|copy Manage secrets
zuul secret metadata list|set|delete Manage secret metadata
zuul export Export secrets in various formats
zuul run Run a command with secrets injected
zuul import Bulk-import secrets from a file
zuul diff Compare secrets between two environments
zuul recover status|resume|abort Inspect or resume incomplete batch operations
zuul deploy fly Deploy to Fly.io with secrets synced and injected
zuul sync netlify Sync secrets to Netlify's environment variables
zuul sync fly Sync secrets to Fly.io
zuul audit Show who has access to secrets (GCP backend only)
zuul completions <shell> Generate shell completions (bash, zsh, fish, etc.)

Use zuul --help or zuul <command> --help for details.

Notable flags:

  • zuul secret list --with-metadata — include metadata key-value pairs in the output
  • zuul sync fly --stage — stage secrets without redeploying (run fly secrets deploy later)
  • --format text|json controls the output mode (text tables or JSON). --export-format and --import-format control the file serialization format (dotenv, json, yaml, etc.) — these are separate concerns.

Note: env create/update/delete work directly with the file backend. For GCP, environments are managed by Terraform — these commands return an error directing you to terraform apply.

Environment Variables

Common (all backends):

Variable Description
ZUUL_DEFAULT_ENV Override default environment name
ZUUL_BACKEND Override backend type

File backend:

Variable Description
ZUUL_KEY_FILE Path to an age identity file (recommended)
ZUUL_PASSPHRASE Passphrase for scrypt-based encryption (fallback)

GCP backend:

Variable Description
ZUUL_GCP_PROJECT Override GCP project ID
ZUUL_GCP_CREDENTIALS GCP service account key: file path or inline JSON

Resolution order (highest priority first): CLI flags → environment variables → .zuul.local.toml (secrets only) → .zuul.toml → built-in defaults.

Development

# Build
cargo build

# Lint and format
cargo clippy -- -D warnings
cargo fmt

Running Tests

Unit and file-backend tests run without any external dependencies:

cargo test

GCP emulator tests run against a GCP Secret Manager emulator:

# Start the emulator
docker compose -f docker-compose.emulator.yml up -d

# Run the GCP integration suite
cargo test --test gcp_emulator -- --ignored

# Stop the emulator when done
docker compose -f docker-compose.emulator.yml down

Releasing

Install cargo-release (one-time):

cargo install cargo-release

Publish a release:

# Patch release (0.1.0 → 0.1.1)
cargo release patch --execute

# Minor release (0.1.0 → 0.2.0)
cargo release minor --execute

# Specific version
cargo release 0.2.0 --execute

This bumps the version in Cargo.toml, commits, tags vX.Y.Z, and pushes. The release workflow then automatically:

  • Builds binaries for macOS (Intel + Apple Silicon) and Linux (x86_64, ARM64, musl)
  • Creates a GitHub Release with binaries and checksums
  • Updates the Homebrew formula at farmisen/homebrew-tap
  • Generates a shell installer script

Documentation

License

MIT

About

A CLI tool for managing secrets across multiple environments, with pluggable backend storage

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors