Skip to content

PredicateSystems/predicate-authority-sidecar

predicate-authorityd

Zero-Trust Authorization for AI Agents. Sub-millisecond latency. No LLM required.


The Problem: Authorization ≠ Intent

When you connect an AI agent to an Identity Provider like Okta or Entra, it receives an access token—a passport that proves identity and carries static scopes like database:write.

But IdP scopes are coarse-grained. If a prompt injection tricks your agent into calling drop_database instead of update_record, your API executes the attack because the agent's token legitimately holds the database:write scope.

The IdP cannot evaluate context. It authorized the agent, not the action.

The Solution: Per-Action Work Visas

Predicate Authority adds a deterministic policy layer between your agent and your tools. Every action is evaluated in <1ms against explicit ALLOW/DENY rules before execution.

┌─────────────┐     ┌─────────────────────┐     ┌─────────────┐
│  AI Agent   │────▶│ predicate-authorityd│────▶│  Your Tools │
│             │     │      (Sidecar)      │     │             │
│  "Click X"  │     │  ALLOW/DENY in <1ms │     │  Execute    │
└─────────────┘     └─────────────────────┘     └─────────────┘
  • Fail-closed: No matching rule = DENY
  • Deterministic: No LLM, no probabilistic reasoning
  • Fast: p99 < 1ms authorization latency
  • Auditable: Cryptographic proof ledger for every decision

Terminal Dashboard

Watch authorization decisions in real-time with the built-in TUI:

TUI Dashboard

./predicate-authorityd --policy-file policy.json dashboard
┌────────────────────────────────────────────────────────────────────────────┐
│  PREDICATE AUTHORITY v0.5.1    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.│
└────────────────────────────────────────────────────────────────────────────┘

Keyboard: j/k scroll, f filter (DENY/agent), c clear filter, P pause, ? help, Q quit

Audit mode: Use --audit-mode or audit-only.json policy to show [ ⚠ WOULD DENY ] in yellow instead of blocking.


Quick Start

30 seconds to your first authorization decision.

# 1. Download (or cargo build --release)
curl -LO https://github.com/PredicateSystems/predicate-authority-sidecar/releases/latest/download/predicate-authorityd-darwin-arm64.tar.gz
tar -xzf predicate-authorityd-*.tar.gz && chmod +x predicate-authorityd

# 2. Create a policy
cat > policy.json << 'EOF'
{
  "rules": [
    {"name": "allow-browser-https", "effect": "allow", "principals": ["agent:*"], "actions": ["browser.*"], "resources": ["https://*"]},
    {"name": "deny-admin", "effect": "deny", "principals": ["agent:*"], "actions": ["admin.*"], "resources": ["*"]}
  ]
}
EOF

# 3. Start the sidecar
./predicate-authorityd --policy-file policy.json run

Test it:

# ALLOWED - browser action on HTTPS
curl -X POST http://127.0.0.1:8787/v1/authorize \
  -H "Content-Type: application/json" \
  -d '{"principal":"agent:web","action":"browser.click","resource":"https://example.com"}'
# {"allowed":true,"reason":"allowed"}

# DENIED - admin action blocked
curl -X POST http://127.0.0.1:8787/v1/authorize \
  -H "Content-Type: application/json" \
  -d '{"principal":"agent:web","action":"admin.delete","resource":"/users/123"}'
# {"allowed":false,"reason":"explicit_deny"}

Why This Exists

Traditional Auth Predicate Authority
"Agent can access database" "Agent can SELECT from orders table"
Scope granted at login Permission evaluated per-action
Trust the agent Trust the policy
Prompt injection = game over Prompt injection = blocked

Every rogue fs.write ~/.ssh/config gets intercepted. Every unauthorized API call gets logged. Every action has a cryptographic proof.


Documentation


Installation

Download Binary

Platform Binary
macOS ARM64 predicate-authorityd-darwin-arm64.tar.gz
macOS x64 predicate-authorityd-darwin-x64.tar.gz
Linux x64 predicate-authorityd-linux-x64.tar.gz
Linux x64 (musl) predicate-authorityd-linux-x64-musl.tar.gz
Windows x64 predicate-authorityd-windows-x64.zip
tar -xzf predicate-authorityd-*.tar.gz
chmod +x predicate-authorityd
./predicate-authorityd version

Build from Source

cargo build --release
./target/release/predicate-authorityd version

Install via pip (Python SDK)

pip install "predicate-authority[sidecar]"
predicate-download-sidecar

Commands

Command Description
run Start the authorization daemon
dashboard Start with interactive TUI
init-config Generate example TOML config
check-config Validate configuration
version Show version info

Policy Rules

Policies are JSON or YAML files with ALLOW/DENY rules:

{
  "rules": [
    {
      "name": "allow-browser-https",
      "effect": "allow",
      "principals": ["agent:*"],
      "actions": ["browser.*"],
      "resources": ["https://*"]
    },
    {
      "name": "deny-filesystem-writes",
      "effect": "deny",
      "principals": ["agent:*"],
      "actions": ["fs.write", "fs.delete"],
      "resources": ["*"]
    }
  ]
}

Evaluation order:

  1. DENY rules checked first (any match = blocked)
  2. ALLOW rules checked (must match + have required_labels)
  3. Default DENY (fail-closed)

Bundled templates: strict.json, read-only.json, ci-cd.json, permissive.json


Identity Providers

Integrate with your existing IdP for token validation:

Mode Use Case
local Development, no token required
local-idp Self-issued tokens, CI/CD, air-gapped
okta Enterprise Okta with JWKS
entra Microsoft Entra ID (Azure AD)
oidc Generic OIDC provider
# Okta example
./predicate-authorityd \
  --identity-mode okta \
  --okta-issuer "https://your-org.okta.com/oauth2/default" \
  --okta-client-id "your-client-id" \
  --okta-audience "api://predicate-authority" \
  --policy-file policy.json \
  run

API Endpoints

Endpoint Method Description
/v1/authorize POST Core authorization check
/v1/delegate POST Delegate mandate to sub-agent
/v1/execute POST Execute operation via sidecar (zero-trust mode)
/health GET Health check
/status GET Stats and status
/metrics GET Prometheus metrics
/policy/reload POST Hot-reload policy

Execution Proxying (Zero-Trust Mode)

The /v1/execute endpoint enables zero-trust execution where the sidecar executes operations on behalf of agents. This prevents "confused deputy" attacks where an agent requests authorization for one resource but accesses another.

Traditional (Cooperative):           Zero-Trust (Execution Proxy):
┌─────────┐  authorize  ┌─────────┐  ┌─────────┐  execute   ┌─────────┐
│  Agent  │────────────▶│ Sidecar │  │  Agent  │───────────▶│ Sidecar │
│         │◀────────────│         │  │         │◀───────────│         │
│         │   ALLOWED   │         │  │         │  result    │ (reads  │
│         │             │         │  │         │            │  file)  │
│  reads  │             │         │  └─────────┘            └─────────┘
│  file   │             │         │
│  (could │             │         │  Agent never touches the resource
│  cheat) │             │         │  directly - sidecar is the executor
└─────────┘             └─────────┘

Example: File Read via Execute Proxy

# 1. First authorize and get a mandate
curl -X POST http://127.0.0.1:8787/v1/authorize \
  -H "Content-Type: application/json" \
  -d '{"principal":"agent:web","action":"fs.read","resource":"/src/index.ts"}'
# Returns: {"allowed":true,"reason":"allowed","mandate_id":"m_abc123"}

# 2. Execute through the sidecar (agent never reads file directly)
curl -X POST http://127.0.0.1:8787/v1/execute \
  -H "Content-Type: application/json" \
  -d '{
    "mandate_id": "m_abc123",
    "action": "fs.read",
    "resource": "/src/index.ts"
  }'
# Returns: {"success":true,"result":{"type":"file_read","content":"...","size":1234,"content_hash":"sha256:..."}}

Supported Actions:

Action Payload Result
fs.read None FileRead { content, size, content_hash }
fs.write { type: "file_write", content, create?, append? } FileWrite { bytes_written, content_hash }
fs.list None FileList { entries: [{ name, type, size, modified? }], total_entries }
fs.delete { type: "file_delete", recursive? } FileDelete { paths_removed }
cli.exec { type: "cli_exec", command, args?, cwd?, timeout_ms? } CliExec { exit_code, stdout, stderr, duration_ms }
http.fetch { type: "http_fetch", method, headers?, body? } HttpFetch { status_code, headers, body, body_hash }
env.read { type: "env_read", keys: ["VAR_NAME"] } EnvRead { values: { "VAR_NAME": "..." } }

Security Guarantees:

  • Mandate must exist and not be expired
  • Requested action must match mandate's action
  • Requested resource must match mandate's resource scope
  • All executions logged to proof ledger with evidence hashes
  • fs.delete with recursive: true requires explicit policy allowlist
  • env.read only returns values for explicitly authorized keys in the policy

Roadmap: Planned Actions

The following actions are planned to support autonomous agent workflows:

Filesystem Operations

Action Priority Payload Result Rationale
fs.stat Medium { path } FsStat { size, modified, permissions, is_dir } Check file existence/metadata without reading content
fs.copy Medium { source, destination, overwrite? } FsCopy { bytes_copied } File duplication with policy enforcement
fs.move Medium { source, destination, overwrite? } FsMove { success } Atomic rename/move operations

Environment & Secrets

Action Priority Payload Result Rationale
env.list Low { pattern? } EnvList { keys: ["VAR1", "VAR2"] } List available env vars (names only, not values)

Process Management

Action Priority Payload Result Rationale
process.list Low { filter? } ProcessList { processes: [{ pid, name, cpu, memory }] } Visibility into running processes
process.kill Low { pid, signal? } ProcessKill { success } Governed process termination

Network Operations

Action Priority Payload Result Rationale
net.dns Low { hostname } NetDns { addresses: ["1.2.3.4"] } DNS resolution for network diagnostics
net.ping Low { host, count? } NetPing { reachable, latency_ms } Network connectivity checks

Note: All planned actions will follow the same mandate validation flow as existing actions


Performance

Metric Target Actual
Authorization latency < 1ms p99 0.2-0.8ms
Delegation issuance < 10ms p99 ~5ms
Revocation check < 1μs O(1) HashSet
Memory footprint < 50MB ~15MB idle

Configuration

Configuration via CLI args, environment variables, or TOML file:

[server]
host = "127.0.0.1"
port = 8787
mode = "local_only"

[policy]
file = "policy.json"
hot_reload = true

[logging]
level = "info"
format = "compact"

See User Manual for full configuration reference.


CLI Reference

Important: CLI arguments go before the subcommand.

# Correct
./predicate-authorityd --port 9000 --policy-file policy.json run

# Wrong
./predicate-authorityd run --port 9000
Full CLI options
GLOBAL OPTIONS:
  -c, --config <FILE>           TOML config file [env: PREDICATE_CONFIG]
      --host <HOST>             Bind host [default: 127.0.0.1]
      --port <PORT>             Bind port [default: 8787]
      --mode <MODE>             local_only or cloud_connected
      --policy-file <PATH>      Policy file (JSON/YAML)
      --log-level <LEVEL>       trace/debug/info/warn/error

IDENTITY OPTIONS:
      --identity-mode <MODE>    local/local-idp/oidc/entra/okta
      --idp-token-ttl-s <SECS>  Token TTL [default: 300]
      --mandate-ttl-s <SECS>    Mandate TTL [default: 300]

OKTA OPTIONS:
      --okta-issuer <URL>       Okta issuer URL
      --okta-client-id <ID>     Client ID
      --okta-audience <AUD>     Expected audience
      --okta-required-scopes    Required scopes (comma-separated)

CONTROL PLANE OPTIONS:
      --control-plane-url <URL> Control plane URL
      --tenant-id <ID>          Tenant ID
      --project-id <ID>         Project ID
      --predicate-api-key <KEY> API key
      --sync-enabled            Enable sync

Development

cargo test                    # Run tests
cargo test --test integration_test  # Integration tests
cargo bench                   # Run benchmarks
cargo clippy                  # Lints

License

MIT


Built for engineers who don't trust their agents.

About

The work visa for AI agents. A high-performance Rust sidecar that deterministicly blocks unauthorized actions before execution.

Topics

Resources

License

Unknown and 2 other licenses found

Licenses found

Unknown
LICENSE
Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

 
 
 

Contributors