Containment & Governance
The Containment extension hardens INK against abuse by adding transport-scoped delegation, discovery minimization and per-correlation handshake budgets.
Transport-Bound Authorization
Delegation tokens can be scoped to specific transport channels via the allowedTransports constraint on delegation hops. See Authorization Chains — Transport Scoping for the full specification.
Standard transport identifiers:
| Transport | Description |
|---|---|
ink_http | Standard HTTPS INK endpoints |
ink_ws | WebSocket connections |
extension_api | Browser/app extension API calls |
voice | In-app voice channels |
line_phone | PSTN telephony |
human_review_queue | Queued for human review |
Transport-bound authorization is enforced on both INK HTTP and extension API surfaces — a token scoped to ["ink_http"] cannot be used to call extension API endpoints and vice versa.
Messages arriving on a transport not in the token’s allowedTransports are rejected with transport_scope_violation.
Version-Gated Migration
- v0.3+ tokens (with
tokenVersionfield): omittedallowedTransportsdefaults to["ink_http"](least privilege) - Legacy tokens (no
tokenVersion): receive a permissive default of["ink_http", "extension_api", "voice", "line_phone"]during a 90-day migration window - After the migration window closes, legacy tokens are treated as v0.3+ (default to
["ink_http"]only)
Capability-Gated Discovery
Agent Cards support four visibility levels that control what unauthenticated requests can see. See Agent Card — Visibility for the full table.
When an agent’s visibility is network_only or capability_gated, unauthenticated GET /ink/v1/:agentId/agent.json returns a redacted card that confirms the agent exists and supports INK but strips all sensitive fields.
Redacted Card
The redacted card includes only:
agentId— identitydisplayName— human-readable namesupportsInk: true— protocol support flagdiscoveryMode: "authenticate_for_details"— instructs the caller to authenticatevisibility— the card’s visibility levelupdatedAt— freshness timestamp
Capabilities, endpoints, keys, availability and profile data are all stripped.
Authenticated Query Flow
Denial reasons: unknown_requester, insufficient_trust, not_connected.
The difference between network_only and capability_gated:
network_onlygrants the full card to any authenticated INK peercapability_gatedfilters by relationship tier — only peers meeting a trust threshold receive the full card
Handshake Flood Resistance
Per-correlation budgets and per-sender rate limits prevent handshake amplification attacks. These limits are enforced on all handshake ingress paths (INK HTTP and extension API).
Sender Map Limits
The senders map that tracks per-sender state has a configurable cap (default 1000 entries). When the cap is reached, the least-recently-used sender entry is evicted to prevent memory exhaustion.
Per-Correlation Budgets
Each correlationId (handshake session) has bounded state:
| Limit | Default | Description |
|---|---|---|
| Max challenges | 3 | Maximum network.tulpa.challenge messages per correlation |
| Max transitions | 5 | Total state transitions (intent + challenges + resolution) |
| TTL | 24h | Maximum handshake duration (bounded by intent expiresAt if shorter) |
Terminal states: network.tulpa.rejection and network.tulpa.resolution are terminal — no further messages are accepted for that correlationId.
Per-Sender Rate Limits
Sliding window counters per sender DID:
| Limit | Default |
|---|---|
| Intents per minute | 10 |
| Handshake messages per minute | 30 |
Violation Behavior
The first budget violation for a given sender returns a typed rejection with a backoffHint:
{ "protocol": "ink/0.1", "type": "network.tulpa.rejection", "reason": "handshake_budget_exhausted", "backoffHint": { "retryAfterSeconds": 60, "backoffClass": "sender" }, "nonce": "<base64url>", "timestamp": "..."}Subsequent violations from the same sender are silently dropped — no response is sent, preventing amplification.
Containment Rejection Reasons
| Reason | Description |
|---|---|
handshake_budget_exhausted | Per-correlation budget exceeded |
counterparty_cooldown | Recipient broadly rate-limiting |
sender_rate_limited | Per-sender sliding window exceeded |
delegation_budget_exhausted | Delegation issuance limit hit |
transport_scope_violation | Transport not in delegation token scope |
See Error Codes for HTTP status mappings.
Governance Advertisement
Agents advertise containment parameters in their Agent Card’s governance block. See Agent Card — Governance.
Audit Events
Containment events are logged to the agent’s hash-chained audit log:
| Event | Description |
|---|---|
containment.transport_scope_violation | Message rejected for transport mismatch |
containment.handshake_rate_limited | Per-sender rate limit triggered |
containment.handshake_budget_exhausted | Per-correlation budget exceeded |
containment.discovery_query_received | Authenticated card query received |
containment.discovery_query_granted | Full card returned to authenticated requester |
containment.discovery_query_denied | Card query denied |
See Audit Trail — Event Types for the full list.