Skip to content

Authorization

A11 carries signed delegation statements to an application verifier, then reuses the verified result within one session stream. The runtime owns the canonical envelope, bounded context storage, propagation, and request restrictions. The application owns signature verification, trust policy, and the claims placed in VerifiedAuthorization.

Authorization envelope

AuthorizationEnvelope contains a version and an ordered chain of compact JWS statements. A11 validates its structural limits:

  • version 1;
  • one to eight ASCII compact JWS statements;
  • at most 16 KiB after canonical MessagePack encoding.

encode_authorization() and decode_authorization() use the native binary form carried by action headers. authorization_to_text() and authorization_from_text() use the a11-auth/1. base64url form required by an ASCII HTTP header. The fingerprint is the base64url SHA-256 digest of the canonical envelope.

Structural decoding does not verify JWS signatures. A verifier returns VerifiedAuthorization only after applying the application's keys, audience, expiry, and delegation policy.

Connection authorization

install_authorizer(registry, verifier) registers the reserved __authorize__ action and applies its context store to the registry. Other incoming actions must then resolve a valid context before their handler runs.

The verifier accepts the native envelope bytes and returns the effective identity and authority:

import time

import a11


async def verify(value: bytes) -> a11.VerifiedAuthorization:
    envelope = a11.decode_authorization(value)
    claims = await trust_policy.verify(envelope.chain)
    return a11.VerifiedAuthorization(
        envelope=envelope,
        subject=claims.subject,
        subject_kind=claims.subject_kind,
        actors=claims.actors,
        audience=claims.audience,
        expires_at=claims.expires_at,
        grants=claims.grants,
        restrictions=claims.restrictions,
    )


contexts = a11.install_authorizer(registry, verify)

The verifier may be synchronous or asynchronous. An expired result is rejected when the context is installed.

The client sends the complete proof once:

context = await a11.establish_authorization(connection, envelope)

The receiver returns a random 128-bit context ID, expiry, envelope fingerprint, and default flag. A default context applies to later actions on the same session stream without another authorization header. A non-default context is selected explicitly:

context = await a11.establish_authorization(
    connection,
    envelope,
    make_default=False,
)
action = connection.action("restricted-operation", schema)
a11.set_authorization_reference(action, context.context_id)
await action.call()

Contexts are scoped to the receiving Session and WireStream. They do not cross a new connection or another stream. Expired entries are pruned, each stream has a bounded context set, and session completion clears its entries. replace= rotates a context while keeping installation and removal in one receiver operation.

Request restrictions

The context store enforces supported restrictions before dispatching an incoming action:

Field Constraint
actions Action name must match one of the * and ? glob patterns
identities Verified audience must appear in the list
request_id Action ID must equal the bound request ID

The verifier defines the restrictions after validating the delegation chain. Malformed restrictions fail closed with PERMISSION_DENIED.

Per-action verification

Applications that do not install a connection authorizer can carry and verify a complete proof on one action:

a11.set_authorization_header(action, envelope)

The receiving handler calls verify_action_authorization(action, verifier) before reading the resulting get_verified_authorization(action) value. A complete proof and a context reference are mutually exclusive headers.

Nested actions with I/O propagation inherit the immutable verified authorization object. Detached children created with propagate_io=False do not inherit connection authority. Raw context references are scoped to their original stream and are excluded from generic header forwarding.

Authorization and model tool policy

Connection authorization establishes caller identity and effective authority for action dispatch. The LLM allowed-action header selects tools offered to a model for one turn. Applications using both enforce the connection context at the registry and apply the model allow-list inside the interaction action.

a11.authorization

Exchange verified authorization once and reuse it on an A11 connection.

AuthorizationEnvelope

AuthorizationEnvelope(chain: Sequence[str], version: SupportsInt = 1)

A versioned ordered sequence of compact signed delegation statements.

VerifiedAuthorization

VerifiedAuthorization(envelope: AuthorizationEnvelope, subject: str, subject_kind: str, actors: Sequence[str], audience: str, expires_at: SupportsFloat, grants: Any = (), restrictions: Any = {}, authorization_epoch: SupportsInt = 0, provenance: Sequence[str] = (), assurance: str = '')

Identity and effective authority returned by an application verifier.

AuthorizationVerifier

Bases: Protocol

Verify one native x-a11-auth value for a configured audience.

AuthorizationContextResponse

Bases: BaseModel

The receiver-issued handle for one connection authorization context.

encode_authorization

encode_authorization(envelope: AuthorizationEnvelope) -> bytes

Encode an authorization envelope in its native binary form.

decode_authorization

decode_authorization(value: bytes | bytearray | memoryview) -> AuthorizationEnvelope

Decode a canonical native binary authorization value.

authorization_to_text

authorization_to_text(envelope: AuthorizationEnvelope) -> str

Encode an envelope for an ASCII-only physical HTTP header.

authorization_from_text

authorization_from_text(value: str) -> AuthorizationEnvelope

Decode the physical HTTP-header representation of an envelope.

get_authorization

get_authorization(action: Action) -> AuthorizationEnvelope | None

Decode the action's full authorization value without verifying it.

set_authorization_header

set_authorization_header(action: Action, envelope: AuthorizationEnvelope | None) -> Action

Set or remove the action's complete signed authorization chain.

set_authorization_reference

set_authorization_reference(action: Action, context_id: str | None) -> Action

Select a non-default context previously issued on this stream.

verify_action_authorization async

verify_action_authorization(action: Action, verifier: AuthorizationVerifier) -> VerifiedAuthorization

Verify and bind a complete value carried by one action.

get_verified_authorization

get_verified_authorization(action: Action) -> VerifiedAuthorization | None

Return the immutable context already bound to an incoming action.

install_authorizer

install_authorizer(registry: Any, verifier: AuthorizationVerifier, *, contexts: AuthorizationContextStore | None = None) -> AuthorizationContextStore

Register __authorize__ and return its connection context store.

establish_authorization async

establish_authorization(connection: Any, envelope: AuthorizationEnvelope, *, make_default: bool = True, replace: str | None = None) -> AuthorizationContextResponse

Authorize a connection and return its receiver-issued context.