# Delegated capability format 0.2

Status: experimental, pre-1.0.

This document is normative for the `0.2` wire format. Rust runtime behavior remains authoritative
if prose and implementation diverge.

## Trust boundary

The verifier MUST obtain the issuer verification key from trusted host configuration using the
signed token's `(issuer, key_id)` tuple. It MUST NOT accept public-key material from the request.

The verifier MUST obtain audience, action, resource, and delegation depth from the operation the
host will execute. These values are not accepted in the wire envelope.

## Request

A request is a strict JSON object containing:

- `spec_version`: `"0.2"`
- `kind`: `"DelegatedRequest"`
- optional non-empty `request_id`
- `delegation_token`

Unknown fields are rejected.

The default evaluator rejects envelopes larger than 64 KiB. Deployments may configure a
different positive limit.

`request_id` is diagnostic correlation data. It is not signed and has no authorization effect.

## Capability token

The token is a strict JSON object containing:

- `spec_version`: `"0.2"`
- `kind`: `"DelegatedCapability"`
- bounded `token_id`, `issuer`, `agent_id`, `delegator_id`, `nonce`, and `key_id`; nonce is at
  least 16 bytes
- non-empty `audience[]` and `allowed_actions[]`
- optional non-empty `allowed_resources[]`
- optional `max_delegation_depth`
- `issued_at` and `expires_at`
- `signature_alg`: `"Ed25519"`
- base64url-no-pad `signature`

The signature covers the entire token with `signature` set to an empty string. JSON object keys
are sorted lexicographically before serialization. Arrays preserve order.

Optional fields with no value are omitted during Rust serialization. Strings and JSON primitives
use `serde_json` encoding. This is the Delegated 0.2 canonicalization profile, not a claim of full
RFC 8785 compatibility.

`signature` is encoded as unpadded URL-safe base64 and must decode to exactly 64 bytes.

String identifiers are limited to 512 bytes. Authorization lists contain at most 64 unique,
non-empty entries.

Identifiers are opaque, case-sensitive strings. Implementations MUST NOT apply implicit action,
audience, resource, issuer, or principal normalization.

## Capability semantics

- `issuer` and `key_id` select a trusted verification key from host configuration.
- `agent_id` and `delegator_id` are assertions made by that issuer.
- `audience` and `allowed_actions` are exact allowlists.
- When `allowed_resources` is present, the host MUST provide a resource and it MUST match exactly.
- When `max_delegation_depth` is present, the host MUST provide trusted depth and it MUST be no
  greater than the maximum.
- `nonce` is single-use within an issuer namespace.
- A capability without `allowed_resources` authorizes any resource for an allowed audience/action.

## Evaluation

An evaluator MUST deny unless all checks pass:

1. Strict request and token decoding.
2. Contract validation.
3. Trusted issuer-key resolution and Ed25519 verification.
4. Activation, expiry, and configured maximum-lifetime checks.
5. Token revocation and agent deny checks.
6. Host operation audience/action binding.
7. Resource binding; a constrained token requires a host resource.
8. Delegation-depth binding; a constrained token requires a host depth.
9. Atomic nonce consumption.

Nonce consumption occurs only after all other authorization checks pass so unauthorized requests
cannot burn an otherwise valid token.

Any issuer resolver, trust-state, or required audit persistence error MUST prevent execution.

The default clock leeway is 30 seconds and may not exceed five minutes. The default maximum token
lifetime is 24 hours. A token is inactive when `issued_at > now + leeway` and expired when
`expires_at <= now - leeway`.

## Audit semantics

Evaluation returns a structured event containing trusted host operation fields. When
`claims_verified` is false, token identity fields are untrusted diagnostic input.

`evaluate` does not persist its event. `evaluate_and_audit` returns an I/O error instead of a
decision when persistence fails. Nonce consumption precedes audit persistence, so an audit failure
can consume a token even though the operation must not execute.

## Non-goals

The format does not provide proof of possession, multi-hop delegation-chain validation, OIDC,
SPIFFE, issuer discovery, key rotation orchestration, transport security, or durable storage.

The format also does not close the race between a successful revocation check and operation
execution, define application action/resource vocabularies, or establish that the bearer is the
agent named by the issuer.
