Outpost Admin CLI
The outpost CLI provides operational tooling for inspecting and validating a running Arbitex Outpost. It communicates with the Outpost admin API on port 8301 and also reads local configuration and cached policy files directly from disk.
All subcommands support a --json flag that emits machine-readable JSON instead of styled ANSI text, which is suitable for use in monitoring scripts and automated pipelines.
Installation
Section titled “Installation”The CLI is included with the arbitex-outpost Python package:
pip install arbitex-outpostAfter installation, the outpost entry point is available on your PATH:
outpost --helpAlternatively, invoke the module directly:
python -m outpost.cli --helpAuthentication
Section titled “Authentication”The outpost status subcommand communicates with the local admin API (port 8301) and requires an admin key. Provide it via the --admin-key flag or the OUTPOST_EMERGENCY_ADMIN_KEY environment variable.
# Flagoutpost status --admin-key sk-admin-...
# Environment variable (preferred for scripting)export OUTPOST_EMERGENCY_ADMIN_KEY=<sk-admin-...>outpost statusThe remaining subcommands (validate-config, policy-show, benchmark-run) do not contact the admin API — they read settings and files directly from the local environment and do not require an admin key.
outpost status
Section titled “outpost status”Displays the current operational status of a running Outpost by querying three admin API endpoints.
outpost status [--json] [--admin-port PORT] [--admin-key KEY]| Flag | Default | Description |
|---|---|---|
--admin-port |
8301 |
Admin API port to connect to |
--admin-key |
$OUTPOST_EMERGENCY_ADMIN_KEY |
Admin API key |
--json |
— | Emit machine-readable JSON |
Endpoints queried
Section titled “Endpoints queried”| Endpoint | Data |
|---|---|
GET /admin/api/status |
uptime_seconds, version |
GET /admin/api/sync-status |
last_sync_at, heartbeat.circuit_breakers, heartbeat.version |
GET /admin/api/audit-queue/status |
count, disk_usage_bytes, degradation_mode |
Human output
Section titled “Human output”Outpost Status--------------Version: 0.19.2Uptime: 3h 42m 15sDegradation mode: NoPolicy sync age: 45s ago
Circuit Breakers: policy_sync CLOSED heartbeat CLOSED audit_sync CLOSED
Audit Queue: Depth: 0 Disk usage: 0 BJSON output (--json)
Section titled “JSON output (--json)”{ "version": "0.19.2", "uptime_seconds": 13335, "degradation_mode": false, "policy_sync_age_seconds": 45, "circuit_breakers": { "policy_sync": "CLOSED", "heartbeat": "CLOSED", "audit_sync": "CLOSED" }, "audit_queue_depth": 0, "disk_usage_bytes": 0}Error conditions
Section titled “Error conditions”| Error | Cause |
|---|---|
Admin API authentication failed (HTTP 401) |
Invalid or missing admin key |
Cannot connect to admin API |
Outpost not running, wrong port, or firewall blocking loopback |
outpost validate-config
Section titled “outpost validate-config”Loads the current OutpostSettings from the environment and runs the startup validation suite. Use this to verify configuration before starting the Outpost or after making changes to environment variables.
outpost validate-config [--json]This subcommand does not contact the admin API and does not require an admin key.
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 |
All checks passed or were skipped |
1 |
One or more checks failed |
2 |
No failures, but one or more warnings |
Human output
Section titled “Human output”ANSI colors are applied only when stdout is a TTY. In piped output, colors are suppressed automatically.
[PASS] mTLS certificates Certificate files present and readable[PASS] Policy cache path /var/lib/arbitex/policy is writable[WARN] GeoIP MMDB GEOIP_MMDB_PATH not set — IP enrichment disabled[SKIP] CredInt bloom filter CREDINT_ENABLED=false — skipping
Result: 1 warning(s), 0 failure(s)JSON output (--json)
Section titled “JSON output (--json)”[ { "name": "mTLS certificates", "status": "pass", "message": "Certificate files present and readable" }, { "name": "Policy cache path", "status": "pass", "message": "/var/lib/arbitex/policy is writable" }, { "name": "GeoIP MMDB", "status": "warn", "message": "GEOIP_MMDB_PATH not set — IP enrichment disabled" }, { "name": "CredInt bloom filter", "status": "skip", "message": "CREDINT_ENABLED=false — skipping" }]Check status values: "pass", "fail", "warn", "skip".
outpost policy-show
Section titled “outpost policy-show”Reads the cached policy bundle from disk and displays a summary. The bundle is read from {policy_cache_path}/policy_bundle.json using the path configured in OutpostSettings.
outpost policy-show [--json]This subcommand reads from disk only and does not contact the admin API.
Human output
Section titled “Human output”Policy Bundle-------------Version: v2026.03.12-4Rules: 47Providers: 3Active: anthropic, openai, azure-openaiBundle size: 142.3 KBLast modified: 2026-03-12T08:52:00ZJSON output (--json)
Section titled “JSON output (--json)”{ "version": "v2026.03.12-4", "rules_count": 47, "providers_count": 3, "active_providers": ["anthropic", "openai", "azure-openai"], "bundle_size_bytes": 145715, "last_modified": "2026-03-12T08:52:00Z"}Error conditions
Section titled “Error conditions”| Error | Exit code | Cause |
|---|---|---|
Policy bundle not found at {path} |
1 |
File does not exist at the configured cache path |
Policy bundle contains invalid JSON |
1 |
File is corrupt or truncated |
outpost benchmark-run
Section titled “outpost benchmark-run”Runs the Outpost benchmark test suite using pytest. Useful for measuring DLP pipeline throughput and latency after configuration changes or hardware upgrades.
outpost benchmark-run [--json] [--timeout SECS]| Flag | Default | Description |
|---|---|---|
--timeout |
120 |
Maximum seconds to wait for benchmarks to complete |
--json |
— | Capture output and return as JSON instead of streaming |
Without --json (streaming mode)
Section titled “Without --json (streaming mode)”pytest output is streamed directly to the terminal in real time. The command exits with the same return code as pytest.
outpost benchmark-run============================= test session starts ==============================platform linux -- Python 3.12.2, pytest-8.3.5collecting ... collected 12 items
tests/benchmarks/test_dlp_throughput.py::test_regex_tier_throughput PASSED [ 8%]tests/benchmarks/test_dlp_throughput.py::test_ner_tier_throughput PASSED [ 16%]...
========================= 12 passed in 18.43s ==================================With --json
Section titled “With --json”Output is captured and returned as a JSON object. Useful for CI pipelines or automated performance regression checks.
{ "returncode": 0, "output": "============================= test session starts ====...\n12 passed in 18.43s"}Timeout behavior
Section titled “Timeout behavior”If the benchmarks exceed the timeout:
- Without
--json: exits with code1and prints an error message. - With
--json: returns{"returncode": 1, "output": "Benchmark timed out after 120s"}.
Config hot-reload (SIGHUP)
Section titled “Config hot-reload (SIGHUP)”Sending SIGHUP to the Outpost process triggers a config hot-reload without restarting the process. The handler compares current settings against a freshly-loaded OutpostSettings and applies any changed values in-place.
Sending SIGHUP
Section titled “Sending SIGHUP”# Find the Outpost PIDpgrep -f "outpost"
# Send SIGHUPkill -HUP <PID>Or in a single command:
kill -HUP $(pgrep -f "outpost")Reloadable settings
Section titled “Reloadable settings”These settings take effect immediately without a restart:
| Setting | Description |
|---|---|
log_level |
Log verbosity (debug, info, warning, error, critical) |
dlp_enabled |
Enable or disable the DLP pipeline |
dlp_ner_enabled |
Enable or disable NER-tier DLP scanning |
dlp_deberta_enabled |
Enable or disable DeBERTa-tier DLP scanning |
credint_enabled |
Enable or disable Credential Intelligence scanning |
budget_enforcement_enabled |
Enable or disable budget enforcement |
debug |
Enable or disable debug mode |
When log_level changes, the root logger level is updated immediately in addition to the settings object.
Restart-required settings
Section titled “Restart-required settings”These settings are detected during reload but not applied. A warning is logged and the old value continues in use. Restart the process to apply these changes:
| Setting | Description |
|---|---|
outpost_id |
Outpost identifier |
org_id |
Organization identifier |
platform_management_url |
Platform management API URL |
outpost_cert_path |
mTLS client certificate path |
outpost_key_path |
mTLS private key path |
outpost_ca_path |
mTLS CA certificate path |
admin_port |
Admin API listen port |
outpost_api_key |
Outpost API key for incoming requests |
Log output
Section titled “Log output”On a successful reload with changes:
INFO [outpost] SIGHUP received — triggering config hot-reloadINFO [outpost] Config reloaded: log_level changed 'info' → 'debug'INFO [outpost] Config reload complete — 1 change(s): log_level: 'info' → 'debug'If restart-required keys changed:
WARNING [outpost] Config reload: 1 key(s) require restart to take effect: outpost_idIf no settings changed:
INFO [outpost] SIGHUP received — triggering config hot-reloadINFO [outpost.config_reload] Config reload: no changes detectedIf the new settings cannot be loaded (e.g. an environment variable was set to an invalid value):
ERROR [outpost.config_reload] Config reload failed — could not load new settings: <error>Graceful shutdown (SIGTERM)
Section titled “Graceful shutdown (SIGTERM)”The Outpost handles SIGTERM (and uvicorn’s graceful shutdown) through the FastAPI lifespan context manager. The shutdown sequence is designed to avoid data loss and to give the Arbitex platform advance notice.
Shutdown sequence
Section titled “Shutdown sequence”-
Final heartbeat — A shutdown heartbeat is sent to the platform with
shutting_down: truebefore any background tasks are cancelled. This allows the platform to route traffic away from the outpost immediately. -
Audit buffer flush — All buffered audit events are flushed to the platform. The flush is bounded by
shutdown_timeout(default: configurable viaSHUTDOWN_TIMEOUT). If the flush does not complete within the timeout, a warning is logged and shutdown continues:WARNING [outpost] Audit buffer flush timed out after 30s — some events may not have been deliveredOn success:
INFO [outpost] Audit buffer flush on shutdown — flushed=142 failed=0 duration_ms=1840 -
Background task cancellation — All background tasks are cancelled in order:
- Audit sync worker
- Certificate rotation scheduler
- Heartbeat sender
- SIEM DLQ retry worker
- SIEM direct sink
- Policy-bundle-configured SIEM direct sink
- CredInt CDN refresh loop
- Policy sync loop
- Periodic metrics update loop
- Prompt hold expiry loop
-
DLP pipeline resources released — logged at INFO level.
-
Shutdown complete — Final log line with elapsed time:
INFO [outpost] Arbitex Outpost stopped — shutdown completed in 2.14s
Triggering graceful shutdown
Section titled “Triggering graceful shutdown”# SIGTERM (standard graceful shutdown — used by Docker, Kubernetes, systemd)kill -TERM <PID>
# SIGINT (Ctrl+C — same graceful handler)kill -INT <PID>In container environments, graceful shutdown is initiated automatically when the container runtime sends SIGTERM (e.g. docker stop, kubectl delete pod). Ensure the terminationGracePeriodSeconds (Kubernetes) or Docker stop timeout is set to at least shutdown_timeout + 10 seconds to give the audit flush time to complete.
Monitoring integration
Section titled “Monitoring integration”Use --json output with jq to build lightweight monitoring scripts that check Outpost health without parsing ANSI text.
Check if any circuit breaker is open
Section titled “Check if any circuit breaker is open”outpost status --json | jq ' .circuit_breakers | to_entries | map(select(.value != "CLOSED")) | if length > 0 then "ALERT: open circuit breakers: \([.[].key] | join(", "))" else "OK: all circuit breakers closed" end'Alert on policy sync staleness
Section titled “Alert on policy sync staleness”outpost status --json | jq ' if .policy_sync_age_seconds > 300 then "WARN: policy sync age \(.policy_sync_age_seconds)s exceeds 5 minutes" else "OK: policy sync age \(.policy_sync_age_seconds)s" end'Check audit queue depth
Section titled “Check audit queue depth”outpost status --json | jq ' if .audit_queue_depth > 1000 then error("audit queue depth \(.audit_queue_depth) is critically high") elif .degradation_mode then "WARN: outpost is in degradation mode" else "OK: audit queue depth \(.audit_queue_depth)" end'Validate config in CI before deployment
Section titled “Validate config in CI before deployment”outpost validate-config --json | jq ' map(select(.status == "fail")) | if length > 0 then error("config validation failures: \([.[].name] | join(", "))") else "Config validation passed" end'Combine with Nagios/Icinga exit codes
Section titled “Combine with Nagios/Icinga exit codes”#!/bin/bash# check_outpost.sh — Nagios-compatible pluginset -euo pipefail
STATUS=$(outpost status --json 2>/dev/null) || { echo "CRITICAL: Cannot connect to outpost admin API" exit 2}
OPEN=$(echo "$STATUS" | jq '[.circuit_breakers | to_entries[] | select(.value == "OPEN")] | length')DEGRADE=$(echo "$STATUS" | jq '.degradation_mode')SYNC_AGE=$(echo "$STATUS" | jq '.policy_sync_age_seconds')
if [ "$OPEN" -gt 0 ] || [ "$DEGRADE" = "true" ]; then echo "CRITICAL: circuit_breakers_open=$OPEN degradation=$DEGRADE sync_age=${SYNC_AGE}s" exit 2elif [ "$SYNC_AGE" -gt 300 ]; then echo "WARNING: policy sync age ${SYNC_AGE}s" exit 1else echo "OK: outpost healthy sync_age=${SYNC_AGE}s" exit 0fiSee also
Section titled “See also”- Outpost Admin API reference — full HTTP API that
outpost statusqueries - Outpost deployment guide — environment variables, mTLS setup, and systemd unit configuration
- Outpost architecture overview — port layout, admin network isolation, and background task wiring
- DLP pipeline reference — DLP tier configuration affected by hot-reload settings