From 4bec0227ec77b04f1f4f91e780449672a1154128 Mon Sep 17 00:00:00 2001 From: SentienceDEV Date: Tue, 17 Feb 2026 07:12:38 -0800 Subject: [PATCH] user manual --- docs/predicate-authority-user-manual.md | 323 ++++++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 docs/predicate-authority-user-manual.md diff --git a/docs/predicate-authority-user-manual.md b/docs/predicate-authority-user-manual.md new file mode 100644 index 0000000..b332f64 --- /dev/null +++ b/docs/predicate-authority-user-manual.md @@ -0,0 +1,323 @@ +# Predicate Authority User Manual + +This guide explains how to use `predicate-authority` in real projects with +practical examples. It is written for application developers and platform +operators who want deterministic, pre-execution authorization for AI agents. + +--- + +## What is `predicate-authority`? + +`predicate-authority` is an authorization layer for AI agents. + +It checks risky actions **before execution** (fail-closed by default), issues +short-lived mandates for allowed actions, and records proof events for audit. + +Core use cases: + +- protect outbound API calls, +- protect tool invocation (MCP/function calls), +- enforce policy invariants using state and verification evidence, +- centralize revocation and operational controls through a sidecar. + +--- + +## Package overview + +- `predicate_contracts`: shared typed contracts and protocols. +- `predicate_authority`: policy engine, guard, sidecar, identity bridge, + revocation, proof ledger. +- `predicate-authorityd`: optional local sidecar daemon (CLI service). + +--- + +## Install + +```bash +pip install predicate-contracts predicate-authority +``` + +For local development from source: + +```bash +cd /path/to/AgentIdentity +pip install -e ./predicate_contracts +pip install -e ./predicate_authority +``` + +--- + +## Fastest local validation path (Day 1) + +Use this path if you want fast feedback with minimal setup. + +You do **not** need: + +- Entra ID / enterprise IdP setup, +- two browser agents, +- hosted control plane. + +### Step 1: run one local authorize/deny script + +- Use in-process `ActionGuard` with a tiny local policy. +- Build one allowed request and one denied request. +- Confirm deny reason is deterministic. + +### Step 2: simulate delegation with two Python scripts + +- Script A (root) requests a mandate with limited delegation depth. +- Script B (worker) uses the received token and attempts delegated action. +- Validate expected behavior for: + - valid delegation, + - over-depth delegation (must fail), + - revoked root/intent (must fail). + +### Step 3: optional sidecar smoke test + +- Start `predicate-authorityd` in local mode. +- Call `/status`, `/ledger/flush-now`, and `/ledger/dead-letter`. +- Confirm operations safety endpoints work before enterprise integration. + +When this passes, add enterprise IdP (OIDC/Entra) and real web-agent E2E flows. + +--- + +## Mental model + +1. Build an `ActionRequest` from current agent context. +2. Call `ActionGuard.authorize(request)` (or sidecar equivalent). +3. If allowed, execute action (with mandate attached if needed). +4. If denied, stop action and handle deny reason. +5. Emit/store proof events for governance. + +--- + +## Quick start (in-process guard) + +```python +from predicate_authority import ActionGuard, InMemoryProofLedger, LocalMandateSigner, PolicyEngine +from predicate_contracts import ( + ActionRequest, + ActionSpec, + PolicyEffect, + PolicyRule, + PrincipalRef, + StateEvidence, + VerificationEvidence, + VerificationSignal, + VerificationStatus, +) + +rules = ( + PolicyRule( + name="allow-orders-create", + effect=PolicyEffect.ALLOW, + principals=("agent:checkout",), + actions=("http.post",), + resources=("https://api.vendor.com/orders",), + required_labels=("on_checkout_page",), + ), +) + +guard = ActionGuard( + policy_engine=PolicyEngine(rules=rules), + mandate_signer=LocalMandateSigner(secret_key="replace-with-strong-secret"), + proof_ledger=InMemoryProofLedger(), +) + +request = ActionRequest( + principal=PrincipalRef(principal_id="agent:checkout", tenant_id="tenant-a"), + action_spec=ActionSpec( + action="http.post", + resource="https://api.vendor.com/orders", + intent="submit customer order", + ), + state_evidence=StateEvidence(source="sdk-python", state_hash="sha256:abc123"), + verification_evidence=VerificationEvidence( + signals=( + VerificationSignal( + label="on_checkout_page", + status=VerificationStatus.PASSED, + required=True, + ), + ) + ), +) + +decision = guard.authorize(request) +if not decision.allowed: + raise RuntimeError(f"Denied: {decision.reason.value}") + +print("Allowed. mandate_id=", decision.mandate.claims.mandate_id if decision.mandate else None) +``` + +--- + +## Policy basics + +A policy rule matches: + +- principal (`agent:*`, `agent:checkout`, etc.), +- action (`http.post`, `tool.execute`, `browser.click`), +- resource (`https://...`, `mcp://...`, etc.), +- optional required verification labels. + +If no allow rule matches, behavior is deny (`NO_MATCHING_POLICY`). + +Common deny reasons: + +- `NO_MATCHING_POLICY` +- `EXPLICIT_DENY` +- `MISSING_REQUIRED_VERIFICATION` +- `INVALID_MANDATE` + +--- + +## Using the sidecar (`predicate-authorityd`) + +Start local sidecar with file policy: + +```bash +PYTHONPATH=. predicate-authorityd \ + --host 127.0.0.1 \ + --port 8787 \ + --mode local_only \ + --policy-file examples/authorityd/policy.json +``` + +Health and status: + +```bash +curl -s http://127.0.0.1:8787/health | jq +curl -s http://127.0.0.1:8787/status | jq +``` + +--- + +## Identity modes + +`predicate-authorityd` supports multiple identity bridge modes: + +- `local` (default): local deterministic bridge, +- `local-idp`: local IdP-style signed token mode (offline/dev/air-gapped), +- `oidc`: enterprise OIDC bridge, +- `entra`: Microsoft Entra bridge. + +Example (`local-idp`): + +```bash +export LOCAL_IDP_SIGNING_KEY="replace-with-strong-secret" +predicate-authorityd \ + --host 127.0.0.1 \ + --port 8787 \ + --mode local_only \ + --policy-file examples/authorityd/policy.json \ + --identity-mode local-idp \ + --local-idp-issuer "http://localhost/predicate-local-idp" \ + --local-idp-audience "api://predicate-authority" +``` + +--- + +## Local identity registry + flush queue + +Enable ephemeral task identity registry and local ledger queue: + +```bash +PYTHONPATH=. predicate-authorityd \ + --host 127.0.0.1 \ + --port 8787 \ + --mode local_only \ + --policy-file examples/authorityd/policy.json \ + --local-identity-enabled \ + --local-identity-registry-file ./.predicate-authorityd/local-identities.json \ + --local-identity-default-ttl-s 900 \ + --flush-worker-enabled \ + --flush-worker-interval-s 2.0 \ + --flush-worker-max-batch-size 50 \ + --flush-worker-dead-letter-max-attempts 5 +``` + +Useful endpoints: + +- `POST /identity/task` +- `GET /identity/list` +- `POST /identity/revoke` +- `GET /ledger/flush-queue` +- `POST /ledger/flush-now` +- `GET /ledger/dead-letter` +- `POST /ledger/requeue` + +--- + +## Operations safety patterns + +Recommended production defaults: + +- keep fail-closed for protected actions, +- use dead-letter threshold to quarantine persistent failures, +- expose `/status` metrics to monitoring, +- provide runbooks for manual flush and dead-letter requeue. + +Example manual recovery: + +```bash +# 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 items +curl -s http://127.0.0.1:8787/ledger/dead-letter | jq + +# requeue a quarantined 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 +``` + +--- + +## `sdk-python` integration example (boundary adapter flow) + +If your web agent uses `sdk-python`, build shared contract evidence before +calling authority: + +```python +from predicate.agent_runtime import AgentRuntime + +# after snapshot + assertions +request = runtime.build_authority_action_request( + principal_id="agent:web-checkout", + action="browser.click", + resource="https://example.com/checkout", + intent="click submit order", + tenant_id="tenant-a", +) + +# send request to your authority hook/client +decision = my_authorizer(request) +if not decision.allowed: + raise RuntimeError("Denied by authority") +``` + +--- + +## Troubleshooting + +- Denied with `MISSING_REQUIRED_VERIFICATION`: + - ensure required labels are present and `PASSED` in evidence. +- Denied with `NO_MATCHING_POLICY`: + - verify principal/action/resource match patterns in active policy. +- Token exchange errors in connected mode: + - verify identity mode config and credential/refresh-token setup. +- Queue not draining: + - check `/status` flush counters and control-plane connectivity. + +--- + +## Where to go next + +- Operations guide: `docs/authorityd-operations.md` +- Architecture proposal: `docs/predicate_authority_docs/better-sdk-opportunity-proposal.md` +- Governance sign-off tracker: `docs/predicate_authority_docs/governance-signoff-tracker.md`