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.
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.
| 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.
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-cliFor local development:
go build -ldflags "-X main.configPath=$HOME/.wardgate-cli.yaml" -o wardgate-cli ./cmd/wardgate-cliAt the path you built in (e.g. /etc/wardgate-cli/config.yaml):
server: http://wardgate:8080
key_env: WARDGATE_AGENT_KEYOr with the key directly (less flexible):
server: http://wardgate:8080
key: "your-agent-key"Internal Wardgate with custom CA: If Wardgate uses HTTPS with a custom/internal CA, add ca_file so the CLI verifies the server cert:
server: https://wardgate.internal:443
key_env: WARDGATE_AGENT_KEY
ca_file: /etc/wardgate-cli/ca.pemMount 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.
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)
- 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
List endpoints:
wardgate-cli endpointsMake a request (curl-like):
wardgate-cli /todoist/tasks
wardgate-cli -X POST -H "Content-Type: application/json" -d '{"content":"Buy milk"}' /todoist/tasksExecute 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:
wardgate-cli conclaves| 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) |
The exec subcommand sends shell commands to a conclave (remote execution environment) through wardgate's policy engine.
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
wardgate-cli exec [-C <dir>] <conclave> "<command>"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 checkedCommand substitution ($()), backticks, process substitution (<(), >()), and subshells are rejected because they introduce hidden command execution that cannot be policy-checked.
Use -C to set the working directory on the conclave. If not specified, the conclave's configured cwd is used:
wardgate-cli exec -C /data/vault obsidian "rg 'meeting notes' ."- 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.
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.
wardgate-cli run [-C <dir>] <conclave> <command> [args...]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
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)- 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.
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.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.