| title | ctrl-exec-cli cli |
|---|---|
| subtitle | Perl CLI client for the ctrl-exec HTTP API |
| brand | odcc |
A monolithic Perl script providing full CLI access to the ctrl-exec HTTP API.
Covers every endpoint (health, ping, run, status, discovery) and the async run
lifecycle (run --async, status, wait). Intended as a
reference implementation and a portable tool for testing API functions before
building higher-level interfaces.
No CPAN dependencies. Uses only modules available as Debian trixie system packages.
Exposes the complete ctrl-exec HTTP API as a command-line interface with human-readable output by default and raw JSON output on request. Useful for scripted automation, ad-hoc operations, and as a reference for how each endpoint behaves before implementing a UI or integration.
| Module | Debian package | Notes |
|---|---|---|
HTTP::Tiny |
perl (core) |
HTTP client |
JSON::PP |
perl (core) |
JSON encode/decode |
Getopt::Long |
perl (core) |
Argument parsing |
All dependencies are included in Perl's core distribution and require no additional installation on a standard Debian trixie system.
Copy the script to any location on the ctrl-exec host or any host with network access to the API port:
sudo cp ctrl-exec-cli /usr/local/bin/
sudo chmod 755 /usr/local/bin/ctrl-exec-cliVerify:
ctrl-exec-cli --version
ctrl-exec-cli healthThe base URL, token, and username are resolved in priority order: flag > environment variable > config file > default.
Config file (~/.ctrl-exec-cli.conf):
url = http://myhost:7445
token = mytoken
username = aliceEnvironment variables:
CTRL_EXEC_API_URL API base URL (default: http://localhost:7445)
CTRL_EXEC_TOKEN Auth token
CTRL_EXEC_USERNAME Username (default: $USER)
Global flags (override all other sources):
--url <url> API base URL
--token <token> Auth token
--username <n> Username
--json Raw JSON output for all commands
Use CTRL_EXEC_TOKEN rather than --token to prevent the token value
appearing in ps output.
Check API server liveness and version. No authentication required.
ctrl-exec-cli health [--json]
Test mTLS connectivity to one or more agents. Reports cert expiry and agent
version per host. Accepts hostname or hostname:port (port defaults to 7443).
ctrl-exec-cli ping <host> [host...] [--json]
Output columns: HOST, STATUS, RTT, CERT EXPIRY, VERSION.
Failed hosts are reported inline and the command exits 1 if any host did not respond.
Run an allowlisted script on one or more agents in parallel. --script is
required. Everything after -- is passed to the script as positional arguments.
ctrl-exec-cli run <host> [host...] --script <n> [--async] [--json] [-- arg...]
Without --async, the command blocks until all hosts complete. stdout from
the script is printed to stdout; stderr to stderr. The top-level reqid is
printed as a header for log correlation. Exit code is 0 if all hosts
succeeded, 1 if any host failed.
With --async, the request is sent with "async": true: the server returns
immediately and the agent runs the script detached, so a long job is not
bound by the API read timeout. Per-host acceptance (accepted / busy /
error) is printed to stderr and the bare reqid to stdout (so
REQID=$(... --async) captures just the id). Retrieve the result later with
status, or block until it finishes with wait.
Show the current state of a run by reqid. Records are retained for 24 hours.
ctrl-exec-cli status <reqid> [--json]
The reqid is the top-level reqid printed by run, or from the JSON response.
A 404 means the reqid is unknown, has expired, or predates result persistence.
The output adapts to the run:
- Synchronous run - reqid, script, host list, completion timestamp, then
the per-host result blocks (as
runprints them). - Asynchronous run - reqid, script, whether the run is
complete, the list of still-pendinghosts, then each host's state:running/done(with exit code and output) /busy/error/expired.
Exit code is 0 unless the run is complete with one or more failed hosts (then 1).
Poll a run until it completes, then print the result (same output as status).
A synchronous run is already complete, so wait returns at once; an async run
is polled until done or the timeout elapses.
ctrl-exec-cli wait <reqid> [--timeout <seconds>] [--json]
--timeout defaults to 300 seconds. Exit code: 0 complete and all hosts
succeeded, 1 complete with failures, 2 timed out while still pending.
List registered agents and their allowlisted scripts.
ctrl-exec-cli discovery [host...] [--scripts] [--json]
Without --scripts, outputs a summary table showing host, status, version,
and script count.
With --scripts, outputs one block per host listing each script name and
absolute path. This is the view to use when you want to see what scripts are
available on each agent.
Optional host arguments filter the results to specific agents. Without host arguments, all registered agents are queried.
# Check the API is reachable
ctrl-exec-cli health
# Ping agents
ctrl-exec-cli ping web-01
ctrl-exec-cli ping web-01 web-02 db-01
# Discovery - summary table (host, status, version, script count)
ctrl-exec-cli discovery
# Discovery - show scripts available on each host
ctrl-exec-cli discovery --scripts
# Discovery - show scripts for specific hosts only
ctrl-exec-cli discovery --scripts web-01 db-01
# Run a script
ctrl-exec-cli run web-01 --script deploy
# Run a script on multiple hosts in parallel
ctrl-exec-cli run web-01 web-02 --script check-disk
# Run with arguments passed to the script
ctrl-exec-cli run db-01 --script backup-mysql -- --database myapp
# Run and get JSON output
ctrl-exec-cli run web-01 --script check-disk --json
# Run asynchronously - submit and get reqid immediately
ctrl-exec-cli run db-01 --script long-job --async
# Capture reqid from async run and retrieve result later
REQID=$(ctrl-exec-cli run db-01 --script long-job --async)
ctrl-exec-cli status $REQID
# Submit async and block until it finishes (up to 10 minutes)
ctrl-exec-cli wait "$(ctrl-exec-cli run db-01 --script long-job --async)" --timeout 600
# Retrieve a result by reqid
ctrl-exec-cli status a1b2c3d4
ctrl-exec-cli status a1b2c3d4 --json
# Override URL and token for a single invocation
ctrl-exec-cli --url http://myhost:7445 --token mytoken ping web-01
# Configure via environment variables
export CTRL_EXEC_API_URL=http://myhost:7445
export CTRL_EXEC_TOKEN=mytoken
ctrl-exec-cli discovery --scripts
ctrl-exec-cli run web-01 --script deploy- HTTPS is supported (HTTP::Tiny uses TLS via
IO::Socket::SSLif available), but certificate verification requireslibio-socket-ssl-perlto be installed. Without it, HTTPS connections proceed without CA verification. wait <reqid>polls an async job to completion (every 2s, until--timeout). For custom cadence or backoff, pollstatus <reqid>from your own script.- There is no retry logic. Connection errors and timeouts are reported and exit 1.
discoverywithout a host filter queries all registered agents viaGET /discovery. On large fleets this may be slow.- The client timeout is 120 seconds. Long-running synchronous jobs that exceed
this will be reported as a connection error even if the job completes on the
agent. Use
--asyncfor jobs expected to run longer than two minutes.