Skip to content

Commit 7cb9507

Browse files
committed
readme
1 parent 9f470be commit 7cb9507

22 files changed

Lines changed: 919 additions & 10 deletions
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
name: phase1-ci-and-release
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: ["main", "phase1"]
7+
workflow_dispatch:
8+
inputs:
9+
publish:
10+
description: "Run ordered publish jobs"
11+
required: true
12+
default: "false"
13+
type: choice
14+
options: ["false", "true"]
15+
16+
jobs:
17+
quality:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Set up Python
24+
uses: actions/setup-python@v5
25+
with:
26+
python-version: "3.11"
27+
28+
- name: Install dependencies
29+
run: python -m pip install --upgrade pip pre-commit pytest
30+
31+
- name: Verify package release order
32+
run: python scripts/verify_release_order.py
33+
34+
- name: Run tests
35+
run: python -m pytest -q
36+
37+
- name: Run pre-commit checks
38+
run: pre-commit run --all-files
39+
40+
publish-predicate-contracts:
41+
runs-on: ubuntu-latest
42+
needs: [quality]
43+
if: github.event_name == 'workflow_dispatch' && inputs.publish == 'true'
44+
steps:
45+
- name: Checkout
46+
uses: actions/checkout@v4
47+
48+
- name: Set up Python
49+
uses: actions/setup-python@v5
50+
with:
51+
python-version: "3.11"
52+
53+
- name: Install build tooling
54+
run: python -m pip install --upgrade pip build twine
55+
56+
- name: Verify release order
57+
run: python scripts/verify_release_order.py
58+
59+
- name: Build predicate-contracts
60+
run: python -m build predicate_contracts
61+
62+
- name: Validate distribution metadata
63+
run: twine check predicate_contracts/dist/*
64+
65+
- name: Publish predicate-contracts to PyPI
66+
env:
67+
TWINE_USERNAME: __token__
68+
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN_PREDICATE_CONTRACTS }}
69+
run: twine upload predicate_contracts/dist/*
70+
71+
publish-predicate-authority:
72+
runs-on: ubuntu-latest
73+
needs: [publish-predicate-contracts]
74+
if: github.event_name == 'workflow_dispatch' && inputs.publish == 'true'
75+
steps:
76+
- name: Checkout
77+
uses: actions/checkout@v4
78+
79+
- name: Set up Python
80+
uses: actions/setup-python@v5
81+
with:
82+
python-version: "3.11"
83+
84+
- name: Install build tooling
85+
run: python -m pip install --upgrade pip build twine
86+
87+
- name: Verify release order
88+
run: python scripts/verify_release_order.py
89+
90+
- name: Build predicate-authority
91+
run: python -m build predicate_authority
92+
93+
- name: Validate distribution metadata
94+
run: twine check predicate_authority/dist/*
95+
96+
- name: Publish predicate-authority to PyPI
97+
env:
98+
TWINE_USERNAME: __token__
99+
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN_PREDICATE_AUTHORITY }}
100+
run: twine upload predicate_authority/dist/*

.github/workflows/tests.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: tests
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: ["main", "phase1"]
7+
workflow_dispatch:
8+
9+
jobs:
10+
pytest:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
python-version: ["3.11", "3.12"]
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Python
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
26+
- name: Install test dependencies
27+
run: python -m pip install --upgrade pip pytest
28+
29+
- name: Run tests
30+
run: python -m pytest -q

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ traces/
5555
artifacts/
5656
tmp/
5757
temp/
58+
59+
# Local build artifacts from package-level builds
60+
predicate_authority/predicate_authority/
61+
predicate_contracts/predicate_contracts/

Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: hooks lint test format format-python format-docs lint-docs
1+
.PHONY: hooks lint test examples verify-release-order build-packages format format-python format-docs lint-docs
22

33
hooks:
44
pre-commit install
@@ -9,6 +9,18 @@ lint:
99
test:
1010
python -m pytest -q
1111

12+
examples:
13+
PYTHONPATH=. python examples/browser_guard_example.py
14+
PYTHONPATH=. python examples/mcp_tool_guard_example.py
15+
PYTHONPATH=. python examples/outbound_http_guard_example.py
16+
17+
verify-release-order:
18+
python scripts/verify_release_order.py
19+
20+
build-packages:
21+
python -m build predicate_contracts
22+
python -m build predicate_authority
23+
1224
format: format-python format-docs
1325

1426
format-python:

README.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Predicate Authority
2+
3+
**Deterministic Authority for AI Agents: Secure the "Confused Deputy" with your existing Identity stack.**
4+
5+
[![License](https://img.shields.io/badge/License-MIT%2FApache--2.0-blue.svg)](LICENSE)
6+
[![PyPI - predicate-authority](https://img.shields.io/pypi/v/predicate-authority.svg)](https://pypi.org/project/predicate-authority/)
7+
[![PyPI - predicate-contracts](https://img.shields.io/pypi/v/predicate-contracts.svg)](https://pypi.org/project/predicate-contracts/)
8+
9+
`predicate-authority` is a production-grade pre-execution authority layer that binds AI agent identity to deterministic state. It bridges standard IdPs (Entra ID, Okta, OIDC) with runtime verification so every sensitive action is authorized, bounded, and provable.
10+
11+
## Why Predicate Authority?
12+
13+
Most agent security fails because it relies on static API keys or broad permissions. Predicate introduces short-lived mandates that are cryptographically tied to:
14+
15+
- `state_hash` (what state the agent is in),
16+
- `intent_hash` (what action it intends to perform),
17+
- policy constraints and required verification labels.
18+
19+
This closes the confused-deputy gap where an agent can misuse delegated credentials.
20+
21+
- **Bridge, don't replace**: leverage existing enterprise identity and governance.
22+
- **Fail-closed by design**: deny before execution when state/intent/policy checks fail.
23+
- **Deterministic binding**: authority is tied to runtime evidence, not only identity.
24+
- **Provable controls**: each decision can emit signed proof events for audit pipelines.
25+
26+
## Repository Components
27+
28+
| Package | Purpose |
29+
| --- | --- |
30+
| `predicate_contracts` | Shared typed contracts and protocols (`ActionRequest`, `PolicyRule`, evidence, decision/proof models). |
31+
| `predicate_authority` | Runtime authorization engine (`PolicyEngine`, `ActionGuard`, mandate signing, proof ledger, telemetry emitter). |
32+
| `examples/` | Browser/MCP/HTTP guard examples using the local Phase 1 runtime. |
33+
34+
## Phase 1 Status
35+
36+
Implemented in this repository:
37+
38+
- local pre-execution `ActionGuard.authorize(...)` and `enforce(...)`,
39+
- signed local mandates with TTL (`LocalMandateSigner`),
40+
- policy evaluation with deny precedence and required verification labels,
41+
- typed `sdk-python` integration adapter (`predicate_authority.integrations`),
42+
- OpenTelemetry-compatible trace emitter (`OpenTelemetryTraceEmitter`),
43+
- pytest coverage for core authorization, mandate, integration, and telemetry flows.
44+
45+
Planned in upcoming phases:
46+
47+
- `predicate-authorityd` sidecar for token lifecycle and local kill-switch,
48+
- enterprise IdP bridge hardening (Entra/Okta/OIDC adapters),
49+
- hosted governance control plane.
50+
51+
## Installation
52+
53+
```bash
54+
pip install predicate-authority
55+
```
56+
57+
For shared contracts directly:
58+
59+
```bash
60+
pip install predicate-contracts
61+
```
62+
63+
## Quick Start (Phase 1 API)
64+
65+
```python
66+
from predicate_authority import ActionGuard, InMemoryProofLedger, LocalMandateSigner, PolicyEngine
67+
from predicate_contracts import (
68+
ActionRequest,
69+
ActionSpec,
70+
PolicyEffect,
71+
PolicyRule,
72+
PrincipalRef,
73+
StateEvidence,
74+
VerificationEvidence,
75+
)
76+
77+
guard = ActionGuard(
78+
policy_engine=PolicyEngine(
79+
rules=(
80+
PolicyRule(
81+
name="allow-payment-submit",
82+
effect=PolicyEffect.ALLOW,
83+
principals=("agent:payments",),
84+
actions=("http.post",),
85+
resources=("https://finance.example.com/transfers",),
86+
),
87+
)
88+
),
89+
mandate_signer=LocalMandateSigner(secret_key="dev-secret"),
90+
proof_ledger=InMemoryProofLedger(),
91+
)
92+
93+
request = ActionRequest(
94+
principal=PrincipalRef(principal_id="agent:payments"),
95+
action_spec=ActionSpec(
96+
action="http.post",
97+
resource="https://finance.example.com/transfers",
98+
intent="submit transfer request #1234",
99+
),
100+
state_evidence=StateEvidence(source="backend", state_hash="state-hash-abc"),
101+
verification_evidence=VerificationEvidence(),
102+
)
103+
104+
decision = guard.authorize(request)
105+
if not decision.allowed:
106+
raise RuntimeError(f"Authority denied: {decision.reason.value}")
107+
```
108+
109+
See runnable examples in:
110+
111+
- `examples/browser_guard_example.py`
112+
- `examples/mcp_tool_guard_example.py`
113+
- `examples/outbound_http_guard_example.py`
114+
115+
## Security: Local Kill-Switch Path
116+
117+
The current Phase 1 runtime supports fail-closed checks and local proof emission. The sidecar model (`predicate-authorityd`) is planned to provide instant local revocation and managed token lifecycle for long-running production agents.
118+
119+
## Release
120+
121+
- CI workflow: `.github/workflows/phase1-ci-and-release.yml`
122+
- Release guide: `docs/pypi-release-guide.md`
123+
124+
Publish order is always:
125+
126+
1. `predicate-contracts`
127+
2. `predicate-authority`
128+
129+
## License
130+
131+
Dual-licensed under **MIT** and **Apache 2.0**:
132+
133+
- `LICENSE-MIT`
134+
- `LICENSE-APACHE`
135+
136+
---
137+
138+
Copyright (c) 2026 Predicate Systems Inc.

docs/better-sdk-opportunity-proposal.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -532,11 +532,9 @@ Status (as of 2026-02-16): **in progress (MVP scaffold implemented in this `pred
532532
- Local policy evaluation and normalized deny reasons.
533533
- In-memory proof ledger with optional trace emitter interface.
534534
- pytest coverage for policy, mandate signing, and proof emission paths.
535-
- Pending for full Phase 1 exit:
536-
- direct `sdk-python` integration hooks (pre-action + postcondition linkage),
537-
- OpenTelemetry-native event export (beyond protocol-level trace emitter),
538-
- developer quickstart/examples for browser/MCP/HTTP guard patterns,
539-
- package publishing pipeline verification (`predicate-contracts` -> `predicate-authority`).
535+
- Remaining to close full Phase 1 exit:
536+
- connect CI publish jobs to real package build/publish steps and credentials,
537+
- publish first `predicate-contracts` and `predicate-authority` versions in dependency order.
540538

541539
## Phase 2: Sidecar and IdP bridge (4-8 weeks)
542540

@@ -674,10 +672,10 @@ Current status: **in progress**
674672
- [x] local policy evaluation.
675673
- [x] fail-closed deny path with normalized reason enums.
676674
- [x] deterministic regression tests for authorize/deny paths.
677-
- [ ] `sdk-python` runtime integration hooks.
678-
- [ ] OpenTelemetry-native authority event export.
679-
- [ ] quickstart/examples for browser/MCP/outbound HTTP.
680-
- [ ] dependency-ordered package publish pipeline in CI.
675+
- [x] `sdk-python` runtime integration hooks (typed adapter path).
676+
- [x] OpenTelemetry-native authority event export.
677+
- [x] quickstart/examples for browser/MCP/outbound HTTP.
678+
- [x] dependency-ordered package publish pipeline in CI (workflow scaffold).
681679

682680
## Phase 2: Sidecar + Identity Bridge (4-6 weeks)
683681

0 commit comments

Comments
 (0)