Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Conformance

# The cross-language conformance suite is the only thing that catches the two
# reference implementations drifting apart on the wire format. It ran on
# neither push nor PR until 2026-08, and skipped silently without
# biscuit-python, which is how the implementations diverged unnoticed.

on:
push:
branches: ["**"]
pull_request:

jobs:
conformance:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2
with:
workspaces: rust

- name: Install Python package
working-directory: python
run: pip install -e .[dev]

- name: Fail if biscuit-python is missing
# The conformance tests skip without it. A silent skip reads as a pass.
run: python -c "import biscuit_auth"

- name: Build Rust interop binaries
working-directory: rust
run: cargo build --bin create_chained_token --bin verify_chained_token

- name: Cross-language conformance
run: python -m pytest tests/conformance/ -v

- name: Python unit tests
working-directory: python
run: python -m pytest tests/ -v --ignore=tests/test_aip_agents/

- name: Rust tests
working-directory: rust
run: cargo test
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ Rust reference implementation available in `rust/`.
> Sunil Prakash. **AIP: Agent Identity Protocol for Verifiable Delegation Across MCP and A2A.** arXiv preprint arXiv:2603.24775, 2026.
> [https://arxiv.org/abs/2603.24775](https://arxiv.org/abs/2603.24775)

- **IETF Internet-Draft:** [draft-prakash-aip-00](https://datatracker.ietf.org/doc/draft-prakash-aip/) (expires 2026-09-28)
- **IETF Internet-Draft:** [draft-prakash-aip-01](https://datatracker.ietf.org/doc/draft-prakash-aip/) (expires 2027-02-20)
- **NIST:** Under evaluation for the NCCoE agent identity demonstration project

```bibtex
Expand Down
4 changes: 4 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ Defines the AIP identity scheme, including agent identifiers, identity documents

Defines the two token formats used in AIP. Compact tokens are JWTs signed with Ed25519, suitable for single-hop authentication where no delegation is needed. Chained tokens use the Biscuit format to support multi-hop delegation with cryptographic scope attenuation. Also defines the three policy profiles (Simple, Standard, Advanced) and the verification rules for each.

### [spec/aip-verification.md](spec/aip-verification.md) -- Verification

Defines the normative algorithm a verifier performs on a token: steps V1 through V7, covering chain integrity, root binding, depth, the structural attenuation walk, delegation context, policy evaluation, and revocation with bounded staleness. Specifies the ordering between them and the error code each failure returns. Also states what a passing verification does and does not establish, in particular that an AIP token is a bearer credential and verification does not authenticate the presenter.

### [spec/aip-delegation.md](spec/aip-delegation.md) -- Delegation

Specifies the rules for delegation chains, including how an agent appends a new block to a chained token that narrows the granted scope. Covers ephemeral grants for short-lived, tightly scoped delegations and defines the full delegation lifecycle from initial grant through chain extension to expiration. Includes formal rules for scope attenuation validation.
Expand Down
12 changes: 6 additions & 6 deletions python/aip_a2a/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,18 @@ def verify_a2a_task(
f"final delegation targets {final_delegate!r}, expected {expected_audience!r}"
)

# Scope check: ChainedToken.authorize() re-verifies the full chain and
# evaluates every block's `check if right(...)` rules — this enforces
# scope attenuation across delegation hops.
# Scope check: ChainedToken.authorize() runs the verification algorithm in
# draft-prakash-aip-01 Section 4, which re-verifies every block signature
# and walks the chain confirming each hop narrows its parent.
try:
token.authorize(required_scope, root_public_key_bytes)
except TokenError as exc:
if exc.code == "aip_token_expired":
if exc.code == "token_expired":
raise ExpiryError() from exc
if exc.code == "scope_insufficient":
raise ScopeError(required_scope) from exc
raise ChainError(str(exc)) from exc
except Exception as exc:
# biscuit_auth.AuthorizationError or similar — the requested scope is not
# authorized at some point in the chain.
raise ScopeError(required_scope) from exc

return VerifiedIdentity(
Expand Down
6 changes: 3 additions & 3 deletions python/aip_mcp/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ def audit_chained(token: "ChainedToken") -> AuditResult:
# stable introspection API exposed by biscuit-python.
try:
source = token._biscuit.block_source(0)
if source and "budget(" in source:
if source and "budget_ceiling(" in source:
for line in source.split("\n"):
line = line.strip().rstrip(";")
if line.startswith("budget("):
if line.startswith("budget_ceiling("):
# Extract integer between budget( and )
inner = line[len("budget("):-1]
inner = line[len("budget_ceiling("):-1]
try:
cents = int(inner)
if cents > HIGH_BUDGET_CENTS:
Expand Down
Loading
Loading