predicate-authorityd runs as a local sidecar for policy reload, revocation,
identity task issuance, queue flushing, and health/status checks.
For a detailed walkthrough of how the sidecar integrates with Identity Providers (Okta, Entra, OIDC) and Local IDP mode, see How the Sidecar Works.
The predicate-authorityd sidecar is a Rust binary. Choose your preferred installation method:
Python:
# Use quotes for zsh compatibility
pip install "predicate-authority[sidecar]"
# IMPORTANT: The binary is NOT downloaded automatically during pip install.
# You must manually download it:
predicate-download-sidecarTypeScript/Node.js:
# Binary is automatically included for your platform
npm install @predicatesystems/authoritydDownload the binary for your platform from GitHub Releases:
| Platform | Binary |
|---|---|
| macOS (Apple Silicon) | predicate-authorityd-darwin-arm64.tar.gz |
| macOS (Intel) | predicate-authorityd-darwin-x64.tar.gz |
| Linux (x64) | predicate-authorityd-linux-x64.tar.gz |
| Linux (ARM64) | predicate-authorityd-linux-arm64.tar.gz |
| Windows (x64) | predicate-authorityd-windows-x64.zip |
# Example for macOS Apple Silicon
curl -LO https://github.com/PredicateSystems/predicate-authority-sidecar/releases/latest/download/predicate-authorityd-darwin-arm64.tar.gz
tar -xzf predicate-authorityd-darwin-arm64.tar.gz
chmod +x predicate-authoritydIf you have the Python SDK installed:
predicate-download-sidecar
# Or with a specific version:
predicate-download-sidecar --version v0.3.8Binary location after download:
~/Library/Application Support/predicate-authority/bin/predicate-authorityd~/.local/share/predicate-authority/bin/predicate-authorityd%LOCALAPPDATA%/predicate-authority/bin/predicate-authorityd.exeIMPORTANT: CLI arguments must be placed before the run subcommand.
GLOBAL OPTIONS (use before 'run'):
-c, --config <FILE> Path to TOML config file [env: PREDICATE_CONFIG]
--host <HOST> Host to bind to [env: PREDICATE_HOST] [default: 127.0.0.1]
--port <PORT> Port to bind to [env: PREDICATE_PORT] [default: 8787]
--mode <MODE> local_only or cloud_connected [env: PREDICATE_MODE]
--policy-file <PATH> Path to policy JSON [env: PREDICATE_POLICY_FILE]
--identity-file <PATH> Path to local identity registry [env: PREDICATE_IDENTITY_FILE]
--log-level <LEVEL> trace, debug, info, warn, error [env: PREDICATE_LOG_LEVEL]
--control-plane-url <URL> Control-plane URL [env: PREDICATE_CONTROL_PLANE_URL]
--tenant-id <ID> Tenant ID [env: PREDICATE_TENANT_ID]
--project-id <ID> Project ID [env: PREDICATE_PROJECT_ID]
--predicate-api-key <KEY> API key [env: PREDICATE_API_KEY]
--sync-enabled Enable control-plane sync [env: PREDICATE_SYNC_ENABLED]
--fail-open Fail open if control-plane unreachable [env: PREDICATE_FAIL_OPEN]
IDENTITY PROVIDER OPTIONS:
--identity-mode <MODE> local, local-idp, oidc, entra, or okta [env: PREDICATE_IDENTITY_MODE]
--allow-local-fallback Allow local/local-idp in cloud_connected mode
--idp-token-ttl-s <SECS> IdP token TTL seconds [default: 300]
--mandate-ttl-s <SECS> Mandate TTL seconds [default: 300]
LOCAL IDP OPTIONS (for identity-mode=local-idp):
--local-idp-issuer <URL> Issuer URL [env: LOCAL_IDP_ISSUER]
--local-idp-audience <AUD> Audience [env: LOCAL_IDP_AUDIENCE]
--local-idp-signing-key-env <VAR> Env var for signing key [default: LOCAL_IDP_SIGNING_KEY]
OIDC OPTIONS (for identity-mode=oidc):
--oidc-issuer <URL> Issuer URL [env: OIDC_ISSUER]
--oidc-client-id <ID> Client ID [env: OIDC_CLIENT_ID]
--oidc-audience <AUD> Audience [env: OIDC_AUDIENCE]
ENTRA OPTIONS (for identity-mode=entra):
--entra-tenant-id <ID> Tenant ID [env: ENTRA_TENANT_ID]
--entra-client-id <ID> Client ID [env: ENTRA_CLIENT_ID]
--entra-audience <AUD> Audience [env: ENTRA_AUDIENCE]
OKTA OPTIONS (for identity-mode=okta):
--okta-issuer <URL> Okta issuer URL [env: OKTA_ISSUER]
--okta-client-id <ID> Okta client ID [env: OKTA_CLIENT_ID]
--okta-audience <AUD> Okta audience [env: OKTA_AUDIENCE]
--okta-required-claims Required claims (comma-separated)
--okta-required-scopes Required scopes (comma-separated)
--okta-required-roles Required roles/groups (comma-separated)
--okta-allowed-tenants Allowed tenant IDs (comma-separated)
COMMANDS:
run Start the daemon (default)
dashboard Start with interactive TUI
init-config Generate example config file
check-config Validate config file
version Show version info
The sidecar includes an interactive terminal dashboard for real-time monitoring of authorization decisions:
./predicate-authorityd --policy-file policy.json dashboard┌────────────────────────────────────────────────────────────────────────────┐
│ PREDICATE AUTHORITY v0.5.0 MODE: strict [LIVE] UPTIME: 2h 34m [?] │
│ Policy: loaded Rules: 12 active [Q:quit P:pause] │
├─────────────────────────────────────────┬──────────────────────────────────┤
│ LIVE AUTHORITY GATE │ METRICS │
│ │ │
│ [ ✓ ALLOW ] agent:web │ Total Requests: 1,870 │
│ browser.navigate → github.com │ ├─ Allowed: 1,847 (98.8%)│
│ m_7f3a2b1c | 0.4ms │ └─ Blocked: 23 (1.2%)│
│ │ │
│ [ ✗ DENY ] agent:scraper │ Throughput: 12.3 req/s │
│ fs.write → ~/.ssh/config │ Avg Latency: 0.8ms │
│ EXPLICIT_DENY | 0.2ms │ │
│ │ TOKEN CONTEXT SAVED │
│ [ ✓ ALLOW ] agent:worker │ Blocked early: 23 actions │
│ browser.click → button#checkout │ Est. tokens saved: ~4,140 │
│ m_9c2d4e5f | 0.6ms │ │
├─────────────────────────────────────────┴──────────────────────────────────┤
│ Generated 47 proofs this session. Run `predicate login` to sync to vault.│
└────────────────────────────────────────────────────────────────────────────┘
Dashboard Demo: Real-time ALLOW/DENY decisions as authorization requests flow through the sidecar.
| Key | Action |
|---|---|
j/k or ↑/↓ | Scroll through events |
f | Cycle filter: ALL → DENY → agent input |
/ | Enter agent filter mode directly |
c | Clear filter (show all events) |
P | Pause/resume live updates |
? | Toggle help overlay |
Q or Esc | Quit dashboard |
Filter authorization events in real-time to focus on specific patterns:
Press f once to show only denied events:
┌─ LIVE AUTHORITY GATE ──────────────────────────────────────────────────────┐
│ [FILTER: DENY ONLY] │
│ │
│ [ ✗ DENY ] agent:scraper │
│ fs.write → ~/.ssh/config │
│ EXPLICIT_DENY | 0.2ms │
│ │
│ [ ✗ DENY ] agent:web │
│ admin.delete → /users/123 │
│ NO_MATCHING_ALLOW | 0.3ms │
└────────────────────────────────────────────────────────────────────────────┘
Press f again (or / directly) to enter agent filter mode, then type the agent ID:
┌─ LIVE AUTHORITY GATE ──────────────────────────────────────────────────────┐
│ [FILTER: agent:web] │
│ │
│ [ ✓ ALLOW ] agent:web │
│ browser.navigate → github.com │
│ m_7f3a2b1c | 0.4ms │
│ │
│ [ ✓ ALLOW ] agent:web │
│ browser.click → button#submit │
│ m_8e4b3c2d | 0.5ms │
├────────────────────────────────────────────────────────────────────────────┤
│ Filter agent: web█ [Enter/Esc] │
└────────────────────────────────────────────────────────────────────────────┘
Filter supports partial matching—typing web matches agent:web, agent:web-scraper, etc.
Audit mode allows you to test policy changes without blocking actions. Denied requests are logged but allowed through, displayed as [ ⚠ WOULD DENY ] in yellow.
Option 1: CLI flag
./predicate-authorityd --policy-file policy.json --audit-mode dashboardOption 2: Auto-detection from policy filename
Policy files containing audit, dry-run, or dryrun in the filename automatically enable audit mode:
# These all auto-enable audit mode:
./predicate-authorityd --policy-file policy-audit.json dashboard
./predicate-authorityd --policy-file dry-run-policy.json dashboard
./predicate-authorityd --policy-file new-rules-dryrun.json dashboardWhen audit mode is active:
[AUDIT] status indicator[ ⚠ WOULD DENY ] in yellow instead of [ ✗ DENY ] in red┌────────────────────────────────────────────────────────────────────────────┐
│ PREDICATE AUTHORITY v0.5.0 MODE: strict [AUDIT] UPTIME: 15m [?] │
│ Policy: policy-audit.json Rules: 8 active [Q:quit P:pause] │
├─────────────────────────────────────────┬──────────────────────────────────┤
│ LIVE AUTHORITY GATE │ METRICS │
│ │ │
│ [ ✓ ALLOW ] agent:web │ Total Requests: 127 │
│ browser.navigate → github.com │ ├─ Allowed: 124 (97.6%)│
│ m_7f3a2b1c | 0.4ms │ └─ Would Block: 3 (2.4%)│
│ │ │
│ [ ⚠ WOULD DENY ] agent:scraper │ Throughput: 8.2 req/s │
│ fs.write → ~/.ssh/config │ Avg Latency: 0.6ms │
│ EXPLICIT_DENY | 0.2ms │ │
└─────────────────────────────────────────┴──────────────────────────────────┘
# Arguments BEFORE 'run' subcommand
./predicate-authorityd \
--host 127.0.0.1 \
--port 8787 \
--mode local_only \
--policy-file policy.json \
runUsing environment variables:
export PREDICATE_HOST=127.0.0.1
export PREDICATE_PORT=8787
export PREDICATE_MODE=local_only
export PREDICATE_POLICY_FILE=policy.json
./predicate-authorityd runUsing a config file:
# Generate example config
./predicate-authorityd init-config --output config.toml
# Run with config
./predicate-authorityd --config config.toml run./predicate-authorityd \
--host 127.0.0.1 \
--port 8787 \
--mode local_only \
--policy-file policy.json \
--identity-file ./local-identities.json \
runGET /healthGET /statusGET /metricsPOST /ledger/flush-nowGET /ledger/dead-letterPOST /ledger/requeueConnect to Predicate Authority control-plane for policy sync, revocation push, and audit forwarding:
export PREDICATE_API_KEY="your-api-key"
./predicate-authorityd \
--host 127.0.0.1 \
--port 8787 \
--mode cloud_connected \
--policy-file policy.json \
--control-plane-url https://api.predicatesystems.dev \
--tenant-id your-tenant \
--project-id your-project \
--predicate-api-key "$PREDICATE_API_KEY" \
--sync-enabled \
runThe sidecar supports multiple identity modes for token validation on authorization requests:
Use --identity-mode local-idp for self-issued JWT tokens with ephemeral task identities:
export LOCAL_IDP_SIGNING_KEY="your-signing-key"
./predicate-authorityd \
--host 127.0.0.1 \
--port 8787 \
--mode local_only \
--policy-file policy.json \
--identity-mode local-idp \
--local-idp-issuer "http://localhost/predicate-local-idp" \
--local-idp-audience "api://predicate-authority" \
runUse --identity-mode oidc for generic OIDC provider integration:
./predicate-authorityd \
--host 127.0.0.1 \
--port 8787 \
--mode cloud_connected \
--policy-file policy.json \
--identity-mode oidc \
--oidc-issuer "https://your-oidc-provider/.well-known/openid-configuration" \
--oidc-client-id "your-client-id" \
--oidc-audience "api://predicate-authority" \
runUse --identity-mode entra for Microsoft Entra ID (Azure AD) integration:
./predicate-authorityd \
--host 127.0.0.1 \
--port 8787 \
--mode cloud_connected \
--policy-file policy.json \
--identity-mode entra \
--entra-tenant-id "your-tenant-id" \
--entra-client-id "your-client-id" \
--entra-audience "api://predicate-authority" \
runUse --identity-mode okta for enterprise Okta integration with JWKS validation:
export OKTA_ISSUER="https://<org>.okta.com/oauth2/default"
export OKTA_CLIENT_ID="<okta-client-id>"
export OKTA_AUDIENCE="api://predicate-authority"
./predicate-authorityd \
--host 127.0.0.1 \
--port 8787 \
--mode cloud_connected \
--policy-file policy.json \
--identity-mode okta \
--okta-issuer "$OKTA_ISSUER" \
--okta-client-id "$OKTA_CLIENT_ID" \
--okta-audience "$OKTA_AUDIENCE" \
--okta-required-claims "sub,tenant_id" \
--okta-required-scopes "authority:check" \
--okta-allowed-tenants "tenant-a,tenant-b" \
--idp-token-ttl-s 300 \
--mandate-ttl-s 300 \
runSafety notes:
idp-token-ttl-s must be >= mandate-ttl-s (enforced at startup)cloud_connected mode, local or local-idp identity requires --allow-local-fallbackRun this in AgentIdentity repo to determine if your tenant supports IdP token exchange:
export OKTA_OBO_COMPAT_CHECK_ENABLED=1
# Tenant supports token exchange:
export OKTA_SUPPORTS_TOKEN_EXCHANGE=true
python3 -m pytest tests/test_okta_obo_compatibility.py -k "live_check_when_enabled"
# Tenant does not support token exchange:
export OKTA_SUPPORTS_TOKEN_EXCHANGE=false
python3 -m pytest tests/test_okta_obo_compatibility.py -k "live_check_when_enabled"If token exchange is not supported, delegation should use authority mandate delegation fallback.
Run this in AgentIdentity repo to validate Entra OBO capability:
export ENTRA_OBO_COMPAT_CHECK_ENABLED=1
# Tenant supports OBO and user assertion is available:
export ENTRA_SUPPORTS_OBO=true
export ENTRA_USER_ASSERTION="<user-assertion-jwt>"
python3 -m pytest tests/test_entra_obo_compatibility.py -k "live_check_when_enabled"
# Tenant does not support OBO (or grant not enabled):
export ENTRA_SUPPORTS_OBO=false
python3 -m pytest tests/test_entra_obo_compatibility.py -k "live_check_when_enabled"Demo script:
python examples/delegation/entra_obo_compat_demo.py \
--tenant-id "$ENTRA_TENANT_ID" \
--client-id "$ENTRA_CLIENT_ID" \
--client-secret "$ENTRA_CLIENT_SECRET" \
--scope "$ENTRA_SCOPE"If OBO is unavailable, delegation should use authority mandate delegation fallback.
Run this in AgentIdentity repo when using a generic OIDC provider:
export OIDC_COMPAT_CHECK_ENABLED=1
# Provider supports token exchange:
export OIDC_SUPPORTS_TOKEN_EXCHANGE=true
export OIDC_SUBJECT_TOKEN="<subject-access-token>"
python3 -m pytest tests/test_oidc_compatibility.py -k "live_check_when_enabled"
# Provider does not support token exchange:
export OIDC_SUPPORTS_TOKEN_EXCHANGE=false
python3 -m pytest tests/test_oidc_compatibility.py -k "live_check_when_enabled"Demo script:
python examples/delegation/oidc_compat_demo.py \
--issuer "$OIDC_ISSUER" \
--client-id "$OIDC_CLIENT_ID" \
--client-secret "$OIDC_CLIENT_SECRET" \
--audience "$OIDC_AUDIENCE" \
--scope "${OIDC_SCOPE:-authority:check}"If token exchange is unavailable, delegation should use authority mandate delegation fallback.
/status counters and queue depth.The sidecar can connect to the Predicate Authority control-plane for:
The default control-plane URL is https://api.predicatesystems.dev.
export PREDICATE_API_KEY="<api-key>"
./predicate-authorityd \
--host 127.0.0.1 \
--port 8787 \
--mode cloud_connected \
--policy-file policy.json \
--control-plane-url https://api.predicatesystems.dev \
--tenant-id "your-tenant" \
--project-id "your-project" \
--predicate-api-key "$PREDICATE_API_KEY" \
runNote: --control-plane-url defaults to https://api.predicatesystems.dev if not specified.
Use this in cloud_connected mode when you want active authority updates from control-plane:
export PREDICATE_API_KEY="<api-key>"
./predicate-authorityd \
--host 127.0.0.1 \
--port 8787 \
--mode cloud_connected \
--policy-file policy.json \
--control-plane-url https://api.predicatesystems.dev \
--tenant-id "your-tenant" \
--project-id "your-project" \
--predicate-api-key "$PREDICATE_API_KEY" \
--sync-enabled \
runQuick checks:
curl -s http://127.0.0.1:8787/status | jq '.control_plane_sync_poll_count, .control_plane_sync_update_count, .control_plane_sync_error_count, .control_plane_last_sync_error'
curl -s http://127.0.0.1:8787/metrics | rg "predicate_authority_control_plane_sync_total"Use this flow when running many sidecars (e.g., OpenClaw bot fleets) and you want:
curl -s -X POST "https://api.predicatesystems.dev/v1/sidecars/enroll" \
-H "Content-Type: application/json" \
-d '{
"tenant_id":"your-tenant",
"sidecar_id":"sidecar-bot-01",
"version":"1.0.0",
"api_key":"<enrollment-key>"
}' | jqResponse includes:
sidecar_token: Bearer token for sidecar-authenticated sync endpointsexpires_at: Token expiry timestampsidecar: Sidecar metadata (id/version/last_seen/active)curl -s -X POST "https://api.predicatesystems.dev/v1/sidecars/heartbeat" \
-H "Authorization: Bearer $SIDECAR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"version":"1.0.1"}' | jqRecommended cadence: every 15-30 seconds.
curl -s "https://api.predicatesystems.dev/v1/sync/authority-updates/sidecar?tenant_id=your-tenant&wait_timeout_s=15" \
-H "Authorization: Bearer $SIDECAR_TOKEN" | jqReturns policy + revocation snapshot and a sync_token.
Subscribe for near-real-time revocation events:
curl -N "https://api.predicatesystems.dev/v1/sync/authority-updates/stream?tenant_id=your-tenant&last_event_id=0" \
-H "Authorization: Bearer $SIDECAR_TOKEN"When operators trigger revocations (including global kill-switch), stream events are pushed with event: revocation.
curl -s "https://api.predicatesystems.dev/v1/sidecars?tenant_id=your-tenant&active_only=true" \
-H "Authorization: Bearer $TOKEN" | jqUse this to track active sidecar count, versions, and last-seen status.
event_idLocal sidecar logs are ephemeral by design (24h TTL, redacted payloads). For production audit requirements, connect to the control-plane.
See Audit Logging & Provenance for:
Use control-plane integrity endpoints to prove an event is included in a tenant audit set:
curl -s "http://127.0.0.1:8080/v1/audit/integrity/root?tenant_id=<tenant>" \
-H "Authorization: Bearer $TOKEN" | jq
curl -s "http://127.0.0.1:8080/v1/audit/integrity/proof/<event_id>?tenant_id=<tenant>" \
-H "Authorization: Bearer $TOKEN" | jq503 store_circuit_open:<operation>.503 event_stream_unavailable:<topic>.# trigger immediate flush
curl -s -X POST http://127.0.0.1:8787/ledger/flush-now \
-H "Content-Type: application/json" \
-d '{"max_items":50}' | jq
# inspect quarantined events
curl -s http://127.0.0.1:8787/ledger/dead-letter | jq
# requeue one item
curl -s -X POST http://127.0.0.1:8787/ledger/requeue \
-H "Content-Type: application/json" \
-d '{"queue_item_id":"q_abc123"}' | jq