Skip to main content

AAC sidecar-to-agent invoke-push authentication (AAC1-HMAC-SHA256) — signing and verification core, with FastAPI mounting surfaces.

Project description

aac-invoke-auth

Authenticate /invoke pushes from your paired AAC sidecar.

When your agent runs behind an AAC sidecar, the sidecar verifies the cross-organizational authority chain and then POSTs the verified context to your agent's /invoke endpoint. The X-AAC-* headers on that push are your agent's entire view of the verified chain — so your agent must be able to tell a genuine sidecar push from a forged one. This package is that check: the sidecar HMAC-signs every push with a pairing secret shared with exactly one agent, and your agent verifies the signature before trusting anything.

The verification core is standard-library only (hmac, hashlib) and framework-agnostic. FastAPI mounting surfaces ship behind an extra:

pip install aac-invoke-auth[fastapi]

Quick start (FastAPI)

Generate the pairing secret once and share the file with your sidecar (the sidecar's sidecar.agent_invoke_auth.secret_file points at the same file; one secret per sidecar–agent pair, never shared across pairs):

openssl rand -hex 32 > /etc/aac/invoke-auth/invoke-auth.secret

Point your agent at it and mount the middleware:

export AAC_INVOKE_AUTH_SECRET_FILE=/etc/aac/invoke-auth/invoke-auth.secret
from fastapi import FastAPI
from aac_invoke_auth.fastapi import InvokeAuthGuard, InvokeAuthMiddleware

app = FastAPI()
app.add_middleware(
    InvokeAuthMiddleware,
    guard=InvokeAuthGuard.from_env(),
    protected_paths=("/invoke",),   # required, exact-match
)

That's the whole integration. Unsigned, tampered, replayed-stale, or wrong-secret pushes get a 401 with a generic detail and never reach your business logic; your handlers read request.json() unchanged.

Prefer route-scoped protection? The guard is also a plain FastAPI dependency:

from fastapi import Depends

guard = InvokeAuthGuard.from_env()

@app.post("/invoke", dependencies=[Depends(guard)])
async def invoke(request: Request): ...

Both surfaces call the same verifier — pick whichever fits your app.

Fail-fast startup, by design

InvokeAuthGuard.from_env() refuses to start when AAC_INVOKE_AUTH_SECRET_FILE is unset. A missing secret is a security downgrade; it should fail your deployment loudly at boot, not degrade into a log line nobody reads.

The single escape hatch — for extraordinary cases only, such as tenant test/pilot environments or debugging a suspected signing mismatch — is setting

export AAC_INVOKE_AUTH_ALLOW_UNAUTHENTICATED=true

explicitly (only the exact value true; a typo still fails the boot). An opted-out agent accepts every push unverified and logs a prominent warning at startup so the posture is visible in your logs. Never run this in production: the sidecar side has a symmetric dev_mode interlock, and both exist to make "unauthenticated" impossible to reach by accident.

Testing your agent

aac_invoke_auth.testing ships with the package:

from aac_invoke_auth.testing import signing_test_client

client = signing_test_client(app)          # wraps FastAPI's TestClient
response = client.post("/invoke", headers=aac_headers, json=payload)

post() calls to protected paths are signed exactly the way the paired sidecar signs them (secret read from AAC_INVOKE_AUTH_SECRET_FILE); everything else passes through. So your test suite exercises the real production verification path instead of opting out of it.

Wire format

AAC1-HMAC-SHA256 over method, path, timestamp, a SHA-256 body digest, and every X-AAC-* header (derived by rule, so header insertion/removal after signing fails verification). Freshness window ±30s. The normative canonicalization text lives in the AAC Engineering Specification §III.2.4a; frozen known-answer vectors live in this package's test suite — a change that breaks a vector is a wire-format break, not a refactor.

Ports of this middleware for other stacks (Express, Spring, Go net/http) follow the same model; the core stays standard-library-only in every language precisely to keep those ports mechanical.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aac_invoke_auth-0.1.0.tar.gz (26.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aac_invoke_auth-0.1.0-py3-none-any.whl (20.7 kB view details)

Uploaded Python 3

File details

Details for the file aac_invoke_auth-0.1.0.tar.gz.

File metadata

  • Download URL: aac_invoke_auth-0.1.0.tar.gz
  • Upload date:
  • Size: 26.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for aac_invoke_auth-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9a0894860dfcb5b1f459f1e0d8005f646edccb697d41304cb3455890c2b3b5f6
MD5 9d97c5df82b730d5812d34f5d110df9f
BLAKE2b-256 26e0b6b2e2c8a1c99a21b93fbe2b7581d8b7f68a73129e910362630f0d97a8ee

See more details on using hashes here.

File details

Details for the file aac_invoke_auth-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for aac_invoke_auth-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f36c7945d90699dbedfb65108604ea7c52fc27543aeac566eee81d1f20f85470
MD5 8ea2d61325bf95439f01da06935c5d59
BLAKE2b-256 90a0f8420ad3be12caeb64f8e7cdb949da7cf3084155a34a0f34c004dad91509

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page