Skip to main content

AuthzX Agent

The AuthzX Agent runs next to your app. It pulls your tenant's policy bundle from AuthzX cloud every 30 seconds, evaluates it in-memory, and answers POST /v1/authorize requests locally in sub-millisecond time.

Why run the agent

  • Low latency — warm-path evaluation is sub-millisecond. Measured end-to-end including local HTTP overhead.
  • Data stays local — decision inputs never leave your network.
  • Graceful degradation — if the cloud is unreachable, the agent keeps serving from the last cached bundle.
  • Same API — SDKs work identically against cloud and agent.

Architecture

AuthzX Cloud Your Infrastructure
+-------------------------+ +---------------------------+
| policy-service | bundle | AuthzX Agent (sidecar) |
| GET .../policies/ | (tar.gz) | |
| bundle | ─────────► | - in-memory evaluation |
+-------------------------+ every | - disk-cached bundle |
30s | - localhost:8181 |
+-------------+-------------+

POST /v1/authorize

+-------------v-------------+
| Your service |
+---------------------------+

The cached bundle on disk is what makes a cold restart return /readyz=200 immediately rather than waiting for the next poll.

Install

Docker

docker run -d \
--name authzx-agent \
-e AUTHZX_API_KEY=azx_... \
-p 8181:8181 \
-v authzx-cache:/var/lib/authzx/bundles \
-e AUTHZX_CACHE_DIR=/var/lib/authzx/bundles \
authzx/agent:latest

Docker Compose

services:
authzx-agent:
image: authzx/agent:latest
environment:
AUTHZX_API_KEY: ${AUTHZX_API_KEY}
AUTHZX_CACHE_DIR: /var/lib/authzx/bundles
volumes:
- authzx-cache:/var/lib/authzx/bundles
ports:
- "8181:8181"

volumes:
authzx-cache:

Mount a persistent volume at AUTHZX_CACHE_DIR so the agent can warm-start across container replacements.

Configuration

The agent loads YAML from --config <path>, ./authzx-agent.yaml, or $HOME/.authzx/agent.yaml. Environment variables override YAML.

Env varYAML keyDefaultPurpose
AUTHZX_API_KEYapi_key— (required)Bearer token sent to the cloud policy bundle endpoint.
AUTHZX_CLOUD_URLcloud_urlhttps://api.authzx.comCloud base URL. Override for staging or self-hosted.
AUTHZX_TENANT_IDtenant_idauto-resolvedUsually inferred from the bundle. Set explicitly for multi-tenant bundles.
AUTHZX_LISTEN_ADDRlisten_addr0.0.0.0:8181HTTP listen address.
AUTHZX_POLL_INTERVALpoll_interval30sGo time.Duration. Bundle re-pull frequency.
AUTHZX_CACHE_DIRcache_dir~/.authzx/bundlesWhere bundle.json is persisted.
AUTHZX_LOG_LEVELlog_levelinfoLog level.
AUTHZX_DECISION_LOGdecision_logfalseSet to true to emit a JSON log line per decision.

All variables are prefixed AUTHZX_ (no R).

Example authzx-agent.yaml:

api_key: "azx_..."
cloud_url: "https://api.authzx.com"
listen_addr: "0.0.0.0:8181"
poll_interval: "30s"
cache_dir: "/var/lib/authzx/bundles"
decision_log: true

Using with SDKs

Point any SDK at the agent:

// Go
client := authzx.NewClient("", authzx.WithBaseURL("http://localhost:8181"))
// Node.js
const authzx = new AuthzX({ baseUrl: 'http://localhost:8181' })
# Python
client = AuthzX(base_url="http://localhost:8181")
# CLI
authzx check --subject user-123 --action read --resource doc-456 --local

No API key is needed on calls to the agent — the agent authenticates with cloud on your behalf to fetch bundles.

Health endpoints

EndpointPurpose
GET /healthzDiagnostic JSON: bundle revision, last sync time, degraded flag, consecutive failures, sync age. Returns 200 while the process is up, even when stale.
GET /readyz200 once a bundle has loaded (from cache or cloud). 503 during the cold-start window. Use this as your orchestrator readiness probe.

Example /healthz body:

{
"status": "ok",
"bundle_revision": "rev_7f2a...",
"last_sync": "2026-04-19T14:02:41Z",
"degraded": false,
"consecutive_failures": 0,
"sync_age_seconds": 12.4
}

Metrics

GET /metrics exposes Prometheus-format metrics.

MetricTypeLabelsMeaning
authzx_agent_upgauge1 while the process is alive.
authzx_agent_decisions_totalcounterallowed, access_pathOne per /v1/authorize call.
authzx_agent_decision_duration_secondshistogramallowedEval latency. Buckets tuned for sub-ms.
authzx_agent_bundle_last_sync_timestamp_secondsgaugeUnix ts of last successful sync.
authzx_agent_bundle_sync_failures_totalcounterMonotonic count of failed bundle polls.
authzx_agent_bundle_cache_hitcountersource (cache / sync)How the agent became ready at startup.
authzx_agent_degradedgauge1 when serving stale data because cloud sync is failing.
authzx_agent_consecutive_sync_failuresgaugeCurrent sync failure streak. Resets on success.

Recommended alerts:

  • authzx_agent_up == 0 — process is down.
  • authzx_agent_degraded == 1 for more than 5 minutes — sustained cloud unreachability.
  • time() - authzx_agent_bundle_last_sync_timestamp_seconds > 600 — no successful sync in 10 minutes.

Decision logs

Set AUTHZX_DECISION_LOG=true to emit one JSON line to stdout per /v1/authorize call:

{
"time": "2026-04-19T14:03:11.482Z",
"level": "INFO",
"msg": "decision",
"subject_id": "a5f77b72-d139-46e7-b3f2-09dfe049bedc",
"resource_id": "14d9ce16-dea0-4de4-b528-ef5949ef9614",
"action": "view",
"decision": true,
"reason": "Access granted via role",
"access_path": "role",
"policy_id": "pol_123",
"ms": 0.42
}

Tail them live:

docker logs -f authzx-agent | grep '"msg":"decision"'

Volume can be high under load, and subject/resource attributes may contain PII — leave this off in production by default. Pipe through Datadog, Loki, CloudWatch, or any structured-log collector as needed.

Graceful degradation

On every startup, the agent does two things in order:

  1. Load from disk cache — if bundle.json exists in AUTHZX_CACHE_DIR, the agent starts serving decisions immediately. /readyz goes 200 before any network call.
  2. Sync with cloud — pulls a fresh bundle and replaces the in-memory engine on revision bump.

If the cloud is unreachable, the agent continues serving from the cached bundle. The following signal the degraded state:

  • /healthz"degraded": true, consecutive_failures > 0, sync_age_seconds growing.
  • authzx_agent_degraded == 1.
  • Log line at every fifth failure: [ERROR] Bundle sync failing for N consecutive attempts, serving from cache (last successful sync: <age>).

When sync recovers, the agent logs Bundle sync recovered after N consecutive failures, cache refreshed.

If the cloud has never been reached and there is no cached bundle on disk, /readyz stays 503 until the first successful sync.

Trust model

v1 assumes network-level isolation — the agent trusts anything that can reach :8181. There is no shared secret on /v1/authorize. Deploy the agent inside a Docker network, Kubernetes NetworkPolicy, or VPC with the same isolation guarantees you apply to any internal service.

Troubleshooting

SymptomLikely causeCheck
/readyz is 503 after a minuteInitial sync failed and no cache on diskIs AUTHZX_CACHE_DIR mounted? Can the agent reach AUTHZX_CLOUD_URL?
[FATAL] Failed to load config: API key is requiredAUTHZX_API_KEY unsetCheck env vars / mounted config.
authzx_agent_degraded == 1 sustainedCloud unreachableVerify outbound connectivity. Logs print last successful sync age at every fifth failure.
Every user sees "permission denied"Wrong tenant bundle, or bundle is stale/healthz → compare bundle_revision with cloud.
[ERROR] Evaluation failed per requestBundle parses but evaluation fails on this input shapeSet AUTHZX_DECISION_LOG=true and capture the failing input.