Metadata-Version: 2.4
Name: a2spa
Version: 2.4.1
Summary: A2SPA payload helper library for AI-agent runtime authorization
Author: AI Modularity
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://aimodularity.com/A2SPA
Project-URL: Documentation, https://aimodularity.com/A2SPA/docs
Project-URL: Source, https://github.com/gavincapriola/A2SPA
Project-URL: Changelog, https://github.com/gavincapriola/A2SPA/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/gavincapriola/A2SPA/issues
Keywords: a2spa,ai-agents,authorization,payload-helpers,signed-payloads,runtime-security
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: <3.13,>=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography==50.0.2
Provides-Extra: pq
Requires-Dist: pqcrypto==1.0.0; extra == "pq"
Dynamic: license-file

# A2SPA Python Payload Helpers

Helper-only Python package for building, hashing, and signing A2SPA runtime authorization payloads.

The verifier remains server-side. This package does not authorize actions by itself.

## Install

```bash
pip install a2spa
```

For ML-DSA signing support where the optional wheel is available:

```bash
pip install "a2spa[pq]"
```

## Basic Usage

```python
from pathlib import Path

from a2spa import build_payload_fields, build_signed_request, policy_input

private_key_text = Path("sender.priv.pem").read_text(encoding="utf-8")

payload = build_payload_fields(
    agent_id="user-123_sender",
    target_agent_id="user-123_receiver",
    input_data=policy_input(action="deliver", workflow_scope="messages:deliver"),
)

request_body = build_signed_request(private_key_text, payload)
```

Send `request_body` to `POST /v1/verify_payload` with your account API key in the `x-api-key` header. The previous `/api/verify_payload` path remains a deprecated compatibility alias.

See the [A2SPA 2.4 migration guide](https://aimodularity.com/A2SPA/docs#api-versioning) for route compatibility and `docs/a2spa-2.4-migration.md` in the server source repository for detailed retry and rollback guidance.

## Helper Functions

- `build_payload_fields(...)`
- `policy_input(...)`
- `policy_template(...)`
- `state_continuity_claim(...)`
- `canonicalize_payload(...)`
- `compute_payload_hash(...)`
- `finalize_payload(...)`
- `sign_payload(...)`
- `build_signed_request(...)`
- `is_authorized_result(...)`
- `require_authorized_result(...)`
- `verify_execution_receipt(...)`
- `enforce_target_authorization(...)`
- `build_target_acknowledgement(...)`
- `execute_protected_action(...)`
- `InMemoryTargetOperationLedger(...)`
- `SQLiteTargetOperationLedger(...)`
- `InMemoryPolicyUsageLedger(...)`
- `SQLitePolicyUsageLedger(...)`
- `policy_constraints_input(...)`
- `evaluate_policy_constraints(...)`
- `build_authorization_evidence_package(...)`
- `normalize_agent_lifecycle(...)`
- `agent_is_active(...)`
- `sign_delegation_grant(...)`
- `validate_delegation_chain(...)`
- `build_consumption_state_record(...)`
- `verify_consumption_state_record(...)`
- `build_debug_snapshot(...)`

## Signed Bytes

Current helper compatibility signs the UTF-8 bytes of `json.dumps(signable_payload, sort_keys=True)` using the normalized signable fields. Do not reimplement canonicalization unless your bytes match `canonicalize_payload(...)` exactly.

## Security Boundary

Private keys must stay in a secret manager, server environment, or deployment secret. Do not paste private keys into chats, logs, client-side code, or source control.

## A2SPA 2.1 Target Enforcement

A2SPA 2.1 adds a deployment profile for protected targets. The target, trusted gateway, service, or executor should fail closed unless the A2SPA authorization result is valid for the exact operation being committed.

```python
from a2spa import SQLiteTargetOperationLedger, execute_protected_action

operation_id = message["idempotency_key"]
operation_ledger = SQLiteTargetOperationLedger("target-operations.sqlite3")
execution = execute_protected_action(
    result,
    lambda authorization: {
        "provider_message_id": email_provider.send(message).id,
    },
    target_id="smtp-gateway",
    operation_id=operation_id,
    target_private_key_pem=target_ack_private_key,
    target_agent_id="user-123_email-target",
    action="send_email",
    workflow_scope="email:send",
    trusted_receipt_public_key_pem=trusted_a2spa_receipt_public_key,
    operation_ledger=operation_ledger,
    require_target_idempotency=True,
)
```

`SQLiteTargetOperationLedger` is a durable single-node implementation. Multi-instance targets should supply a transactional shared-store adapter with the same `begin_operation`, `commit_operation`, `cancel_operation`, and `fail_operation` contract. In-progress or indeterminate operations fail closed; an exact committed retry returns the stored execution without invoking the operation again.

Policies containing `usage.max_uses` or `usage.rate_limit` also require a target-owned policy usage ledger and an operation ledger. `execute_protected_action(...)` atomically reserves the use before invoking the callback. The bundled SQLite implementation is single-node; multi-instance targets must supply a transactional shared-store adapter with equivalent `usage_context` and `reserve_usage` behavior.

If the agent can reach another route that commits the same protected consequence without this guard, the deployment fails the A2SPA 2.1 profile.

## A2SPA 2.2-2.4 Capability Helpers

The Python package also exposes dependency-light helpers for granular policy constraints, evidence packages, signed delegation chains, and HMAC-protected state-integrity records.

```python
from a2spa import build_action_context, evaluate_policy_constraints, policy_constraints_input

policy = policy_constraints_input(
    resource="dataset:customers:acme",
    environment="production",
    parameters={"requested_fields": {"subset": ["name", "status"]}},
)

result = evaluate_policy_constraints(
    policy,
    build_action_context(
        resource="dataset:customers:acme",
        environment="production",
        parameters={"requested_fields": ["name"]},
    ),
)
assert result["allowed"] is True
```

These helpers are local verification/building primitives. Production deployments still need explicit trust-anchor operation, revocation freshness, protected state-integrity keys, and target-side enforcement at every side-effect path.
