wardgate-cli¶
A restricted HTTP client for AI agents that replaces curl when talking to Wardgate. It uses curl-like arguments but only allows connections to a single Wardgate server, preventing agents from redirecting to arbitrary APIs.
Why It Exists¶
AI agents need to call APIs. The typical approach is to give them curl and let them construct requests. But curl is too powerful:
- Arbitrary URLs - An agent can connect anywhere
- Data exfiltration - Curl can fetch any URL the agent specifies, bypassing Wardgate entirely
Wardgate-cli solves this by restricting what the agent can do:
- Fixed config path - The server URL comes from a config file at a path set at build time. The agent cannot pass
-configor override it via environment variables. - Single server - Only requests to the configured Wardgate server are allowed. Any other URL is rejected.
- Key from config only - The agent key is the only value that may come from environment variables (via
key_env). The server URL never comes from env.
Security Model¶
| Setting | Source | Agent can override? |
|---|---|---|
| Server URL | Config file only | No |
| Config path | Fixed at build time | No |
| Agent key | Config (key or key_env) |
No (key_env reads env, but server is fixed) |
The config file path defaults to /etc/wardgate-cli/config.yaml and is compiled into the binary. For production, build with this path and mount the config read-only. The agent runs as a non-root user and cannot write to /etc/.
As long as no other tools are allowed that can be used to read arbitrary URLs, this is a secure way to use Wardgate and prevent data leakage in another way.
Proper Usage¶
1. Download or Build for Your Environment¶
For the default config path /etc/wardgate-cli/config.yaml, you can download the binary from the releases page or build it yourself.
For production (agent container):
go build -ldflags "-X main.configPath=/etc/wardgate-cli/config.yaml" -o wardgate-cli ./cmd/wardgate-cli
For local development:
2. Create the Config File¶
At the path you built in (e.g. /etc/wardgate-cli/config.yaml):
Or with the key directly (less flexible):
Internal Wardgate with custom CA: If Wardgate uses HTTPS with a custom/internal CA, add ca_file so the CLI verifies the server cert:
Mount the CA cert (PEM) at that path. The CLI adds it to the system trust store for TLS verification. Alternatively, add the CA to the container's system store (e.g. Alpine: put PEM in /usr/local/share/ca-certificates/ and run update-ca-certificates) and omit ca_file.
3. Provide the Key¶
If using key_env, set that variable in the agent's environment. Options:
- Container env - Set
WARDGATE_AGENT_KEYin the container's environment - -env file - Use
wardgate-cli -env /path/to/.env(the agent can pass this; it only affects the key, not the server)
4. Deploy in the Agent Container¶
- Mount the config at the built-in path, read-only
- Ensure the agent runs as non-root and cannot write to the config directory
- Give the agent
wardgate-cliinstead ofcurlin its tool set
Commands¶
List endpoints:
Make a request (curl-like):
wardgate-cli /todoist/tasks
wardgate-cli -X POST -H "Content-Type: application/json" -d '{"content":"Buy milk"}' /todoist/tasks
Execute a command on a conclave:
wardgate-cli exec code "git status"
wardgate-cli exec code "rg TODO src/ | head -20"
wardgate-cli exec -C /home/agent/project code "make build"
Run a defined command on a conclave:
wardgate-cli run obsidian search "*.md"
wardgate-cli run obsidian grep "TODO"
wardgate-cli run -C /data/archive obsidian search "*.txt"
List conclaves:
Options¶
| Option | Description |
|---|---|
-X, --request |
HTTP method (default: GET when not specified) |
-H, --header |
HTTP header (Key: Value) |
-d, --data |
Request body |
-o, --output |
Write response to file |
-s, --silent |
Suppress progress output |
-v, --verbose |
Verbose output |
-L, --location |
Follow redirects (same-host only) |
-k, --insecure |
Skip TLS verification (for self-signed certs) |
ca_file (config) |
Path to custom CA cert (PEM) for internal Wardgate with custom CA |
-w, --write-out |
Write-out format (e.g. %{http_code}) |
-env |
Path to .env file for key_env (default: .env) |
Conclave Exec¶
The exec subcommand sends shell commands to a conclave (remote execution environment) through wardgate's policy engine.
How It Works¶
wardgate-clireceives the conclave name and command string- Parses the command into individual segments (splitting on
|,&&,||,;) - Sends segments and the raw command to wardgate for policy evaluation and remote execution
- Wardgate evaluates each segment against the conclave's rules
- If all pass: forwards the command to the conclave via WebSocket
- The conclave executes the command and streams stdout/stderr back
Usage¶
Pipeline Support¶
Piped and chained commands are parsed and each segment is evaluated individually:
wardgate-cli exec code "rg TODO src/ | head -20" # both rg and head checked
wardgate-cli exec code "git add . && git commit -m msg" # both git invocations checked
Rejected Constructs¶
Command substitution ($()), backticks, process substitution (<(), >()), and subshells are rejected because they introduce hidden command execution that cannot be policy-checked.
Working Directory¶
Use -C to set the working directory on the conclave. If not specified, the conclave's configured cwd is used:
Security¶
- Commands are parsed and each segment is evaluated against per-conclave policy rules
- The agent cannot invoke
/bin/shdirectly (denied by policy) wardgate-clicontrols all parsing - the agent only provides a command string- Execution happens on the conclave, not the agent host
- Conclaves connect outbound to wardgate - no inbound ports required
See Conclaves for full documentation including policy configuration.
Conclave Run¶
The run subcommand executes a pre-defined command template on a conclave. Unlike exec, the agent doesn't construct a shell command - it just provides the command name and arguments.
Usage¶
How It Works¶
wardgate-clisends the command name and positional arguments to wardgate- Wardgate looks up the command template in the conclave's config
- Arguments are shell-escaped and substituted into the template
- The expanded command is executed on the conclave
Examples¶
wardgate-cli run obsidian search "*.md" # find . -iname '*.md'
wardgate-cli run obsidian grep "TODO" # rg 'TODO' | grep -v SECRET1 | grep -v SECRET2
wardgate-cli run obsidian status # ls -la (no args needed)
Security¶
- The agent cannot inject shell metacharacters - all arguments are single-quote escaped
- The command shape is fixed by the config; only placeholder values change
- No shell parsing needed - the template defines the exact command structure
See Conclaves for command template configuration.
Agent Integration¶
Configure your agent to use wardgate-cli instead of curl when calling Wardgate:
# Instead of: curl -H "Authorization: Bearer $KEY" http://wardgate:8080/todoist/tasks
# Use:
wardgate-cli /todoist/tasks
No need to add your bearer token. The standard curl arguments are still supported, but the method is optional and defaults to GET when not specified.
AI Skill¶
The wardgate-cli AI Skill is a skill file that teaches AI agents how to use wardgate-cli -- discovery, API requests, conclave exec, and troubleshooting. Copy it into your agent's skill/tool directory so it knows how to interact with Wardgate without additional prompting.