Skip to content

Command Wrappers

Axium's wrapper system intercepts shell commands and applies context-aware transformations before execution.


Overview

Command wrappers enable transparent command transformation:

# You type:
aws s3 ls

# Axium intercepts and executes:
enva-prod aws s3 ls

This allows: - Environment-specific prefixing (AWS profiles, Terraform workspaces) - Context injection (Docker contexts, kubectl namespaces) - Credential management (aws-vault, SOPS, etc.) - Logging and auditing of command execution


How It Works

  1. Shell Integration loads wrapper functions
  2. Wrapper function intercepts command
  3. Calls axium run <command> <args>
  4. Intelligent routing - Check if spoke command or native command
  5. Spoke route - Dispatch to spoke subcommand OR Prefix route - Apply prefix rules from prefixes.yaml
  6. Executes transformed command
Shell: aws whoami
Wrapper: axium run aws whoami
Routing: Is 'whoami' a spoke subcommand? YES
Execute: axium aws whoami (spoke command)
Shell: aws s3 ls
Wrapper: axium run aws s3 ls
Routing: Is 's3' a spoke subcommand? NO
Daemon: Apply prefixes.yaml rules
Execute: enva-prod aws s3 ls (native CLI)

Configuration

Wrappers are defined in prefixes.yaml:

prefixes:
  - command: aws
    prefix: ${env.prefix}

  - command: terraform
    prefix: ${env.prefix}

  - command: kubectl
    prefix: "kubeconfig ${env.kubeconfig}"

The daemon reads this file and generates the wrapper list.


Shell Integration

Initialization

# In ~/.bashrc or ~/.zshrc
source ~/.config/axium/bash/init.sh

This sources Axium's shell functions:

  • axium_refresh_wrappers - Regenerate wrapper functions
  • axium_clear_wrappers - Remove all wrappers
  • Wrapper functions for each command in prefixes.yaml

Wrapper Function Template

# Example generated wrapper for 'aws' command
aws() {
    command axium run aws "$@"
}

All arguments are passed through to axium run.


Wrapper Management

List Wrapped Commands

axium wrapper list

# Output:
aws
terraform
kubectl
docker

Refresh Wrappers

After changing prefixes.yaml:

# 1. Reload daemon configuration
axium daemon reload

# 2. Refresh wrappers in current shell
eval "$(axium wrapper refresh)"

Or in one line:

axium daemon reload && eval "$(axium wrapper refresh)"

Clear All Wrappers

Temporarily disable all command wrapping:

eval "$(axium wrapper clear)"

# Commands now execute directly
aws --version  # Runs /usr/bin/aws directly

To re-enable:

eval "$(axium wrapper refresh)"

The axium run Command

Core command that applies prefix rules:

axium run <command> [args...]

Intelligent Routing

axium run intelligently routes commands based on context:

Spoke Commands - Routes to spoke subcommands:

axium run aws whoami
# → Routes to: axium aws whoami (spoke command)

axium run aws info
# → Routes to: axium aws info (spoke command)

Native Commands - Applies prefix wrapping:

axium run aws s3 ls
# → Applies prefix: enva-prod aws s3 ls (native AWS CLI)

axium run terraform plan
# → Applies prefix: enva-prod terraform plan (native Terraform)

This means you can use the same wrapper for both spoke-provided functionality and native CLI commands — Axium automatically determines the correct routing.

Behavior

  1. Check spoke routing - Is this a spoke/gear subcommand?
  2. Get context - Current environment, tmux pane
  3. Query daemon - Apply prefix rules
  4. Expand templates - Substitute ${env.key} values
  5. Execute - Run transformed command

Examples

# Spoke command routing
axium run aws whoami
# Routes to spoke: axium aws whoami

# Native command with prefix
axium run aws s3 ls
# Applies prefix: enva-prod aws s3 ls

# With environment context
axium env set prod
axium run aws s3 ls
# Becomes: enva-prod aws s3 ls

# Passes all arguments
axium run terraform plan -out=tfplan
# Becomes: enva-prod terraform plan -out=tfplan

Fallback Behavior

If daemon is unavailable:

axium run aws s3 ls
# Falls back to: aws s3 ls (unwrapped)

State Cache

Wrapper state is cached in ~/.config/axium/state_cache.json:

{
  "prefixed_commands": ["aws", "terraform", "kubectl"],
  "last_updated": "2025-10-13T17:30:00Z"
}

This cache is updated when: - Daemon starts - axium daemon reload is called - prefixes.yaml changes

The shell integration reads this cache to generate wrapper functions.


Environment Context

Wrappers are environment-aware:

# Set environment
axium env set dev

# Wrapper uses dev prefix
aws s3 ls
# Executes: enva-dev aws s3 ls

# Switch environment
axium env set prod

# Wrapper uses prod prefix
aws s3 ls
# Executes: enva-prod aws s3 ls

Pane-Specific Context

In tmux, each pane can have its own environment:

# Pane 1
axium env set dev
aws s3 ls  # Uses dev prefix

# Pane 2
axium env set prod
aws s3 ls  # Uses prod prefix

Template Expansion

Wrappers support template variables:

prefixes:
  - command: aws
    prefix: ${env.prefix}

  - command: docker
    prefix: "docker --context ${env.docker_context}"

  - command: kubectl
    prefix: "kubectl --kubeconfig ${env.kubeconfig}"

Variables are expanded at runtime:

# With env.prefix = "aws-vault exec prod --"
aws s3 ls
# Becomes: aws-vault exec prod -- aws s3 ls

# With env.docker_context = "remote"
docker ps
# Becomes: docker --context remote ps

Shell Functions Support

Prefix commands can be shell functions:

# Define in ~/.bashrc
enva-prod() {
    echo "[Running in prod]" >&2
    AWS_PROFILE=prod "$@"
}

# Use in prefixes.yaml
prefixes:
  - command: aws
    prefix: enva-prod

When executed:

aws s3 ls
# Axium detects shell function and executes via: bash -ic "enva-prod aws s3 ls"

Conflict Detection (Gears)

Gears can register prefix rules in their manifests:

# gear.yaml
prefixes:
  - command: terraform
    wrapper: axium tf-run

If multiple gears register the same command, a conflict is detected:

[WARNING] Prefix conflict: command 'terraform' already registered by gear 'terraform-gear'
[WARNING] Gear 'my-gear' attempted to register 'terraform' but was rejected

First registration wins.


Debugging

Check Wrapper Function

# ZSH
which aws
# Output: aws () { command axium run aws "$@" }

# Bash
type aws
# Output: aws is a function

Test Wrapper

# Enable debug output
AXIUM_DEBUG=1 axium run aws --version

# Check daemon prefix logic
axium daemon logs | grep apply_prefixes

Verify State Cache

cat ~/.config/axium/state_cache.json | jq .prefixed_commands

Best Practices

1. Wrapper Naming

Use clear, consistent prefix patterns:

# GOOD
prefixes:
  - command: aws
    prefix: ${env.prefix}

# AVOID
prefixes:
  - command: aws
    prefix: weird-custom-thing

2. Test Before Production

# Test in dev environment
envs:
  dev:
    prefix: ""  # No wrapping in dev

  prod:
    prefix: aws-vault exec prod --  # Wrap in prod

3. Document Dependencies

# prefixes.yaml
prefixes:
  - command: aws
    prefix: ${env.prefix}
    # NOTE: Requires 'aws-vault' installed
    # Install: brew install aws-vault

4. Refresh After Changes

Always refresh after modifying prefixes.yaml:

axium daemon reload && eval "$(axium wrapper refresh)"

Troubleshooting

Wrapper Not Working

Check loaded:

which aws
# Should show function, not path

Reload shell integration:

source ~/.config/axium/bash/init.sh
eval "$(axium wrapper refresh)"

Wrong Prefix Applied

Check current environment:

axium env get
axium env show

Check prefix rule:

cat ~/.config/axium/prefixes.yaml

Command Not Found After Wrapping

The wrapper function may be hiding the real command. Clear wrappers:

eval "$(axium wrapper clear)"
which aws  # Should show /usr/bin/aws or similar

Advanced Usage

Conditional Wrapping

Different prefixes per environment:

# envs.yaml
envs:
  dev:
    prefix: ""  # No prefix in dev

  staging:
    prefix: "aws-vault exec staging --"

  prod:
    prefix: "aws-vault exec prod --"

Multiple Commands, One Prefix

prefixes:
  - command: aws
    prefix: ${env.prefix}
  - command: terraform
    prefix: ${env.prefix}
  - command: kubectl
    prefix: ${env.prefix}

Environment Variables in Wrappers

prefixes:
  - command: docker
    prefix: "DOCKER_HOST=${env.docker_host}"

See Also