Skip to content

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.


The CLI is included with the arbitex-outpost Python package:

Terminal window
pip install arbitex-outpost

After installation, the outpost entry point is available on your PATH:

Terminal window
outpost --help

Alternatively, invoke the module directly:

Terminal window
python -m outpost.cli --help

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.

Terminal window
# Flag
outpost status --admin-key sk-admin-...
# Environment variable (preferred for scripting)
export OUTPOST_EMERGENCY_ADMIN_KEY=<sk-admin-...>
outpost status

The 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.


Displays the current operational status of a running Outpost by querying three admin API endpoints.

Terminal window
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
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
Outpost Status
--------------
Version: 0.19.2
Uptime: 3h 42m 15s
Degradation mode: No
Policy sync age: 45s ago
Circuit Breakers:
policy_sync CLOSED
heartbeat CLOSED
audit_sync CLOSED
Audit Queue:
Depth: 0
Disk usage: 0 B
{
"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 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

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.

Terminal window
outpost validate-config [--json]

This subcommand does not contact the admin API and does not require an admin key.

Code Meaning
0 All checks passed or were skipped
1 One or more checks failed
2 No failures, but one or more warnings

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)
[
{
"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".


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.

Terminal window
outpost policy-show [--json]

This subcommand reads from disk only and does not contact the admin API.

Policy Bundle
-------------
Version: v2026.03.12-4
Rules: 47
Providers: 3
Active: anthropic, openai, azure-openai
Bundle size: 142.3 KB
Last modified: 2026-03-12T08:52:00Z
{
"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 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

Runs the Outpost benchmark test suite using pytest. Useful for measuring DLP pipeline throughput and latency after configuration changes or hardware upgrades.

Terminal window
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

pytest output is streamed directly to the terminal in real time. The command exits with the same return code as pytest.

Terminal window
outpost benchmark-run
============================= test session starts ==============================
platform linux -- Python 3.12.2, pytest-8.3.5
collecting ... 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 ==================================

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"
}

If the benchmarks exceed the timeout:

  • Without --json: exits with code 1 and prints an error message.
  • With --json: returns {"returncode": 1, "output": "Benchmark timed out after 120s"}.

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.

Terminal window
# Find the Outpost PID
pgrep -f "outpost"
# Send SIGHUP
kill -HUP <PID>

Or in a single command:

Terminal window
kill -HUP $(pgrep -f "outpost")

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.

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

On a successful reload with changes:

INFO [outpost] SIGHUP received — triggering config hot-reload
INFO [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_id

If no settings changed:

INFO [outpost] SIGHUP received — triggering config hot-reload
INFO [outpost.config_reload] Config reload: no changes detected

If 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>

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.

  1. Final heartbeat — A shutdown heartbeat is sent to the platform with shutting_down: true before any background tasks are cancelled. This allows the platform to route traffic away from the outpost immediately.

  2. Audit buffer flush — All buffered audit events are flushed to the platform. The flush is bounded by shutdown_timeout (default: configurable via SHUTDOWN_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 delivered

    On success:

    INFO [outpost] Audit buffer flush on shutdown — flushed=142 failed=0 duration_ms=1840
  3. 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
  4. DLP pipeline resources released — logged at INFO level.

  5. Shutdown complete — Final log line with elapsed time:

    INFO [outpost] Arbitex Outpost stopped — shutdown completed in 2.14s
Terminal window
# 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.


Use --json output with jq to build lightweight monitoring scripts that check Outpost health without parsing ANSI text.

Terminal window
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
'
Terminal window
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
'
Terminal window
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
'
Terminal window
outpost validate-config --json | jq '
map(select(.status == "fail")) |
if length > 0 then
error("config validation failures: \([.[].name] | join(", "))")
else
"Config validation passed"
end
'
#!/bin/bash
# check_outpost.sh — Nagios-compatible plugin
set -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 2
elif [ "$SYNC_AGE" -gt 300 ]; then
echo "WARNING: policy sync age ${SYNC_AGE}s"
exit 1
else
echo "OK: outpost healthy sync_age=${SYNC_AGE}s"
exit 0
fi