Internet-Draft Identity Propagation Context July 2026
Sharma Expires 24 January 2027 [Page]
Workgroup:
Individual Submission
Internet-Draft:
draft-sharma-oauth-identity-propagation-context-01
Published:
Intended Status:
Experimental
Expires:
Author:
R.K. Sharma
Ericsson

Identity Propagation Context for Multi-Hop Delegation in OAuth 2.0 Environments

Abstract

This specification defines the Identity Propagation Context (IPC), a signed, short-lived context artifact that carries identity and delegation chain information across multi-hop service chains with per-hop cryptographic re-signing. IPC complements the delegation semantics of OAuth 2.0 Token Exchange (RFC 8693) by enabling each trust boundary to independently verify the upstream signature and re-sign the identity context it forwards, supporting cascade revocation, delegation depth limits, and cross-protocol propagation (HTTP, gRPC, and event-streaming systems).

Unlike the act claim in RFC 8693, which is explicitly designated as "informational only and not to be considered in access control decisions," IPC is designed for identity propagation that can be used in access control decisions, with per-hop cryptographic re-signing within a single trust domain. In autonomous agent architectures and multi-service platforms, Each trust boundary verifies the upstream signature and re-signs with its own key before forwarding; the delegation chain content is asserted by the IPC Creator and trusted transitively through the chain of re-signing intermediaries. This provides single-hop-verifiable integrity (each service can verify its immediate upstream) rather than end-to-end cryptographic proof of the full chain.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 24 January 2027.

Table of Contents

1. Introduction

1.1. Problem Statement

Modern cloud-native platforms serve multiple classes of autonomous entities - AI agents, applications, and sub-agents - that initiate operations traversing complex service chains. A fundamental problem remains unsolved by existing standards:

How do you propagate the identity of an entity - including its full delegation chain back to an accountable human - from the initiating service to the final enforcement point, across multiple trust boundaries and protocols, in a manner that is per-hop verified, enforceable, revocable, and depth-limited?

The authentication layer is largely solved: platforms can assign cryptographic identities to agents (e.g., SPIFFE-based identities with mTLS and DPoP credential binding) and establish single-hop delegation (e.g., OAuth 2.0 Token Exchange). What remains unsolved is carrying the full delegation context - who is the original human, who authorized this agent, what is the complete chain of delegation - through multi-hop internal service calls where each intermediate service independently verifies the context before forwarding it.

This problem has been independently formalized as "authorization propagation" in the academic literature [Tallam2026].

Existing mechanisms fall short:

  • OAuth 2.0 Bearer Tokens (RFC 6750): Validated at the first hop only, not propagated with delegation context.
  • OAuth 2.0 Token Exchange (RFC 8693): Supports nested act claims but explicitly limits them to "informational only" for access control decisions. Requires STS interaction to extend the delegation chain at each new delegation hop (e.g., when an intermediate agent sub-delegates). The resulting token with nested act claims can be forwarded downstream, but intermediate services cannot independently attest their verification without producing a new token via the STS.
  • Agent Identity Protocol (AIP) [I-D.draft-singla-agent-identity-protocol]: Defines decentralized agent identity with delegation chains, but requires the agent to self-issue Credential Tokens. Does not address platform-transparent propagation through service infrastructure where the agent is unaware of downstream services. HTTP-only; no gRPC or event-streaming bindings.
  • WIMSE AI Agent Identity [I-D.draft-ni-wimse-ai-agent-identity]: Defines agent identity bootstrapping and attestation via a proxy architecture, but does not specify how verified identity propagates through multi-hop service chains after initial credential issuance.
  • OTEL Baggage (W3C): Unsigned observability context - not suitable for enforcement.
  • Transaction Tokens [I-D.draft-ietf-oauth-transaction-tokens]: Carry immutable authorization context (user identity, scope, transaction details) through a call chain within a Trust Domain. However, Txn-Tokens are passed unchanged through the chain — they do not support per-hop re-signing, do not carry a mutable delegation chain that grows with sub-delegation, and do not provide per-hop verification that each intermediate validated the context. IPC and Txn-Tokens are complementary: Txn-Tokens for authorization context, IPC for delegation chain provenance.
  • mTLS / SPIFFE: Identifies the transport endpoint, not the originating entity or its delegation chain. Production platforms (e.g., Google Cloud Agent Identity) assign per-agent SPIFFE identities and use mTLS + DPoP for credential binding, yet still lack a mechanism to propagate the full delegation chain (human → agent → sub-agent) through multi-hop internal service calls. SPIFFE proves "this workload is Service-A"; it does not prove "this request originated from operator@example.com who delegated to energy-optimizer who delegated to cell-configurator."
  • AI Agent Authentication Frameworks [I-D.draft-klrc-aiagent-auth]: Composes SPIFFE, WIMSE, OAuth, and OIDC into a unified agent authentication architecture, but focuses on credential issuance and initial authentication rather than per-hop identity propagation through service chains after the agent authenticates.

1.2. Goals

This specification defines:

  1. A context format (IPC) that carries delegation chain information with per-hop cryptographic re-signing.
  2. Re-signing semantics at trust boundaries.
  3. Delegation depth enforcement rules.
  4. Expiry attenuation (child <= parent).
  5. Cascade revocation semantics.
  6. Protocol bindings for HTTP, gRPC, and event-streaming (e.g., Kafka).
  7. A trust model for IPC issuance, verification, and re-signing.

1.3. Relationship to RFC 8693

IPC is complementary to RFC 8693. Token Exchange may be used at the first hop to establish the initial delegation relationship. IPC then carries the verified identity context through the remaining multi-hop chain without requiring further STS interaction.

1.4. Terminology

  • IPC: Identity Propagation Context - a signed context artifact carrying delegation chain information.
  • Issuer: The service that creates or re-signs an IPC at a trust boundary.
  • Entity: Any principal (human, agent, application, or system) that initiates or participates in a delegation chain.
  • Delegation Chain: The ordered sequence of entities from the accountable root (typically human) to the current executing entity.
  • Trust Boundary: A point in the service chain where IPC is verified and re-signed by a new issuer.
  • Enforcement Point: A service that reads the IPC and makes authorization decisions based on its contents.
  • STS: Security Token Service - a trusted service that issues delegation tokens establishing delegation relationships between entities. The STS authenticates the delegating party and produces a delegation-token that the IPC Creator consumes during IPC construction (Section 4.2).

1.5. Requirements Language

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

1.6. Scope of Applicability

IPC is designed for identity propagation in multi-hop service chains within a single trust domain or platform. It is NOT suitable for:

  • Cross-organization federation (use RFC 8693 + trust frameworks)
  • User-to-application consent flows (use OAuth 2.0 authorization code flow)
  • General-purpose session management (use session cookies or OAuth refresh tokens)
  • Replacing OAuth 2.0 bearer tokens for authentication (IPC travels alongside, not replacing, the bearer token)
  • Dynamic topologies where the terminal enforcement point is unknown at IPC creation time (the IPC Creator must know the target audience)

1.7. Coexistence with OAuth 2.0 Bearer Tokens

IPC is carried alongside the standard OAuth 2.0 bearer token in requests. The bearer token handles authentication (proving the caller has valid credentials). IPC handles identity propagation (carrying the full delegation chain for enforcement decisions). Both MAY be present in the same request.

1.8. Multi-Vendor Platform Interoperability

A standardized IPC format and verification algorithm enables platform operators to compose service chains from components provided by multiple vendors without requiring proprietary identity propagation mechanisms. Any service that implements this specification can participate in an IPC chain regardless of its vendor origin, provided it operates within the platform's trust domain and has access to the upstream issuer's public key.

This is distinct from cross-organization federation: the platform operator controls the trust domain, manages key distribution, and defines enforcement policy. The standardization benefit is that vendors do not need bilateral integration for identity propagation - they implement one specification.

Implementations claiming multi-vendor interoperability conformance MUST support key discovery via Section 3.6. IPC Creators and Trust Boundaries MUST support key discovery as both key consumers (querying upstream endpoints) and key publishers (exposing own keys) per the conformance requirements in Section 11.3. Enforcement Points that only verify (and never re-sign) MUST support key discovery as key consumers. Static key configuration alone is insufficient for multi-vendor claims.

2. IPC Format

2.1. Claims

IPC intentionally carries identity and delegation context, not authorization grants or permissions. Authorization decisions are made by policy engines (e.g., OpenFGA, OPA, Cedar) that consume IPC identity as input. This separation ensures that IPC remains a general-purpose propagation mechanism independent of any particular authorization model.

IPC uses distinct claim names (entityId, issuer) rather than JWT registered names (sub, iss) to avoid semantic confusion when IPCs coexist with JWTs in the same request context, and because IPC's issuer semantics differ from JWT's iss - in IPC, the issuer changes at every re-signing hop.

The IPC is a signed JSON object with the following claims:

Table 1
Claim Type Required Description
version string REQUIRED IPC specification version (e.g., "1.0")
entityId string REQUIRED Identifier of the entity currently executing
entityType string REQUIRED AUTONOMOUS, SUPERVISED, UTILITY, HUMAN, or SYSTEM
authorizedBy string REQUIRED Identifier of the immediate delegating party
delegationDepth integer REQUIRED Number of delegation hops from root. MUST equal delegationChain.length - 1
delegationChain array REQUIRED Ordered list of entity identifiers from root to current
audience string REQUIRED (Level 2+), OPTIONAL (Level 1) Intended terminal enforcement point service identifier. Verified only by the final service in the chain (not intermediate trust boundaries). Prevents cross-service replay attacks.
sessionId string REQUIRED Unique identifier for this IPC instance (UUID v7 per RFC 9562 RECOMMENDED). Deployments MAY correlate sessionId with W3C Trace Context through a deployment-internal mapping. Using the same value for both sessionId and externally-visible trace-id is NOT RECOMMENDED when trace context propagates beyond the trust domain boundary, as it enables correlation of identity chains via observability infrastructure.
issuedAt string REQUIRED ISO 8601 timestamp (MUST be UTC, trailing "Z", no fractional seconds). Format: e.g., 2026-07-17T08:00:00Z (exactly 20 characters)
expiresAt string REQUIRED ISO 8601 timestamp (MUST be UTC, trailing "Z", no fractional seconds). Format: e.g., 2026-07-17T08:00:00Z (exactly 20 characters)
issuer string REQUIRED Identifier of the service that signed this IPC
trustLevel integer REQUIRED 1 (unsigned), 2 (signed), or 3 (reserved for future use)
alg string REQUIRED Signing algorithm identifier from the IANA JOSE Algorithms registry: "EdDSA" (default for multi-hop chains; MUST use curve Ed25519 per RFC 8037), "ES256" (FIPS-compatible alternative for multi-hop chains; ECDSA with P-256 per RFC 7518 Section 3.4; see Section 2.6.1), "HS256" (single-hop only; MUST NOT be used when the IPC will cross more than one trust boundary), or "none" (Trust Level 1 only). Deployments MUST choose either EdDSA or ES256 for their asymmetric algorithm; mixing within a chain is prohibited.
kid string REQUIRED (Level 2+) Key identifier of the signing key. Enables key selection during rotation. The kid format is deployment-specific. The RECOMMENDED format is <issuer>/<key-version> (e.g., api-gateway/key-1).
signature string REQUIRED (Level 2+) Cryptographic signature over all other claims

The entityType values are defined as follows:

Table 2
Type Definition
HUMAN A natural person authenticated via an identity provider
SYSTEM Platform-initiated operation with no direct human trigger
AUTONOMOUS Agent operating independently on behalf of a human principal
SUPERVISED Agent requiring human approval for sensitive operations
UTILITY Stateless service component invoked by other entities

The entityType is assigned at IPC creation time based on the authenticated entity's registration in the platform. It MUST NOT change during re-signing.

The entityType values are case-sensitive. Verifiers MUST perform exact string matching against the defined values (AUTONOMOUS, SUPERVISED, UTILITY, HUMAN, SYSTEM).

IPC uses ISO 8601 UTC strings rather than numeric epoch seconds (as used in JWT) for timestamps. This enables human-readable inspection of IPC tokens in logs, debugging tools, and wire captures without requiring numeric-to-date conversion.

Timestamps MUST NOT include fractional seconds. The canonical format is exactly YYYY-MM-DDTHH:MM:SSZ (20 characters). This ensures JCS canonicalization produces identical bytes regardless of implementation, preventing signature verification failures during re-signing.

Implementations SHOULD parse ISO 8601 timestamps to integer epoch representation once upon receipt and perform all subsequent comparisons as integer arithmetic. This avoids repeated string parsing at each verification step.

2.2. Validation Rules

  • delegationDepth MUST equal delegationChain.length - 1. If mismatched, the IPC MUST be rejected.

Rationale for carrying both: delegationDepth enables fast policy rejection (single integer comparison against maxDepth) without requiring the verifier to parse and compute the length of the delegationChain array. The redundancy also serves as an integrity check - a mismatch indicates tampering or corruption. - authorizedBy MUST equal delegationChain[delegationChain.length - 2] when the chain has two or more entries. When delegationChain has exactly one entry (no delegation), authorizedBy MUST equal entityId. If mismatched, the IPC MUST be rejected.

Rationale for carrying authorizedBy separately: it enables fast authorization policy lookups ("who authorized this entity?") without requiring the verifier to parse and index the delegationChain array. The redundancy also serves as an integrity check.

The delegationChain carries identifiers only, not entity types. Enforcement points that require type information for intermediate chain entries MUST resolve types through deployment-specific policy or a principal registry.

  • If audience is present, it identifies the final enforcement point in the chain, not each intermediate hop. Trust boundary services that re-sign and forward the IPC MUST NOT verify audience against their own service identifier - they are intermediaries, not the intended audience. Only the terminal enforcement point (the service that makes the final authorization decision and does not re-sign) MUST verify that its own service identifier matches the audience value. If it does not match, the IPC MUST be rejected.

For Trust Level 2+ IPCs, audience MUST be a non-empty string identifying the terminal enforcement point. An empty string value for audience in a Trust Level 2+ IPC MUST be rejected at creation time. The verification algorithm's empty-audience check (Section 3.2 Step 9f) rejects empty audience for Trust Level 2+, while Step 12 verifies audience matching only at terminal enforcement points. In fan-out topologies where an intermediate service forwards to multiple terminal services, separate IPCs MUST be created for each terminal path (each with its own audience value). Fan-out IPC creation MUST NOT be performed on incoming HS256-signed IPCs. If the incoming IPC has alg of "HS256", the service MUST act as a terminal enforcement point only and MUST NOT create fan-out IPCs. HS256-signed IPCs cannot be re-signed or forwarded (Section 3.3) — they are terminal by definition. The preserved alg will therefore always be "EdDSA" in fan-out scenarios. In this scenario, the intermediate service acts as both a Trust Boundary (verifying the incoming IPC) and an IPC Creator (issuing new IPCs for each downstream path). The new IPCs MUST preserve the original delegationChain, entityId, entityType, authorizedBy, delegationDepth, version, alg, and trustLevel from the verified incoming IPC. Extension claims (Section 2.2) present in the incoming IPC MUST also be preserved in fan-out IPCs. Fan-out creation is semantically equivalent to re-signing with respect to claim preservation: only issuedAt, expiresAt, audience, sessionId, issuer, kid, and signature differ in each fan-out IPC. The issuedAt of each fan-out IPC MUST be set to the current time (the time of fan-out creation), NOT preserved from the incoming IPC. The expiresAt MUST be set to min(now() + config.maxTTL, original.expiresAt) to maintain expiry attenuation (the fan-out IPC cannot outlive the original). The audience, sessionId, issuer, kid, and signature differ in each fan-out IPC. Each fan-out IPC MUST have a unique sessionId to avoid replay cache conflicts at shared downstream infrastructure.

Note on replay cache interaction: If the fan-out service is also an enforcement point (dual-role per Section 11.3), it adds the original incoming IPC's sessionId to its own replay cache upon verification. Each fan-out IPC carries a new, independent sessionId that is consumed by downstream enforcement points' replay caches. The original IPC's replay detection and each downstream path's replay detection operate independently.

Fan-out security constraints: - The fan-out service MUST be configured with an explicit allowlist of downstream audience values for which it may create IPCs. Fan-out IPC creation for audience values not in the allowlist MUST be rejected. - The fan-out service MUST enforce a maximum fan-out count per incoming IPC (RECOMMENDED default: 10). If the number of downstream paths exceeds this limit, the service MUST reject the fan-out request. - Fan-out IPC creation MUST NOT be performed when the incoming IPC was accepted in historical replay mode (Section 6.6) where temporal validity checks were skipped. Creating fresh IPCs from expired historical records would circumvent expiry attenuation.

Rationale for regenerating issuedAt: If the original issuedAt were preserved, the maxTokenAge check (Step 8f) would measure age from the original creation time. In fan-out topologies with processing delays, this could cause fan-out IPCs to fail maxTokenAge checks at downstream services despite being freshly created. Regenerating issuedAt gives each fan-out IPC a fresh lifetime budget while expiresAt attenuation ensures it cannot outlive the original authority.

A single IPC cannot target multiple terminal enforcement points. - The first entry in delegationChain MUST be a human or system principal (determined by deployment policy). - A verifier that receives an IPC with a major version it does not implement MUST reject it. Within the same major version (e.g., "1.x"), a verifier implementing version "1.0" MUST accept tokens with version "1.0" or any later minor version (e.g., "1.1", "1.2"), ignoring unrecognized claims per the extension mechanism above. This enables rolling upgrades without fleet-wide flag-day deployments.

Minor version increments (e.g., "1.0" to "1.1") MUST only add OPTIONAL claims. A minor version MUST NOT promote an extension claim to REQUIRED status, MUST NOT change the semantics of existing claims, and MUST NOT introduce new security-critical properties that earlier minor versions would silently ignore. Changes that require all verifiers to understand new claims for security correctness MUST use a major version increment (e.g., "1.x" to "2.0"). This ensures that a "1.0" verifier can safely process any "1.x" token without creating a security gap where newer security properties are silently dropped.

To support non-breaking schema evolution without version bumps, IPC tokens MAY include additional claims beyond those defined in Section 2.1. Verifiers MUST ignore unrecognized claims during verification and MUST NOT reject an IPC solely because it contains claims not defined in this specification. The version field indicates the minimum set of claims a verifier must understand. Unrecognized claims MUST NOT be included in authorization decisions.

This mechanism allows deployments to carry deployment-specific metadata (e.g., trace correlation IDs, cost center tags) without requiring a version bump, while ensuring that security-critical claims are always processed by version-aware verifiers. Deployments adding extension claims SHOULD use a namespace prefix (e.g., x-<org>-<claim>) to avoid collision with future standard claims or other deployments' extensions.

  • Entity identifiers (entityId, entries in delegationChain, authorizedBy, issuer, audience) MUST conform to the following constraints:

Verifier-enforceable (checked by the verification algorithm): - MUST be non-empty strings between 1 and 128 characters - MUST be compared using exact byte-for-byte matching (case-sensitive) - MUST NOT contain whitespace or control characters - SHOULD contain only ASCII printable characters (U+0021 through U+007E). If non-ASCII characters are used, identifiers MUST be in Unicode Normalization Form C (NFC) to ensure JCS canonicalization produces identical bytes across implementations.

Administrative (enforced by deployment policy, not by verifiers): - MUST be unique within the deployment's trust domain - RECOMMENDED format: URI (e.g., mailto:operator@example.com, urn:service:api-gateway), email address, or deployment-scoped identifier with a namespace prefix (e.g., agent:energy-optimizer) - Using email-format identifiers for HUMAN entities (e.g., operator@example.com) and namespace-prefixed identifiers for agents (e.g., agent:energy-optimizer, service:api-gateway) is RECOMMENDED for human readability in audit logs.

2.3. Example

{
  "version": "1.0",
  "entityId": "cell-configurator",
  "entityType": "UTILITY",
  "authorizedBy": "energy-optimizer",
  "delegationDepth": 2,
  "delegationChain": [
    "operator@example.com",
    "energy-optimizer",
    "cell-configurator"
  ],
  "audience": "ncmp.platform.example.com",
  "sessionId": "0191a0b0-c8f4-7b2e-9a1c-3d4e5f6a7b8c",
  "issuedAt": "2026-07-17T08:00:00Z",
  "expiresAt": "2026-07-17T08:05:00Z",
  "issuer": "api-gateway",
  "trustLevel": 2,
  "alg": "EdDSA",
  "kid": "api-gateway/key-1",
  "signature": "Base64url(...)"
}

2.4. Encoding

The IPC MUST be serialized as JSON, then Base64url-encoded for transport.

2.5. Signature Computation

The signature is computed over a canonical serialization of all claims EXCLUDING the signature field itself. The process is:

  1. Construct a JSON object containing all claims except signature.
  2. Serialize using JSON Canonicalization Scheme (JCS) per RFC 8785 to produce a deterministic byte sequence.
  3. Compute the signature over the canonical byte sequence using the algorithm specified in the alg claim.
  4. Base64url-encode the signature (without padding, per RFC 4648 Section 5) and include it as the signature claim.

The issuer field IS included in the signature input. This means that when a trust boundary re-signs the IPC: 1. The issuer field is updated to the re-signing service's identifier. 2. A new signature is computed over all claims (including the new issuer) using the re-signing service's private key. 3. The new signature replaces the old signature value.

This ensures that each signature cryptographically binds the issuer to the IPC content, proving which service validated and re-signed it.

The signature input includes the current issuer and kid values at the time of signing. When a trust boundary re-signs, it updates both issuer and kid before computing the new signature. The downstream verifier's canonical bytes will therefore reflect the re-signing service's identity and key identifier, not the original creator's.

Implementations that re-sign IPCs MUST NOT re-serialize claims through a non-JCS-compliant JSON serializer. The canonical bytes for signature computation MUST be produced by a JCS-compliant implementation operating on the complete token object (including any unrecognized extension claims). Re-serializing through a non-compliant serializer may alter number representation, Unicode normalization, or key ordering, causing downstream signature verification to fail.

The integer claims delegationDepth and trustLevel MUST be serialized as JSON integers without decimal points (e.g., 2, not 2.0). JCS (RFC 8785) specifies that integer-valued numbers serialize without a decimal point, but intermediate deserialization into floating-point types may produce incorrect canonical output.

Note on JCS sort order: JCS (RFC 8785 Section 3.2.3) sorts object member names by comparing their UTF-16 code unit sequences. For ASCII-only member names (all claims defined in this specification), this is equivalent to byte-order sorting. Extension claims (Section 2.2) that use non-ASCII characters in claim names MUST be aware that JCS sort order follows RFC 8785 Section 3.2.3 exactly, including surrogate pair handling. Extension claim names SHOULD be restricted to ASCII characters for maximum interoperability across JCS implementations.

2.6. Algorithm Restrictions

EdDSA with curve Ed25519 (per RFC 8037) MUST be used for IPCs that traverse multiple trust boundaries (the common case). Ed25519's asymmetric key model allows each trust boundary to verify using the upstream's public key without sharing secret material.

HS256 (HMAC-SHA-256, per RFC 7518 Section 3.2) MAY be used only in single-hop deployments where exactly one service creates the IPC and exactly one service verifies it. In this configuration, both services share the symmetric secret. HS256 MUST NOT be used when:

  • The IPC will be re-signed at any trust boundary
  • More than two services participate in the chain
  • The deployment topology may change to include additional hops

Deployments that begin with single-hop HMAC and later add trust boundaries MUST migrate to EdDSA before enabling re-signing.

2.7. FIPS-Compatible Algorithm Profile

Deployments operating in FIPS 140-2/140-3 constrained environments (e.g., government, regulated telecom, banking, healthcare) where Ed25519 is not available in validated cryptographic modules MAY use ES256 (ECDSA with P-256 and SHA-256, per RFC 7518 Section 3.4) as an alternative asymmetric signing algorithm.

When ES256 is used: - The alg claim MUST be set to "ES256" - Key discovery endpoints MUST serve keys with "kty": "EC", "crv": "P-256" per RFC 7518 Section 6.2 - The signature is computed over the same JCS canonical bytes as EdDSA (Section 2.5) - The signature value is the base64url-encoded concatenation of the R and S values (64 bytes total, per RFC 7518 Section 3.4)

Deployments MUST NOT mix Ed25519 and ES256 within the same service chain. All trust boundaries and enforcement points in a given chain MUST use the same asymmetric algorithm. The choice is deployment-wide, not per-service.

The verification algorithm (Section 3.2, Step 5) MUST accept "ES256" as a valid algorithm value when the deployment is configured for the FIPS profile. Step 6g dispatches to ECDSA-P256.Verify for "ES256" tokens.

Services configured as Trust Boundaries (i.e., services that re-sign IPCs) MUST have allowHMAC = false. Implementations MUST reject configurations where a service is assigned the Trust Boundary role (Section 11.3) and allowHMAC = true. This prevents misconfigured services from accepting HMAC-signed tokens in a multi-hop chain where symmetric key material would need to be shared across trust boundaries.

2.8. Design Rationale: Why Not JWS?

IPC uses a JSON format with JCS (RFC 8785) canonicalization rather than JWS (RFC 7515). This is a deliberate design choice driven by IPC's per-hop re-signing workflow. The following analysis evaluates both JWS compact serialization and JWS JSON Serialization:

JWS Compact Serialization (RFC 7515 Section 7.1):

JWS compact serialization represents a signed token as BASE64URL(header).BASE64URL(payload).BASE64URL(signature). Re-signing an IPC at a trust boundary would require: 1. Base64url-decode the payload to access claims 2. Modify issuer and kid in the decoded payload 3. Re-encode the modified payload as base64url 4. Construct a new protected header (or reuse) 5. Compute signature over the new header.payload concatenation 6. Assemble the new compact token

IPC's approach requires: 1. Parse the JSON object 2. Modify issuer and kid 3. Compute JCS canonical bytes (excluding signature) 4. Sign and set the signature field

The difference is not in operation count but in verification determinism: JWS requires the verifier to use the exact base64url encoding the signer produced (since the signature covers the encoded form). IPC's JCS approach allows the verifier to independently reconstruct the canonical signed bytes from the parsed JSON object without depending on the signer's encoding choices. This eliminates a class of interoperability failures where base64url implementations differ in whitespace handling, line-wrapping, or padding.

JWS JSON Serialization (RFC 7515 Section 7.2):

JWS JSON Serialization could in principle support IPC's re-signing model by placing mutable fields (issuer, kid) in the unprotected header. However, this approach has practical drawbacks:

  1. Unprotected header semantics vary across implementations. Libraries differ in how they expose and validate unprotected headers. Some JOSE libraries reject or ignore unprotected headers entirely. A constrained profile mandating specific unprotected header behavior would require the same implementation effort as IPC's native format.

  2. Base64url-within-base64url nesting. The IPC payload would be base64url-encoded inside the JWS structure, which is then base64url- encoded for transport. IPC's flat structure means the decoded JSON is directly inspectable without double decoding - valuable for debugging, log analysis, and operator tooling.

  3. Single-object manipulation model. IPC middleware operates on one JSON object throughout the verify-modify-sign cycle. JWS JSON Serialization requires managing separate protected, header, payload, and signature fields with different encoding rules, increasing the surface area for implementation bugs in the re-signing path.

The core design principle: IPC optimizes for the re-signing hot path (verify upstream, update issuer, re-sign, forward) that executes at every trust boundary in every request. JCS provides a deterministic canonical form that any conformant implementation can independently reproduce from the parsed claims, without depending on how the upstream serialized the token.

Tradeoffs acknowledged:

  • IPCs are not directly verifiable by existing JWT/JWS libraries without an IPC-specific implementation. This is acceptable because IPC operates within platform infrastructure where purpose-built middleware is deployed at each trust boundary.
  • This choice increases the implementation barrier compared to JWS-based approaches: deployments MUST verify that their JCS implementation produces correct canonical output (Appendix E provides a test vector for this purpose) before using it in production.
  • The JCS library ecosystem (RFC 8785) is smaller than the JWS ecosystem. Implementors SHOULD validate their chosen JCS library against the RFC 8785 test vectors before production deployment.
  • A future revision of this specification MAY define a JWS-based profile for deployments that prefer JOSE tooling compatibility, provided the re-signing semantics and verification determinism requirements can be met. The authors welcome WG input on whether a constrained JWS JSON Serialization profile should be developed as an alternative encoding.

3. Trust Boundaries and Re-signing

3.1. Concept

At each trust boundary, the receiving service:

  1. Extracts the IPC from the incoming protocol binding.
  2. Verifies the signature using the upstream issuer's public key.
  3. Validates all enforcement rules (Section 5).
  4. Re-signs the IPC with its own private key, updating the issuer field.
  5. Forwards the re-signed IPC to the next service.

3.2. Verification Algorithm

A service receiving an IPC MUST execute the following steps in order. The service MUST reject at the first step that fails.

VERIFY-IPC(encoded_ipc, key_resolver, config):

  0. Size and Format Pre-check:
     If len(encoded_ipc) == 0 -> REJECT (401, "malformed_ipc")
     If len(encoded_ipc) > 4096 -> REJECT (401, "malformed_ipc")
     If encoded_ipc contains any character not in [A-Za-z0-9_-]
     -> REJECT (401, "malformed_ipc")
     // Base64url encoding MUST NOT include padding ('=' characters).
     // This pre-check prevents resource exhaustion from oversized
     // inputs (Section 8.6) and rejects obviously malformed values
     // before incurring decode and parse costs.

  1. Decode: base64url-decode encoded_ipc to obtain JSON bytes.
     If decoding fails -> REJECT (401, "malformed_ipc")

  2. Parse: JSON-parse the bytes into a Token object.
     If parsing fails -> REJECT (401, "malformed_ipc")
     If the JSON object contains duplicate member names
     -> REJECT (401, "malformed_ipc")
     // RFC 8259 states names SHOULD be unique but does not mandate it.
     // IPC requires uniqueness to prevent ambiguous claim values that
     // could differ between parser implementations, creating security
     // vulnerabilities where verification and enforcement use different
     // values for the same claim name.
     //
     // Implementation note: Verifiers MUST use a JSON parser that
     // detects and reports duplicate member names. Parsers that silently
     // retain only the first or last value for duplicate names are
     // non-conformant. Many standard library JSON parsers (e.g.,
     // Python's json.loads into dict, Go's json.Unmarshal into map)
     // silently discard duplicates. Implementations MUST use a parser
     // mode or wrapper that detects duplicates before constructing the
     // Token object.

  3. Version:
     If Token.version is not a string -> REJECT (401, "malformed_ipc")
     If Token.version does not match the format "^[1-9][0-9]*\.[0-9]+$"
     -> REJECT (401, "malformed_ipc")
     // Version MUST be exactly "<major>.<minor>" where major is a
     // non-zero-padded positive integer and minor is a non-negative
     // integer. Examples: "1.0", "2.1", "10.0". Invalid: "01.0",
     // "1", "1.0.1", "1.0-beta", "v1.0".
     Parse Token.version as "<major>.<minor>".
     If major version != "1" -> REJECT (401, "unsupported_version")
     // Minor version differences are acceptable per Section 2.2.

  4. Trust Level Dispatch:
     If Token.trustLevel is not an integer -> REJECT (401, "malformed_ipc")
     If Token.trustLevel == 1:
       If Token.alg is absent OR Token.alg is not a string OR Token.alg != "none"
       -> REJECT (401, "malformed_ipc")
       If Token.kid is present and Token.kid != ""
       -> REJECT (401, "malformed_ipc")
       If Token.signature is present and Token.signature != ""
       -> REJECT (401, "malformed_ipc")
       // Trust Level 1 IPCs MUST NOT carry kid or signature values.
       // A non-empty kid or signature in an unsigned IPC indicates
       // either corruption or a downgrade attempt.
       If config.isEnforcementPoint == true
       -> REJECT (401, "unsigned_token")
       // Enforcement points MUST NOT accept Trust Level 1 IPCs regardless
       // of config.allowUnsignedForAudit. This prevents downgrade attacks
       // where an unsigned IPC is substituted for a signed one.
       If config.allowUnsignedForAudit != true
       -> REJECT (401, "unsigned_token")
       Skip steps 5-6 (key resolution and signature verification)
       and proceed to Step 7. Trust Level 1 IPCs undergo structural
       validation only and MUST NOT be used for authorization decisions.
     If Token.trustLevel < 1 or Token.trustLevel > 2
     -> REJECT (401, "malformed_ipc")
     // For trustLevel == 2: continue to Step 5

  5. Algorithm Validation (Level 2+ only):
     If Token.alg is not one of: "EdDSA", "ES256", "HS256"
     -> REJECT (401, "unsupported_algorithm")
     If Token.alg is "EdDSA" or "ES256":
       If config.asymmetricAlgorithm is configured AND
       Token.alg != config.asymmetricAlgorithm
       -> REJECT (401, "algorithm_not_permitted")
       // Deployments MUST NOT mix EdDSA and ES256 within a chain.
       // config.asymmetricAlgorithm enforces deployment-wide
       // algorithm consistency.
     If Token.signature is not a string or is empty or is absent
     -> REJECT (401, "malformed_ipc")

  6. Key Resolution and Signature Verification:
     Pre-check: If Token.kid is absent, null, or not a string
        -> REJECT (401, 'malformed_ipc')
     If Token.issuer is absent, null, or not a string
        -> REJECT (401, 'malformed_ipc')
     a. Extract Token.kid and Token.issuer
     b. Resolve upstream_public_key using the tuple
        (Token.issuer, Token.kid, Token.alg):
        - Via key discovery endpoint (Section 3.6), OR
        - Via static key configuration
        The verifier MUST maintain a mapping of known upstream issuers
        to their key sources (via static config, key discovery endpoint,
        or service mesh control plane).
     c. If Token.kid is unknown or key not found
        -> REJECT (401, "unknown_key")
     c2. If the resolved key has status "compromised"
        -> REJECT (401, "unknown_key")
        // Compromised keys MUST be rejected regardless of token
        // validity or expiry.
     c3. Pre-validate Token.issuedAt for key validity comparison:
        If Token.issuedAt is absent, not a string, or does not match
        the canonical format YYYY-MM-DDTHH:MM:SSZ (20 characters)
        -> REJECT (401, "malformed_ipc")
        // This minimal validation allows safe comparison against
        // key validity timestamps. Full temporal validation
        // (including clock skew and expiry checks) occurs in Step 8.
        If the resolved key has a `validFrom` timestamp and
        Token.issuedAt < key.validFrom
        -> REJECT (401, "unknown_key")
        // The token claims to have been issued before this key
        // existed.
     c4. If the resolved key has status "retired" and key.validUntil
        is set:
        Accept only if Token.issuedAt <= key.validUntil.
        If Token.issuedAt > key.validUntil
        -> REJECT (401, "unknown_key")
        // A retired key should only verify tokens issued during
        // the key's active period.
     d. Verify that the resolved key belongs to Token.issuer. If the
        key source serves keys for multiple services, confirm the kid
        is associated with Token.issuer. When keys are statically
        configured, the configuration MUST explicitly associate each
        kid with its owning issuer identifier. A kid that resolves
        successfully but is not mapped to Token.issuer indicates a
        potential confused-deputy attack (T13). If mismatch:
        -> REJECT (401, "unknown_key")
     e. Construct canonical_bytes = JCS(Token excluding "signature")
     f. Decode sig = base64url-decode(Token.signature)
     g. Dispatch on Token.alg:
        If Token.alg == "EdDSA":
          Verify: Ed25519.Verify(upstream_public_key, canonical_bytes, sig)
        Else if Token.alg == "ES256":
          Verify: ECDSA-P256-SHA256.Verify(upstream_public_key, canonical_bytes, sig)
        Else if Token.alg == "HS256":
          If config.allowHMAC != true -> REJECT (401, "algorithm_not_permitted")
          Verify: HMAC-SHA256.Verify(shared_secret, canonical_bytes, sig)
        // Note: No Else branch is needed here. Step 5 has already
        // validated that Token.alg is one of the supported values.
        // Implementations MAY include a defensive Else -> REJECT for
        // defense-in-depth, but it is unreachable in conformant code.
     If verification fails -> REJECT (401, "signature_invalid")

  7. Type Validation:
     All REQUIRED claims MUST be present and of the correct JSON type
     as defined in Section 2.1 (string, integer, or array).
     - entityId, entityType, authorizedBy, sessionId, issuedAt,
       expiresAt, issuer, version: MUST be strings
     - delegationDepth, trustLevel: MUST be integers. Implementations
       MUST verify that these values are encoded as JSON integer
       literals without decimal points or exponent notation (matching
       the pattern `0|[1-9][0-9]*`). Values such as `2.0`, `2e0`, or
       `2E0` MUST be rejected as `malformed_ipc` even if mathematically
       integral. This ensures JCS canonicalization produces identical
       bytes across implementations regardless of parser numeric
       handling.
     - delegationChain: MUST be an array of strings
     - audience: MUST be a string. MUST be present when trustLevel >= 2.
        If trustLevel >= 2 and audience is absent -> REJECT (401, 'malformed_ipc')
        // Note: The audience non-empty check is enforced again at Step 9f
        // as defense-in-depth. Step 7 checks presence; Step 9f checks
        // semantic validity (non-empty, non-whitespace).
     - alg: MUST be a string (REQUIRED at all trust levels)
     - kid, signature (if present): MUST be strings.
       MUST be present when trustLevel >= 2.
       If trustLevel >= 2 and (kid is absent OR signature is absent)
       -> REJECT (401, 'malformed_ipc')
     - entityType MUST be one of: AUTONOMOUS, SUPERVISED, UTILITY,
       HUMAN, SYSTEM (case-sensitive exact match)
     If any claim is missing, null, of wrong type, or entityType is
     unrecognized -> REJECT (401, "malformed_ipc")

  8. Temporal Validity:
     a. Parse Token.issuedAt and Token.expiresAt as ISO 8601 UTC timestamps.
        Both MUST match the canonical format YYYY-MM-DDTHH:MM:SSZ (exactly
        20 characters, trailing "Z", no fractional seconds).
        If either fails to parse or does not match the canonical format
        -> REJECT (401, 'malformed_ipc')
     b. now = current UTC time
     c. If Token.expiresAt <= Token.issuedAt -> REJECT (401, "malformed_ipc")
        // expiresAt must be strictly after issuedAt (positive lifetime)
     c2. If (Token.expiresAt - Token.issuedAt) > config.maxTTL
        -> REJECT (401, "malformed_ipc")
        // The token's stated lifetime MUST NOT exceed the deployment's
        // maximum configured TTL. This prevents a compromised or
        // misconfigured IPC Creator from issuing long-lived tokens
        // that bypass short-TTL replay protection.
     d. If Token.issuedAt > now + config.clockSkew -> REJECT (403, "issued_in_future")
     e. If now > Token.expiresAt + config.clockSkew -> REJECT (403, "token_expired")
     f. If config.maxTokenAge > 0:
        age = now - Token.issuedAt
        If age > config.maxTokenAge + config.clockSkew -> REJECT (403, "token_too_old")

     Note: maxTokenAge is OPTIONAL and provides defense against tokens
     that are valid but were issued unreasonably long ago (e.g., due to
     clock manipulation at the creator). Deployments that do not require
     this check SHOULD set config.maxTokenAge to 0 (disabled).

     Note: The addition of config.clockSkew to the maxTokenAge comparison
     is intentional. Without this tolerance, tokens issued at the boundary
     of maxTokenAge could be rejected by verifiers whose clocks are
     slightly behind the issuer's clock. The effective maximum age is
     therefore maxTokenAge + clockSkew. Deployments that require a strict
     maximum age SHOULD account for this extension when configuring
     maxTokenAge.

  9. Structural Integrity:
     a. If len(Token.delegationChain) == 0 -> REJECT (403, "empty_chain")
     b. If Token.delegationDepth != len(Token.delegationChain) - 1
        -> REJECT (403, "depth_mismatch")
     c. If len(Token.delegationChain) >= 2 AND
        Token.authorizedBy != Token.delegationChain[len-2]
        -> REJECT (403, "chain_consistency_error")
     d. If len(Token.delegationChain) == 1 AND
        Token.authorizedBy != Token.entityId
        -> REJECT (403, "chain_consistency_error")
     e. If Token.entityId != Token.delegationChain[len(Token.delegationChain) - 1]
        -> REJECT (403, "chain_consistency_error")
     f. If Token.trustLevel >= 2 AND (Token.audience == "" OR Token.audience contains only whitespace)
        -> REJECT (401, "malformed_ipc")
        // An empty or whitespace-only audience for Trust Level 2+ is a
        // structural validity error (malformed token), not a semantic
        // audience mismatch.
     g. Identifier constraints:
        For each of: Token.entityId, Token.authorizedBy, Token.issuer,
        Token.audience (if present), and each entry in Token.delegationChain:
          If len(identifier) < 1 or len(identifier) > 128
          -> REJECT (401, 'malformed_ipc')
          If identifier contains whitespace or control characters (U+0000-U+001F, U+007F)
          -> REJECT (401, 'malformed_ipc')

  10. Depth Enforcement:
     If Token.delegationDepth > config.maxDepth
     -> REJECT (403, "depth_exceeded")

  11. Circular Detection:
     seen = {}
     For each entity in Token.delegationChain:
       If entity in seen -> REJECT (403, "circular_delegation")
       seen.add(entity)

  12. Audience (terminal enforcement points only):
      If config.isTerminalEnforcementPoint:
        If Token.audience is present AND Token.audience != "" AND Token.audience != config.serviceId
        -> REJECT (403, "audience_mismatch")
      // Trust boundaries that re-sign and forward MUST skip this step.
      // Only the terminal service that makes the final authorization
      // decision (and does not re-sign) executes audience verification.
      //
      // isTerminalEnforcementPoint is a static configuration property
      // of the service's role in the deployment topology.
      //
      // Services that ALWAYS act as terminal enforcement points (never
      // forward) set isTerminalEnforcementPoint = true.
      //
      // Services that ALWAYS act as pass-through trust boundaries (never
      // make final decisions) set isTerminalEnforcementPoint = false.
      //
      // Services that sometimes forward and sometimes terminate MUST
      // set isTerminalEnforcementPoint = false in the verification
      // algorithm and perform audience verification in application
      // logic AFTER VERIFY-IPC returns ACCEPT, only when the service
      // determines it will not forward. Such services MUST be
      // configured as both Trust Boundary and Enforcement Point
      // (per Section 11.3) and MUST document their audience
      // verification behavior.

  13. Revocation (if RevocationChecker configured):
      // Revocation checks MUST use a local in-memory cache or
      // bounded-latency shared cache. Verifiers MUST NOT perform
      // unbounded synchronous network calls per entity during
      // verification. If pull-based revocation is used, polling
      // MUST refresh a local cache asynchronously; verification
      // reads from the cache only.
      // If revocation state is unavailable, enforcement points
      // MUST either fail closed (reject the IPC) or operate under
      // an explicitly configured degraded mode that is logged and
      // alerted.
      For each entity in Token.delegationChain:
        revocation = RevocationChecker.GetRevocation(entity)
        If revocation is not null:
          If revocation.scope == "cascade"
          -> REJECT (403, "entity_revoked")
          If revocation.scope == "single" AND entity == Token.entityId
          -> REJECT (403, "entity_revoked")
      // "single" revocations only reject if the revoked entity is
      // the current executor (entityId), not an intermediate in the chain.

  13b. Caller-to-Issuer Binding (RECOMMENDED):
      If config.callerBindingEnabled AND caller_identity is available
      (from mTLS certificate, bearer token, or service mesh metadata):
        If caller_identity does not match Token.issuer AND
        caller_identity is not in config.allowedPresentersFor[Token.issuer]
        -> REJECT (403, "issuer_caller_mismatch")
      // This step verifies that the authenticated transport identity
      // of the immediate caller is authorized to present IPCs signed
      // by Token.issuer. Without this check, a service that obtains
      // a valid IPC could present it directly to a downstream service
      // bypassing the intended chain (confused deputy, T13).
      // This step requires caller_identity to be available from the
      // transport layer (mTLS, bearer token, service mesh). If caller
      // identity is not available, this step is skipped but a warning
      // SHOULD be logged.

  14. Replay Detection (enforcement points only):
      If config.isEnforcementPoint:
        // The replay cache operation MUST be atomic (check-and-insert).
      // Implementations SHOULD use atomic primitives (e.g., Redis SETNX,
      // CAS operations) to prevent race conditions (T18).
      If replay cache is unavailable or at capacity:
        -> REJECT (503, "replay_cache_unavailable")
        // Enforcement points MUST fail closed when replay detection
        // cannot be performed. Failing open would allow replay attacks
        // during infrastructure degradation — exactly when attacks are
        // most likely.
      If Token.sessionId exists in replay cache
        -> REJECT (403, "token_replayed")
      Else: Atomically insert Token.sessionId into replay cache with
          TTL = max(config.replayCacheTTL, Token.expiresAt - now() + config.clockSkew)
        // The replay cache TTL MUST be at least config.clockSkew to
        // prevent premature eviction. The config.replayCacheTTL parameter
        // (Section 5.6) provides the normative minimum floor.
      // Pure trust boundaries that only re-sign MAY skip this step.

     // Implementations MUST NOT authorize or execute the requested
     // operation until all steps complete successfully. Optimistic
     // execution that begins processing before ACCEPT is non-conformant.

  15. ACCEPT: Token is valid.

3.3. Re-signing Algorithm

A trust boundary that forwards requests downstream MUST re-sign the IPC after successful verification. The re-signing algorithm is:

Trust boundaries MUST NOT re-sign IPCs with alg set to "HS256". If a trust boundary receives an HMAC-signed IPC that passes verification (because it is configured as the single-hop verifier), it MUST NOT forward a re-signed IPC downstream. HMAC-signed IPCs are terminal by definition — the trust boundary may only act as an Enforcement Point for that request, not as a pass-through re-signer. If re-signing and forwarding is required, the deployment MUST migrate to EdDSA (Section 2.6).

RESIGN-IPC(token, service_id, signing_private_key):

  1. Verify that token passed VERIFY-IPC (Section 3.2).
     Re-signing MUST NOT occur without prior verification.
     If token.alg == "HS256" -> ABORT (cannot re-sign HMAC tokens)

  2. Update mutable fields:
     token.issuer = service_id
     token.kid = signing_key_id

  3. Compute new signature:
     a. canonical_bytes = JCS(token excluding "signature")
     b. Dispatch on token.alg:
        If token.alg == "EdDSA":
          sig = Ed25519.Sign(signing_private_key, canonical_bytes)
        Else if token.alg == "ES256":
          sig = ECDSA-P256-SHA256.Sign(signing_private_key, canonical_bytes)
     c. token.signature = base64url-encode(sig)

  4. Immutability check:
     All claims other than "issuer", "kid", and "signature" MUST remain
     unchanged from the verified token. If any other field was
     modified, the implementation is non-conformant.

     Note: The token object used for JCS canonicalization in Step 3a
     MUST include all claims present in the verified token, including
     any unrecognized extension claims (Section 2.2). Implementations
     MUST preserve the complete JSON object through the re-signing
     process. Deserializing into a typed struct that discards unknown
     fields will produce incorrect canonical output, causing downstream
     signature verification to fail.

  5. Encode for transport:
     encoded = base64url-encode(JSON(token))

  6. Set protocol binding:
     HTTP:  Set header Identity-Propagation-Context: <encoded>
     gRPC:  Set metadata identity-propagation-context: <encoded>
     Kafka: Set record header identity-propagation-context: <encoded>

3.4. Immutability

The IPC payload (all claims except issuer, kid, and signature) MUST NOT be modified during re-signing. Only the issuer, kid, and signature fields change during re-signing.

3.5. Key Management

Each trust boundary service maintains: - Its own signing key (private) - The public key of the upstream issuer (for verification)

A key discovery endpoint (similar to OIDC .well-known/jwks.json) is RECOMMENDED for dynamic key retrieval.

3.6. Key Discovery Endpoint

Services fulfilling the Trust Boundary or IPC Creator conformance classes (Section 11.3) MUST make their public keys available through at least one of the following mechanisms:

  1. The IPC key discovery HTTP endpoint defined below (REQUIRED for multi-vendor interoperability claims per Section 1.8).
  2. An authenticated platform control plane that provides equivalent key material and metadata (e.g., service mesh xDS/SDS, SPIFFE bundle endpoints, Kubernetes secrets).

The HTTP endpoint is the mandatory-to-implement interoperability profile. Deployments using control-plane key distribution alone MUST ensure that the distributed key material includes all fields defined below (serviceId, keys with status, validFrom, validUntil). Enforcement Points that only verify (and never re-sign) MAY use static key configuration alone but SHOULD support key discovery for operational flexibility.

The key discovery endpoint MUST be available at:

GET /.well-known/ipc-keys

The response MUST be a JSON object:

{
  "serviceId": "scope-enforcement",
  "cacheMaxAge": 300,
  "keys": [
    {
      "kid": "scope-enforcement-key-1",
      "kty": "OKP",
      "crv": "Ed25519",
      "x": "<base64url-encoded public key>",
      "use": "sig",
      "status": "active",
      "validFrom": "2026-07-01T00:00:00Z",
      "validUntil": null
    }
  ]
}
  • serviceId (string, REQUIRED): This service's IPC issuer identifier. The serviceId value in the key discovery response MUST exactly match the issuer claim value that this service sets when signing or re-signing IPCs. Verifiers use this correspondence to confirm that a resolved key belongs to the claimed issuer (Section 3.2, Step 6d).
  • cacheMaxAge (integer, OPTIONAL): Maximum number of seconds a client SHOULD cache this response. If absent, clients use their locally configured cache TTL (RECOMMENDED default: 300 seconds). The server MAY reduce this value during key rotation to accelerate downstream key refresh (e.g., set to 30 seconds after publishing a new key). Clients MUST NOT cache responses longer than this value when present.
  • keys (array, REQUIRED): JWK objects with IPC metadata.

    • status (string, REQUIRED): "active", "retired", or "compromised".
    • validFrom (string, REQUIRED): ISO 8601 UTC timestamp.
    • validUntil (string or null, REQUIRED): null if still active.

Implementations MUST cache key discovery responses for no longer than the configured cache TTL (RECOMMENDED default: 300 seconds) and MUST refresh before using a key whose validUntil has passed.

Key material validation depends on the algorithm: - For Ed25519 keys ("kty": "OKP", "crv": "Ed25519"): the x field MUST be exactly 43 characters of base64url (representing 32 bytes of public key material, without padding). Verifiers MUST reject keys where x does not decode to exactly 32 bytes. - For ES256 keys ("kty": "EC", "crv": "P-256"): the x and y fields MUST each be exactly 43 characters of base64url (representing 32 bytes each). Both coordinates are REQUIRED. Verifiers MUST reject keys where x or y does not decode to exactly 32 bytes.

The key discovery endpoint exposes only public key material and MAY be served without authentication (public keys are not secret). However, deployments SHOULD apply rate limiting to prevent enumeration attacks and SHOULD restrict access to the platform's internal network when the endpoint is not intended for external consumption.

Deployments MUST maintain a mapping from issuer identifiers to key discovery URLs. This mapping MAY be distributed via service mesh configuration, DNS service discovery, or static configuration. The mechanism is deployment-specific and not defined by this specification.

3.7. Error Response Format

When IPC verification or enforcement fails, the error response MUST use the following JSON structure:

{
  "error": "signature_invalid",
  "errorDescription": "IPC signature verification failed",
  "sessionId": "0191a0b0-c8f4-7b2e-9a1c-3d4e5f6a7b8c",
  "entityId": "cell-configurator",
  "issuer": "api-gateway"
}

Fields:

  • error (string, REQUIRED): Machine-readable error code. MUST be one of: malformed_ipc, unsupported_version, unsigned_token, signature_invalid, issued_in_future, token_expired, token_replayed, unknown_key, token_too_old, empty_chain, depth_mismatch, chain_consistency_error, depth_exceeded, circular_delegation, audience_mismatch, entity_revoked, algorithm_not_permitted, unsupported_algorithm, replay_cache_unavailable, issuer_caller_mismatch.
  • errorDescription (string, REQUIRED): Human-readable description.
  • sessionId (string, OPTIONAL): From the IPC if parseable.
  • entityId (string, OPTIONAL): From the IPC if parseable.
  • issuer (string, OPTIONAL): From the IPC if parseable.

IPC error responses use camelCase field names (e.g., errorDescription) to align with the IPC claims naming convention (entityId, sessionId). This intentionally differs from OAuth 2.0's error_description convention.

The sessionId, entityId, and issuer fields SHOULD be included when the IPC was successfully parsed (even if signature verification failed). They MUST NOT be included if the IPC could not be decoded.

3.8. Verification Failure

When IPC verification fails at a trust boundary, the service MUST:

  1. Reject the request. The IPC MUST NOT be forwarded downstream.
  2. Return an appropriate error to the caller:

    • HTTP: 401 Unauthorized if the IPC signature is invalid, or if the IPC header is absent and fail-closed mode is configured (Section 3.9).
    • HTTP: 403 Forbidden if the IPC is valid but fails enforcement rules (e.g., delegationDepth > maxDepth, expired, audience mismatch).
    • gRPC: UNAUTHENTICATED (code 16) or PERMISSION_DENIED (code 7) following the same distinction.
  3. Log the failure with sufficient detail for audit (sessionId, entityId, failure reason) without logging the full IPC (which could be replayed from logs).

Trust Boundaries and Enforcement Points MUST log the following for every IPC verification (successful or failed): - sessionId of the IPC - entityId of the current executor - Verification result (accept/reject) - If rejected: the error code from Section 3.7 - Timestamp of the verification event

This requirement is normative because the single-signer model (Section 8.5) relies on audit trails for forensic traceability. Without audit logging, a compromised intermediate cannot be detected after the fact.

During migration periods, deployments MAY configure enforcement points to operate in "permissive mode" where IPC failures are logged but not enforced. This MUST be a temporary configuration and MUST NOT be used in production enforcement.

3.9. Absent IPC

The behavior when an IPC header is absent from a request is deployment-specific:

  • Security-critical enforcement points SHOULD reject requests lacking a valid IPC (fail-closed).
  • Services operating in mixed environments during migration MAY accept requests without IPC, provided the bearer token alone provides sufficient authorization for the requested operation.
  • Deployments MUST document which services require IPC and which operate in a degraded mode without it.

4. Delegation Issuance

4.1. IPC Creation

An IPC is created at the platform trust boundary (e.g., an API gateway) when:

  1. An entity authenticates (e.g., via OAuth 2.0 JWT).
  2. Delegation context is provided (e.g., via a delegation-token from an STS).
  3. The trust boundary validates both and constructs the IPC.

4.2. Delegation Token

A delegation-token is a short-lived, signed token issued by a platform STS that establishes the delegation relationship. It is consumed by the trust boundary during IPC creation and discarded afterward.

The delegation-token is NOT the IPC. It is the input from which the IPC is constructed. The format and issuance protocol for delegation-tokens is out of scope for this specification - platforms MAY use RFC 8693 Token Exchange, proprietary STS protocols, or any mechanism that provides authenticated delegation context to the trust boundary.

4.3. IPC Creation Algorithm

The following algorithm defines how an IPC Creator constructs a new IPC:

CREATE-IPC(authenticated_entity, delegation_context, config):

  1. Determine delegation chain:
     If delegation_context is present:
       chain = delegation_context.chain + [authenticated_entity.id]
     Else:
       chain = [authenticated_entity.id]

  2. Validate chain depth:
     If len(chain) - 1 > config.maxDepth -> REJECT

  3. Set claims:
     version = "1.0"
     entityId = authenticated_entity.id
     entityType = authenticated_entity.type
     If len(chain) == 1:
       authorizedBy = entityId  // self-authorized root
     Else:
       authorizedBy = chain[len(chain) - 2]  // immediate parent
     delegationDepth = len(chain) - 1
     delegationChain = chain
     audience = config.targetServiceId  // final enforcement point
       If trustLevel >= 2 AND audience is empty -> REJECT (creation failed)
     sessionId = generateUUIDv7()
     issuedAt = now()
     If delegation_context is present:
       expiresAt = min(now() + config.maxTTL,
                       delegation_context.expiresAt)  // attenuation
     Else:
       expiresAt = now() + config.maxTTL
     issuer = config.serviceId
     kid = config.currentKeyId
     trustLevel = 2
     alg = config.asymmetricAlgorithm  // "EdDSA" (default) or "ES256" (FIPS)

  4. Sign:
     canonical_bytes = JCS(token excluding "signature")
     If alg == "EdDSA":
       token.signature = base64url(Ed25519.Sign(config.privateKey,
                                                canonical_bytes))
     Else if alg == "ES256":
       token.signature = base64url(ECDSA-P256-SHA256.Sign(config.privateKey,
                                                          canonical_bytes))

  5. Encode:
     encoded = base64url(JSON(token))

  6. Attach to outbound request via protocol binding (Section 6)

IPC Creators SHOULD implement rate limiting per authenticated entity to prevent resource exhaustion at downstream replay caches and signing infrastructure. RECOMMENDED limits: no more than 100 IPC creations per second per entity under normal operation. Deployments with higher-throughput requirements MUST ensure their replay cache infrastructure can sustain the expected creation rate without evicting entries before their TTL expires.

A new IPC MUST be created for each sub-delegation. Existing IPCs MUST NOT be modified to add entries to the delegation chain - the immutability rule (Section 3.4) prevents this. When an entity sub-delegates, the platform creates a fresh IPC with the extended chain.

5. Enforcement Rules

5.1. Maximum Delegation Depth

Each deployment MUST configure a maximum delegation depth (maxDepth). An IPC with delegationDepth > maxDepth MUST be rejected.

Deployments SHOULD configure consistent maxDepth values across all trust boundaries and enforcement points in a given service chain. Inconsistent values may result in IPCs being accepted at early hops but rejected downstream. The IPC Creator SHOULD set delegation chain depth based on the most restrictive enforcement point in the target path.

5.2. Chain Verification

Every entry in delegationChain MUST be traceable to an accountable root (the first entry). The root SHOULD be either:

  • A human principal (entityType: HUMAN), OR
  • A system principal (entityType: SYSTEM) for platform-initiated operations

Root type enforcement is a deployment policy decision. Deployments without a principal registry MAY accept any root identifier format that conforms to the identifier constraints in Section 2.2.

Deployments MAY restrict roots to human principals only via policy. An IPC where the first entry in delegationChain does not match the deployment's allowed root types MUST be rejected.

The root entity's type is determined by the IPC creator (trust boundary) at issuance time based on the authenticated credential. The entityType claim in the IPC reflects the current executing entity, not the root. Verifiers determine root type via deployment-specific policy (e.g., convention that email-format identifiers are HUMAN, or a local principal registry lookup).

Root type enforcement is a policy decision applied after the verification algorithm (Section 3.2) completes. It is not encoded in the verification algorithm because the IPC does not carry the root entity's type - only the current entity's type (entityType). Enforcement points that require root-type validation MUST resolve the root identity's type through external policy.

5.3. Expiry Attenuation

Expiry attenuation applies at IPC creation time, not during re-signing.

When a new IPC is created as a result of sub-delegation (e.g., Agent-1 delegates to Sub-Agent-1, and the platform creates a new IPC for the sub-agent's request):

child.expiresAt <= parent.expiresAt

A child IPC MUST NOT have a longer lifetime than the parent delegation that authorized it. This ensures authority shrinks over time, never grows.

This rule does NOT apply during re-signing (Section 3.3). Re-signing preserves the original expiresAt unchanged - the immutability rule (Section 3.4) prevents any modification of expiresAt during re-signing.

Security note: Expiry attenuation is enforced by the IPC Creator at issuance time. Downstream services cannot independently verify that attenuation was correctly applied. A compromised or misconfigured IPC Creator could issue child IPCs with longer lifetimes than their parent delegation. The maxTokenAge check (verification Step 8f) provides a deployment-wide hard cap independent of attenuation.

5.4. Circular Detection

If any identifier appears more than once in the delegationChain, the IPC MUST be rejected. This prevents circular delegation.

5.5. Cascade Revocation

When a parent entity is revoked:

  1. A revocation event is published (e.g., via event-streaming topic).
  2. All enforcement points receiving IPCs where the revoked entity appears in the delegationChain MUST reject the IPC as specified by the revocation scope rules in Section 5.5.1.
  3. Additionally, IPCs naturally expire within their short TTL, providing a secondary bound on exposure.

5.5.1. Revocation Event Format

The revocation event is a JSON object published to a well-known topic (e.g., ipc-revocation):

{
  "version": "1.0",
  "eventType": "entity.revoked",
  "eventId": "evt-0191b2c3-d4e5-7f6a-8b9c-0d1e2f3a4b5c",
  "entityId": "energy-optimizer",
  "revokedAt": "2026-07-17T08:02:30Z",
  "reason": "compromised",
  "scope": "cascade",
  "issuedBy": "platform-admin",
  "alg": "EdDSA",
  "kid": "platform-admin/key-1",
  "signature": "Base64url(...)"
}

Field definitions:

Table 3
Field Type Required Description
version string REQUIRED Revocation event schema version (e.g., "1.0"). Major/minor semantics per Section 2.2 versioning rules. Receivers MUST reject events with an unsupported major version.
eventType string REQUIRED MUST be "entity.revoked"
eventId string REQUIRED Unique event identifier (UUID v7 RECOMMENDED)
entityId string REQUIRED Identifier of the revoked entity
revokedAt string REQUIRED ISO 8601 UTC timestamp of revocation
reason string REQUIRED One of: "compromised", "policy_violation", "operator_request", "expired", "rotation"
scope string REQUIRED "cascade" or "single"
issuedBy string REQUIRED Identifier of the revoking authority
alg string REQUIRED (production) Signing algorithm identifier ("EdDSA" or "ES256"). Enables algorithm-agnostic verification.
kid string REQUIRED (production) Key identifier for the revoking authority's signing key. Enables key selection during rotation.
signature string REQUIRED (production), OPTIONAL (development) Signature over all other fields (JCS-canonical, excluding signature itself) using the algorithm specified in alg.

Revocation event signing keys are discovered via the same key discovery mechanism as IPC signing keys (Section 3.6). The issuedBy field corresponds to the serviceId in the key discovery endpoint. If no key discovery endpoint is available for the revoking authority, signature verification is deployment-specific.

Processing rules:

  • When scope is cascade, enforcement points MUST reject any IPC where the revoked entityId appears anywhere in the delegationChain.
  • When scope is single, only IPCs where the revoked entity is the entityId (current executor) are rejected.
  • Enforcement points SHOULD verify the signature before acting on the event. Enforcement points operating in production MUST reject revocation events without a valid signature unless the event source is authenticated by the transport layer (e.g., mTLS-authenticated event producer). In development environments, unsigned revocation events MAY be accepted with a logged warning.
  • Duplicate eventId values MUST be ignored (idempotent processing). Enforcement points MUST maintain deduplication state for eventId values for at least 24 hours after first receipt. Revocation events with revokedAt timestamps older than 24 hours from the current time SHOULD be rejected as stale, preventing replay of old revocation events. Deployments requiring a different deduplication window MUST document their chosen value and ensure it exceeds the maximum propagation delay in their event infrastructure.
  • Enforcement points MUST NOT remove revocation state until the entity's maximum IPC TTL has elapsed after revokedAt.

Enforcement points SHOULD garbage-collect revocation state entries whose revokedAt timestamp is older than the maximum configured IPC TTL. Once all IPCs that could reference a revoked entity have expired, the revocation entry serves no enforcement purpose and MAY be removed from the active revocation set (while retaining it in audit logs).

Guidance on scope selection: - Use cascade when the entity is compromised or its key material may be leaked - all downstream authority derived from this entity is suspect. - Use single when the entity's current execution should be stopped but its prior delegations remain trustworthy (e.g., an agent that completed its task and should not accept new requests, but its sub-agents are still valid). With single, the entity MAY still appear as an intermediate in other IPCs' delegationChain without causing rejection.

5.5.2. Propagation Guarantees

This specification does not mandate a specific propagation latency. However, implementations SHOULD target sub-5-second propagation to minimize the window of exposure. The short IPC TTL (RECOMMENDED default: <= 5 minutes; deployment-configurable) provides a hard upper bound on exposure regardless of revocation propagation timing.

5.5.3. Partition Tolerance

During network partitions where revocation events cannot be delivered, enforcement points MUST continue to validate IPC expiry (expiresAt). The short TTL ensures that even without revocation delivery, compromised IPCs expire naturally within the configured window.

5.5.4. Revocation Checking Interface

Enforcement points that support revocation MUST implement one of the following revocation checking mechanisms:

Option A: Event-Driven (Push) - Subscribe to the ipc-revocation topic (or equivalent event stream) - Maintain a local revocation set in memory or shared cache - Check every IPC's delegationChain against the local revocation set - Reject if any entity in the chain appears in the revocation set

Option B: Polling (Pull) - Query a revocation status endpoint: GET /ipc/v1/revocation-status/{entityId} - Response: {"revoked": true/false, "revokedAt": "...", "scope": "..."} - Cache responses for no longer than the deployment's configured revocation cache TTL (RECOMMENDED default: 30 seconds) - Check every IPC's delegationChain against cached status

Option C: Expiry-Only (No Infrastructure) - If no revocation infrastructure is deployed, enforcement points MUST rely solely on IPC expiry (expiresAt) for revocation - This is acceptable only when TTLs are short (RECOMMENDED default: <= 5 minutes) - Deployments using this option MUST document the maximum exposure window in their security assessment

The revocation checking mechanism is deployment-specific. This specification does not mandate a single mechanism but requires that deployments explicitly choose and document which option they use.

When a revocation check determines that an entity is revoked, the enforcement point MUST reject the IPC with entity_revoked regardless of other validity checks.

5.6. Configuration Parameters

This section defines the configuration parameters referenced throughout this specification, with normative defaults and constraints to ensure interoperability between independent implementations.

Table 4
Parameter Type Default Normative Constraint Description
maxDepth integer 5 MUST be >= 1, MUST NOT exceed 10 Maximum delegation chain depth
maxTTL duration 300s (5 min) MUST be >= 30s, MUST NOT exceed 600s (10 min) Maximum IPC lifetime
clockSkew duration 30s MUST be >= 5s, MUST NOT exceed 60s, MUST be less than maxTTL Clock skew tolerance for temporal checks. The constraint clockSkew < maxTTL ensures that the skew tolerance does not exceed the token lifetime, which would undermine short-TTL replay protection.
maxTokenAge duration 0 (disabled) If > 0, MUST be >= maxTTL Maximum age from issuedAt (0 = disabled). The constraint maxTokenAge >= maxTTL prevents false rejections: since a token's lifetime can be up to maxTTL, setting maxTokenAge below maxTTL would cause valid tokens to be rejected before their expiresAt. The maxTokenAge check provides value as a deployment-wide hard cap when verifiers and creators have different maxTTL configurations, or as defense against clock manipulation at the creator.
replayCacheTTL duration derived MUST be >= maxTTL + clockSkew Minimum replay cache entry lifetime
keyDiscoveryCacheTTL duration 300s MUST be >= 30s, MUST NOT exceed 3600s Key discovery response cache duration
allowUnsignedForAudit boolean false N/A Whether Trust Level 1 IPCs are accepted
allowHMAC boolean false N/A Whether HS256 (HMAC-SHA-256) is permitted
isTerminalEnforcementPoint boolean (required) N/A Whether this service always acts as terminal (true), never terminal (false). Dual-role services set false and check audience in application logic (Section 3.2 Step 12).
isEnforcementPoint boolean (required) N/A Whether this service makes authorization decisions
serviceId string (required) MUST conform to identifier constraints (Section 2.2) This service's IPC issuer identifier

Configuration consistency constraints:

  • If isTerminalEnforcementPoint is true, then isEnforcementPoint MUST also be true. A terminal enforcement point is by definition an enforcement point (it makes the final authorization decision). Implementations MUST reject configurations where isTerminalEnforcementPoint = true and isEnforcementPoint = false.
  • If the service is configured as a Trust Boundary (i.e., it re-signs IPCs per Section 3.3), allowHMAC MUST be false. Implementations MUST reject configurations where a Trust Boundary service has allowHMAC = true (Section 2.6).

Normative requirements:

  • Implementations MUST use the default values specified above unless explicitly overridden by deployment configuration.
  • Implementations MUST reject configuration where clockSkew is greater than or equal to maxTTL. A clock skew tolerance that equals or exceeds the token lifetime effectively disables temporal validity as a security control.
  • Implementations MUST reject configuration where maxTTL exceeds 600 seconds (10 minutes). Deployments requiring longer lifetimes are outside the threat model of this specification and MUST conduct a dedicated security assessment documenting the increased replay and revocation exposure windows.
  • Implementations MUST reject configuration where maxDepth exceeds 10. Deeper chains indicate an architectural issue that should be resolved by restructuring the service topology.
  • The replayCacheTTL MUST be at least maxTTL + clockSkew to ensure replay detection covers the full validity window of any IPC. Implementations SHOULD derive this automatically from the configured maxTTL and clockSkew values rather than requiring manual configuration.
  • Two conformant implementations using default configuration values MUST be able to interoperate without additional coordination.

6. Protocol Bindings

IPCs MUST be transmitted only over channels protected by TLS 1.2 or later. Transmission of IPCs over unencrypted channels is a protocol violation. This requirement applies to all protocol bindings defined in this section.

Note on IETF Working Group scope: The HTTP binding (Section 6.1) is the primary interoperability profile and falls within the scope of IETF working groups focused on HTTP-based protocols (e.g., OAuth, WIMSE). The gRPC (Section 6.2) and event-streaming (Section 6.3) bindings address real deployment requirements in multi-protocol platforms but may be considered for separate companion documents if the adopting WG's charter is limited to HTTP. The IPC semantics (format, verification algorithm, trust model) are protocol-agnostic; only the transport encoding differs across bindings.

6.1. HTTP

The IPC is carried in the HTTP header:

Identity-Propagation-Context: <base64url-encoded IPC>

Note: The X- prefix is not used, per RFC 6648 which deprecates custom header prefixes for new headers.

6.2. gRPC

The IPC is carried in gRPC metadata:

identity-propagation-context: <base64url-encoded IPC>

6.3. Event-Streaming (Kafka)

The IPC is carried in a record header:

Header key: identity-propagation-context
Header value: <base64url-encoded IPC>

6.4. Protocol Transition Behavior

When a service receives a request via one protocol and emits via another (e.g., HTTP to gRPC, or gRPC to Kafka), this SHOULD be treated as a trust boundary unless the deployment's security model explicitly exempts it. The service MUST:

  1. Verify the incoming IPC using the inbound protocol's binding
  2. Execute the full verification algorithm (Section 3.2)
  3. Re-sign the IPC (Section 3.3)
  4. Emit the re-signed IPC using the outbound protocol's binding

The verification and re-signing requirements apply regardless of the protocol direction. A Kafka consumer that triggers an HTTP call to a downstream service MUST verify the IPC from the Kafka record header and propagate it in the HTTP Identity-Propagation-Context header.

Audience semantics apply at the terminal enforcement point regardless of which protocol delivered the IPC.

IPC is a request-direction mechanism. Responses do not carry IPC. Asynchronous callbacks or event-driven responses that initiate new request chains require fresh IPC creation at the originating service.

6.5. Header Value ABNF

The Identity-Propagation-Context header value is defined by the following ABNF [RFC5234]:

IPC-header-value = base64url-encoded-ipc
base64url-encoded-ipc = 1*4096base64url-char
base64url-char = ALPHA / DIGIT / "-" / "_"

; The decoded value is a JSON object conforming to the IPC
; schema defined in Section 2.1, serialized per Section 2.4.

The header field MUST contain exactly one Base64url-encoded IPC. Multiple IPCs in a single header value are not permitted. If a request contains multiple Identity-Propagation-Context headers, the verifier MUST reject with malformed_ipc.

Base64url encoding MUST be used without padding (per RFC 4648 Section 5).

6.6. Event-Streaming Retention Considerations

In event-streaming systems (e.g., Kafka) where records are retained beyond the IPC's expiresAt, consumers processing historical records will encounter expired IPCs. The following guidance applies:

  • During real-time processing, temporal validity (Step 8) MUST be enforced normally.
  • During historical replay or reprocessing, enforcement points MAY skip temporal validity checks (Step 8) while still validating structural integrity and chain semantics. This mode MUST be explicitly configured and MUST be limited to replay/audit scenarios. Services operating in historical replay mode MUST NOT create new IPCs (including fan-out IPCs) from records processed in this mode, as doing so would circumvent expiry attenuation by minting fresh tokens from expired authority.
  • Alternatively, deployments MAY strip IPC from records at retention boundaries and rely on audit logs for historical provenance.

Intermediate services that introduce queuing delays (e.g., message broker consumers with processing backlogs) SHOULD verify temporal validity before forwarding and SHOULD reject IPCs with remaining lifetime shorter than the expected downstream processing time.

7. Trust Levels

Table 5
Level Name Description Use Case
1 Unsigned No signature, audit trail only Development, logging
2 Signed HS256 or EdDSA (Ed25519) signature Authorization, enforcement
3 Reserved Reserved for future extension N/A

Note: Trust Level 3 is reserved for future specification of attested or countersigned IPCs (e.g., hardware-attested or multi-party countersigned). This version of the specification defines only Levels 1 and 2. Implementations MUST NOT use Level 3 until a future revision defines its semantics. Verifiers implementing this version of the specification MUST reject IPCs with trustLevel values other than 1 or 2 (per Section 3.2, Step 4). A future version that defines Trust Level 3 semantics will require verifier upgrades; existing verifiers will safely reject Level 3 IPCs rather than processing them with undefined behavior.

For Trust Level 1 (unsigned) IPCs: - The alg claim MUST be set to the string "none" - The kid claim MUST be absent or empty string - The signature claim MUST be absent or empty string - All other claims MUST be present and conform to Section 2.1 - Trust Level 1 IPCs MUST NOT be used for authorization decisions (Section 8.3) and exist solely for development, debugging, and audit trail purposes

8. Security Considerations

8.1. Replay Protection

IPCs SHOULD have short TTLs (RECOMMENDED default: <= 5 minutes; deployments MUST configure a maximum TTL appropriate to their threat model) to limit replay windows. The sessionId claim provides request-level uniqueness.

Enforcement points MUST maintain a replay cache keyed by sessionId for at least the deployment's maximum configured IPC TTL. If a sessionId has been seen before within the cache window, the IPC MUST be rejected with token_replayed.

The replay cache SHOULD be shared across all instances of the same enforcement point (e.g., via Redis or equivalent shared store) in horizontally-scaled deployments. A local in-process cache is acceptable only for single-instance deployments.

Without a shared replay cache, same-service replay within the TTL window is possible from a different instance. The short TTL and audience binding provide partial mitigation. Deployments that cannot deploy shared state MUST document this as an accepted risk.

For Trust Level 2 deployments, the combination of REQUIRED audience and sessionId replay cache provides defense-in-depth against both cross-service and same-service replay attacks.

Trust boundary services that also make enforcement decisions (i.e., services fulfilling both the Trust Boundary and Enforcement Point conformance classes from Section 11.3) MUST also maintain a replay cache. A pure pass-through trust boundary that only re-signs without making authorization decisions MAY skip replay checking.

Each IPC creation produces a unique sessionId. When an entity sub-delegates (creating a new IPC with an extended chain), the child IPC receives its own unique sessionId. The parent IPC's sessionId and the child IPC's sessionId are independent values. Replay caches operate per-IPC: they prevent the same IPC from being presented twice, not multiple IPCs from the same original request.

For high-throughput deployments where a centralized replay cache creates unacceptable latency or availability risk, implementations MAY use probabilistic data structures (e.g., Bloom filters) as a RECOMMENDED alternative. Probabilistic structures may produce false-positive rejections but MUST NOT produce false negatives (missed replays). Deployments using probabilistic replay detection MUST document the expected false-positive rate in their operational assessment.

Note on retries: IPC is single-use per enforcement point within the replay cache window. If a request times out and the client retries with the same IPC, the enforcement point will reject the retry as token_replayed. Retry scenarios require fresh IPC creation at the platform boundary. Deployments using automatic retry policies (HTTP retry-after, gRPC retry, Kafka consumer rebalancing) MUST ensure that retried requests obtain fresh IPCs from the IPC Creator, or MUST configure intermediate trust boundaries to skip replay detection (acceptable when they only re-sign without making authorization decisions).

8.2. Key Compromise

If a signing key is compromised: 1. Rotate the key immediately. 2. Publish revocation events for all IPCs signed by the compromised key. 3. Short TTL bounds the exposure window naturally.

8.3. Spoofing

An unsigned IPC (Trust Level 1) MUST NOT be used for enforcement decisions. Only signed (Level 2+) IPCs may be used for authorization.

8.4. Chain Forgery

Because the IPC payload is immutable (only signature changes at re-signing), an attacker cannot inject entries into the delegation chain without invalidating the signature.

8.5. Single-Signer Verification Model

IPC uses a "trust your immediate upstream" model. At any given hop, only the most recent signer's signature is present - previous signatures are replaced during re-signing. This means:

  • Service C can verify that Service B signed the IPC.
  • Service C CANNOT cryptographically verify that Service A signed it before B.
  • Service C trusts that B performed proper verification before re-signing.

This is a deliberate design choice that enables local verification (each service only needs its upstream's public key, not a full chain of keys). The tradeoff is that compromise of a single intermediate service allows it to forge or modify IPCs for all downstream services.

Mitigations: - Short TTLs limit the window of exploitation. - Revocation events enable rapid response to compromised intermediaries. - Audit logs at each trust boundary provide forensic traceability even though the cryptographic proof is single-hop.

Important: IPC provides per-hop re-signing with transitive trust, not end-to-end cryptographic proof of the full chain. Each trust boundary verifies the upstream signature before re-signing, attesting (implicitly, via its own signature) that it performed this verification. This is analogous to TLS certificate chain validation - each hop trusts its immediate upstream. A compromised intermediate can fabricate or alter the chain for downstream services. IPC's security model relies on all trust boundaries being platform-operated and within a single administrative domain. Cross-domain propagation where intermediate trust boundaries are not fully trusted is explicitly out of scope (Section 1.6).

In fan-out scenarios (Section 2.2), an intermediate service acts as both Trust Boundary and IPC Creator. Downstream services cannot distinguish a legitimately fan-out-created IPC from one fabricated by a compromised intermediate. This is an inherent limitation of the single-trust-domain model: all IPC Creators and Trust Boundaries are assumed to be platform-operated and trustworthy. Deployments SHOULD monitor fan-out services with heightened scrutiny and SHOULD log all IPC creation events for forensic analysis.

8.6. Size Considerations

IPC size grows linearly with delegation chain depth due to the delegationChain array. Deployments MUST consider transport-level limits:

  • HTTP headers: Most proxies and servers limit individual header values to 8,192 bytes. A delegation chain of depth 10 with 64-character entity identifiers produces an IPC of approximately 1,200 bytes (Base64url-encoded), well within limits.
  • gRPC metadata: Default maximum metadata size is 8 KB per entry.
  • Kafka record headers: No protocol-level limit, but implementations commonly limit total header size to 64 KB.

The base64url-encoded IPC (as transmitted in protocol bindings) MUST NOT exceed 4096 bytes. Verifiers MUST reject encoded IPCs exceeding this limit before JSON parsing, to prevent resource exhaustion via oversized tokens. This limit accommodates delegation chains of depth 10 with 128-character identifiers and reasonable extension claims while remaining well within HTTP header size constraints.

The configured maxDepth serves as the primary mitigation for IPC size growth. Deployments SHOULD set maxDepth to the minimum value that accommodates legitimate delegation patterns (RECOMMENDED: <= 5 for most deployments).

8.7. Threat Model

IPC identifies the following threat categories:

Table 6
ID Threat Mitigation
T1 Token Replay Short TTL (<= 5 min), sessionId uniqueness, TLS-only transport
T2 Key Compromise at Trust Boundary Key rotation (Section 8.8), short TTL limits exposure, revocation events
T3 Delegation Chain Injection Signature covers all claims; payload immutable during re-sign
T4 Circular Delegation Explicit duplicate detection in delegationChain (Section 5.4)
T5 Depth Escalation Configurable maxDepth enforcement at every trust boundary
T6 Revocation Delay Dual-layer: event-streaming (sub-5s target) + short TTL hard bound
T7 Cross-Service Replay Audience claim binding; REQUIRED for Trust Level 2+
T8 Intermediate Service Compromise Single-signer model tradeoff (Section 8.5); mitigated by short TTL + revocation
T9 IPC Stripping Fail-closed enforcement (Section 3.9); requests without IPC are rejected
T10 Downgrade to Trust Level 1 Enforcement points MUST reject unsigned IPCs (Section 8.10)
T11 Clock Skew Exploitation Clock synchronization requirements (Section 8.9)
T12 Denial of Service via Large Chains Token size limits (Section 8.6); maxDepth enforcement
T13 Confused Deputy at Trust Boundary Trust boundaries MUST verify that the presenting service identity (mTLS cert, service account) matches the expected upstream for the IPC's issuer. Reject if mismatch.
T14 Compromised IPC Creator IPC Creator requires highest security posture: HSM-protected signing keys, restricted access, audit logging of all creation events, maxTokenAge provides deployment-wide hard cap
T15 Token theft from memory/logs Short TTLs limit replay window; TLS prevents network interception; deployments SHOULD sanitize IPC values from application logs; enforcement points SHOULD NOT log full IPC header values
T16 Colluding IPC Creator and Intermediate A compromised Creator can fabricate chains; a colluding intermediate can re-sign without verifying. Combined, they can produce back-dated IPCs with arbitrary chains that pass downstream verification. Mitigation: IPC Creators and Trust Boundaries are assumed platform-operated (Section 1.6); HSM-protected keys at Creators (T14); maxTokenAge caps temporal exploitation; audit logging (Section 3.8) provides forensic detection. This threat is inherent to the single-trust-domain model.
T17 Time-of-Check-to-Time-of-Use (TOCTOU) Between VERIFY-IPC returning ACCEPT and application code using the claims for authorization, semantic state may change (e.g., entity revoked, permissions altered). Mitigation: short TTLs (≤ 5 min) bound the window; applications performing asynchronous or deferred processing SHOULD re-verify temporal validity at point-of-use; revocation checking (Step 13) provides near-real-time state when infrastructure is deployed.
T18 Replay Cache Race Condition In horizontally-scaled enforcement points, two requests with the same sessionId may arrive at different instances simultaneously, both passing the replay cache check before either writes. Mitigation: the race window is bounded by cache write propagation delay (typically < 10ms for Redis); audience binding prevents cross-service exploitation; shared cache with atomic check-and-set operations (e.g., Redis SETNX) eliminates the window entirely. Deployments SHOULD use atomic cache operations for replay detection.
T19 Compromised Key Discovery Endpoint An attacker who can serve forged key material at /.well-known/ipc-keys can inject arbitrary public keys, enabling full IPC forgery for any claimed issuer. This is distinct from T2 (key compromise) because no legitimate key is stolen — arbitrary keys are injected. Mitigations: serve key discovery endpoints over mTLS with client certificate validation restricting access to known platform services; implement key pinning where verifiers reject new keys that differ from previously-seen keys for the same issuer unless a rotation event is expected; monitor for unexpected key changes via audit logging; key discovery endpoints MUST be served over HTTPS only; deployments SHOULD restrict access to the platform's internal network.
T20 Cross-Cluster Replay In deployments where a logical service (same audience value) spans multiple clusters with independent replay caches, an attacker can replay the same IPC at both clusters within the TTL window. The audience binding provides no protection because the replayed IPC targets the same audience. Mitigations: use a shared cross-cluster replay cache (e.g., globally-replicated Redis); reduce IPC TTL to minimize the exploitation window; deploy cluster-scoped audience identifiers (e.g., backend.cluster-a.example.com) when strict replay prevention is required across clusters. Deployments that cannot share replay state across clusters MUST document this as an accepted risk with the exposure window bounded by maxTTL.
T21 Compromised Service Mesh Sidecar In sidecar deployments (Appendix D), the sidecar proxy holds IPC signing keys and shares the pod network namespace with the application container. A compromised sidecar (e.g., via container escape, supply chain attack on sidecar image, or control plane compromise) can forge or modify IPCs for all requests transiting that pod. This is a specialization of T8 (intermediate compromise) with a distinct attack surface: sidecars are often pulled from third-party registries, key material is injected via mesh control plane, and the shared network namespace enables request interception without network-level detection. Mitigations: verify sidecar image signatures and provenance; restrict signing key access to the sidecar process only (not the application container); monitor for unexpected IPC creation patterns from sidecar instances; use pod security policies to prevent container escapes. Note that HSM-protected signing keys (T14 mitigation) are generally impractical for sidecar deployments due to scale and ephemeral pod lifecycle.

8.8. Key Rotation Procedure

When a trust boundary service rotates its signing key:

  1. Generate a new Ed25519 keypair.
  2. Publish the new public key at the key discovery endpoint (Section 3.6) with status: "active" and validFrom set to the current time. The old key remains status: "active" during the transition.
  3. Wait at least cacheMaxAge (from the key discovery response, or the deployment's configured keyDiscoveryCacheTTL) to ensure all downstream verifiers have fetched the new key.
  4. Begin signing new IPCs with the new key. The old key is no longer used for signing but remains accepted for verification.
  5. Update the old key's status to "retired" and set validUntil to the current time.
  6. Downstream services MUST continue accepting IPCs signed by the retired key until validUntil plus the maximum configured IPC TTL (maxTTL) plus clockSkew has elapsed.

During the rotation window (up to the cache TTL), downstream services MAY receive IPCs signed by either the old or new key. Services MUST accept both until the old key's validUntil plus the maximum configured IPC TTL (Section 5.6 maxTTL) has elapsed.

Key rotation MUST NOT require coordination with upstream or downstream services beyond the key discovery endpoint. This enables rolling restarts and zero-downtime key rotation.

If a key is suspected compromised:

  1. Set the key status to "compromised" at the discovery endpoint.
  2. Publish a revocation event with reason: "rotation" for any IPCs that may have been signed by the compromised key.
  3. Generate and publish a new key immediately.
  4. Downstream services MUST reject IPCs signed by a compromised key regardless of their expiry time.

8.9. Clock Synchronization

IPC's temporal validity checks (issuedAt, expiresAt) require synchronized clocks across trust boundary services. The following requirements apply:

  • All services participating in IPC verification MUST synchronize their clocks using NTP [RFC 5905] or an equivalent time synchronization protocol.
  • Clock skew between any two adjacent trust boundary services SHOULD NOT exceed the configured clock skew tolerance.
  • Verification implementations MUST apply a configurable clock skew tolerance (RECOMMENDED default: 30 seconds) when checking issuedAt and expiresAt. Deployments with tighter synchronization guarantees MAY use a lower tolerance.
  • If a deployment cannot guarantee clock synchronization within the configured tolerance, it MUST increase the tolerance accordingly and MUST document the increased tolerance as a security risk.
  • Services MUST NOT issue IPCs with issuedAt derived from an unsynchronized clock. If NTP synchronization is lost, the service SHOULD reject incoming requests rather than issuing IPCs with unreliable timestamps.

Note: An IPC verified as valid at one trust boundary may expire before reaching the next service (in-flight expiry). The clock skew tolerance (RECOMMENDED default: 30 seconds) is designed to accommodate this scenario for normal request propagation latency. Deployments with unusually high inter-service latency (> 10 seconds) SHOULD increase their clock skew tolerance or reduce their IPC TTL to compensate.

8.10. Downgrade Attack Prevention

An attacker may attempt to downgrade IPC security by:

  1. Stripping the IPC header entirely: Mitigated by fail-closed enforcement (Section 3.9). Enforcement points MUST reject requests without IPC in production.

  2. Replacing a signed IPC with an unsigned one (Trust Level 1): Enforcement points MUST reject Trust Level 1 IPCs for authorization decisions (Section 8.3). A service configured for Trust Level 2 enforcement MUST NOT accept Trust Level 1 IPCs.

  3. Replaying an expired IPC from a weaker configuration: Temporal validity checks reject expired IPCs regardless of other claims.

  4. Presenting an IPC signed by an unknown issuer: Services MUST verify the IPC was signed by a known upstream issuer. Unknown issuers MUST be rejected with signature_invalid.

  5. Forcing fallback to permissive mode: Permissive mode (Section 3.8) MUST be a temporary deployment configuration. Implementations SHOULD support a deployment-wide flag that disables permissive mode entirely and SHOULD alert operators if permissive mode remains enabled beyond a configured duration (RECOMMENDED: 7 days maximum).

8.11. Denial of Service Considerations

IPC introduces the following DoS attack surfaces:

  • Verification cost: Ed25519 signature verification is computationally inexpensive (~50 microseconds). An attacker sending invalid IPCs imposes minimal CPU cost per request. Rate limiting at the transport layer (e.g., connection limits) is the primary mitigation.

  • Key discovery amplification: An attacker presenting IPCs with many distinct issuer values could trigger key discovery endpoint lookups. Mitigations: cache resolved keys (max 300s), reject unknown issuers immediately, rate-limit key discovery requests.

  • Revocation event flooding: An attacker with revocation authority could flood enforcement points with revocation events. Mitigations: authenticate revocation event sources (signature verification), deduplicate by eventId, rate-limit revocation submissions.

  • Large delegation chains: Token size grows with chain depth. Mitigations: maxDepth enforcement (RECOMMENDED <= 5), transport-layer header size limits provide a natural cap.

8.12. Proof-of-Possession

IPC does not include a proof-of-possession mechanism binding the IPC to the presenting entity. Possession of a valid IPC header value is sufficient to present it. This is a deliberate design choice that enables transparent propagation through middleware and sidecars without requiring per-hop proof construction.

This is mitigated by: 1. Short TTLs limiting the replay window 2. The audience claim preventing cross-service use 3. The replay cache preventing same-service reuse 4. TLS preventing passive interception 5. The coexisting bearer token providing PoP when DPoP or mTLS is used at the transport layer

Deployments requiring stronger binding between IPC and the presenting entity SHOULD use mTLS at the transport layer, which cryptographically binds each request to the presenting service's certificate.

8.13. Bearer Token and IPC Binding

IPC travels alongside the OAuth 2.0 bearer token (Section 1.7) but is not cryptographically bound to it. An attacker who obtains both a valid bearer token for Entity A and a valid IPC for Entity B could present them together, creating a mismatch between transport-level authentication and identity-chain claims.

Enforcement points MUST verify that the authenticated identity established by the bearer token (e.g., the token's sub, azp, or client_id claim) is consistent with the IPC's issuer field or identifies a known trust boundary in the deployment's topology. If the bearer token identifies an entity that is not a legitimate upstream issuer for the presented IPC, the enforcement point MUST reject the request. The RECOMMENDED binding verification procedure is:

  1. Extract the azp (authorized party) claim from the bearer token. If azp is absent, fall back to client_id.
  2. Compare the extracted value against the IPC's issuer field.
  3. If the values match, binding is confirmed.
  4. If the values do not match, check whether the bearer token's subject identifies a known trust boundary service in the deployment's topology (e.g., via a configured allowlist of trust boundary identifiers).
  5. If neither check succeeds, REJECT the request.

Deployments that use a different bearer token claim for binding verification (e.g., a custom service_id claim) MUST document the specific claim used and the comparison semantics in their deployment's security architecture. Two implementations within the same deployment MUST use the same binding claim to ensure consistent enforcement.

When the enforcement point cannot determine consistency (e.g., bearer token lacks identifying claims, or the mapping between bearer token subjects and IPC issuers is unavailable), it SHOULD log a warning and MAY proceed based on deployment policy. Deployments operating in this degraded mode MUST document the accepted risk.

In multi-hop chains, each intermediate service presents its own bearer token (or mTLS identity) when calling the next service downstream. The binding verification at each hop compares the immediate caller's transport-level identity against the IPC's current issuer field (which was set by the re-signing service at the previous hop). This means binding verification is always between the direct caller and the IPC's issuer — not between the original client and the IPC creator.

For opaque bearer tokens (where claims are not directly available), enforcement points MUST use token introspection (RFC 7662) to obtain the client_id or equivalent identifier for binding verification, or rely on transport-level identity (mTLS) instead.

Deployments using mTLS or DPoP [RFC 9449] at the transport layer gain stronger implicit binding: the transport-level identity is cryptographically proven and SHOULD be compared against the IPC's issuer to detect token-swapping attacks. When mTLS or DPoP [RFC 9449] is available, enforcement points MUST use the transport-level identity for binding verification rather than relying solely on bearer token claims.

9. Privacy Considerations

IPCs contain identity information (entity IDs, delegation chains) that reveals organizational structure, delegation relationships, and operational patterns. The following privacy requirements apply:

9.1. Transport Protection

IPCs MUST only be transmitted over encrypted channels (TLS). Unencrypted transmission exposes the full delegation chain to passive observers.

9.2. Delegation Chain as Sensitive Metadata

The delegationChain array reveals: - Organizational hierarchy (who delegates to whom) - Service naming conventions (internal service identifiers) - Operational patterns (which agents are used for which tasks) - Delegation depth as a proxy for system complexity

Deployments SHOULD consider using opaque or pseudonymous identifiers rather than human-readable names in delegationChain entries when IPCs may be exposed beyond the immediate trust domain (e.g., captured in shared logging infrastructure, debugging tools, or error responses).

When opaque identifiers are used, a mapping to human-readable names MUST be maintained by the platform operator for audit purposes but SHOULD NOT be distributed to all services in the chain.

9.3. Logging and Retention

Section 3.8 requires normative audit logging of IPC verification events. Deployments MUST establish retention policies for IPC audit logs that balance forensic traceability against data minimization:

  • Audit logs SHOULD retain sessionId, entityId, verification result, and timestamp — not the full IPC payload.
  • Full IPC payloads (including delegationChain) MUST NOT be logged unless required for active security investigation.
  • Log retention periods SHOULD be defined by deployment policy and communicated to affected parties per applicable data protection regulations.

9.4. Data Minimization

Deployments SHOULD minimize the identity information included in IPCs based on the principle of data minimization:

  • Entity identifiers SHOULD NOT contain personally identifiable information (PII) beyond what is necessary for enforcement decisions.
  • The delegationChain carries the minimum information needed for cascade revocation and depth enforcement. Deployments that do not require these features at enforcement points MAY truncate the chain to include only the root and current entity (reducing metadata exposure) while documenting the loss of cascade revocation capability.

9.5. Correlation Risks

The sessionId enables correlation of a single request across multiple services. Deployments MUST NOT use sessionId for cross-request user tracking. The RECOMMENDED use of UUID v7 (which contains a timestamp component) means that sessionId values reveal approximate creation time even without access to the issuedAt claim.

As noted in Section 2.1, using the same value for sessionId and externally-visible trace identifiers (e.g., W3C Trace Context) is NOT RECOMMENDED when trace context propagates beyond the trust domain boundary.

10. IANA Considerations

10.1. Header Registration

Registration request for a new HTTP header field:

  • Header Field Name: Identity-Propagation-Context
  • Applicable Protocol: HTTP
  • Status: Standard
  • Structured Type: N/A (opaque base64url-encoded value; not a Structured Field per RFC 8941)
  • Specification Document: This document, Section 6.1

Note: gRPC metadata keys and Kafka record header keys do not have IANA registries. The metadata key identity-propagation-context (Section 6.2) and Kafka record header key identity-propagation-context (Section 6.3) are defined by this specification and SHOULD be treated as reserved by implementations that support IPC.

10.2. Claims Registration

This specification defines the following claims for use in IPCs. Should a future profile encode IPC as a JWT (RFC 7519), these claims would be registered in the "JSON Web Token Claims" registry. For this version, the claims are defined locally within this specification.

Table 7
Claim Name Description Change Controller Specification
entityId Identity of executing entity IESG This document, Section 2.1
entityType Classification (AUTONOMOUS/SUPERVISED/UTILITY/HUMAN/SYSTEM) IESG This document, Section 2.1
authorizedBy Immediate delegating party IESG This document, Section 2.1
delegationDepth Hops from root IESG This document, Section 2.1
delegationChain Ordered provenance chain IESG This document, Section 2.1
trustLevel Signature assurance level: 1 (unsigned), 2 (signed), 3 (reserved) IESG This document, Section 2.1

10.3. Well-Known URI Registration

Registration request for a new well-known URI suffix per RFC 8615:

  • URI Suffix: ipc-keys
  • Change Controller: IESG
  • Specification Document: This document, Section 3.6
  • Related Information: N/A

10.4. IPC Entity Type Registry

This specification establishes the "IPC Entity Type" registry under the "Identity Propagation Context (IPC)" heading. The registration policy is "Specification Required" per RFC 8126.

Initial registry contents:

Table 8
Value Description Change Controller Reference
HUMAN A natural person authenticated via an identity provider IESG This document, Section 2.1
SYSTEM Platform-initiated operation with no direct human trigger IESG This document, Section 2.1
AUTONOMOUS Agent operating independently on behalf of a human principal IESG This document, Section 2.1
SUPERVISED Agent requiring human approval for sensitive operations IESG This document, Section 2.1
UTILITY Stateless service component invoked by other entities IESG This document, Section 2.1

Registration requirements for new entity types: - Value MUST be an uppercase ASCII string matching ^[A-Z][A-Z0-9_]{1,31}$ - A specification document MUST define the semantics and intended use - New values MUST NOT conflict semantically with existing registered types

11. Relationship to Other Specifications

Table 9
Specification Relationship
RFC 8693 (Token Exchange) Complementary - used at first hop; IPC carries identity through remaining chain
Transaction Tokens [I-D.draft-ietf-oauth-transaction-tokens] Complementary - Transaction Tokens carry immutable authorization context (scope, transaction details) through a call chain unchanged. IPC carries the mutable delegation chain identity with per-hop re-signing. Txn-Tokens answer "what is this transaction authorized to do?"; IPC answers "who initiated this request and through what delegation chain?" Both may coexist in the same request: the Txn-Token in the Txn-Token header and the IPC in the Identity-Propagation-Context header.
RFC 6750 (Bearer Tokens) IPC extends bearer token concepts with delegation chain and re-signing
RFC 7519 (JWT) IPC uses JSON with JCS (RFC 8785) for canonical serialization rather than JWT's JWS structure. JWT claim names are intentionally distinct to avoid semantic collision.
Agent Identity Protocol (AIP) [I-D.draft-singla-agent-identity-protocol] Complementary - AIP requires the agent to actively participate (generate keys, sign tokens); IPC requires nothing from the agent beyond a standard OAuth JWT. AIP is HTTP-only; IPC adds gRPC and event-streaming bindings.
WIMSE AI Agent Identity [I-D.draft-ni-wimse-ai-agent-identity] Complementary - WIMSE defines agent identity bootstrapping and attestation; IPC defines how the resulting verified identity propagates through service chains after credential issuance.
AgentID Protocol [I-D.draft-gudlab-agentid-protocol] Shared principle - both require cryptographic traceability to a human/organization root. IPC adds per-hop re-signing and cross-protocol propagation.
W3C Baggage Complementary - Baggage for observability, IPC for enforcement
RFC 9449 (DPoP) Complementary - DPoP provides proof-of-possession for OAuth tokens at the transport layer; IPC provides identity propagation through the service chain. When both are deployed, DPoP strengthens IPC-to-bearer-token binding (Section 8.13).
SPIFFE/SPIRE Complementary - SPIFFE identifies workloads (pod-to-pod), IPC identifies the initiating entity and its delegation chain through those workloads
AI Agent Authentication Framework [I-D.draft-klrc-aiagent-auth] Complementary - klrc composes SPIFFE (identity), WIMSE (credential), and OAuth (authorization) into a layered agent auth stack, using RFC 8693 token exchange for downstream delegation. IPC addresses klrc's "Delegation (downstream)" layer without per-hop STS interaction: after the agent authenticates and obtains delegated authority per klrc's model, IPC carries the verified delegation chain through multi-hop internal service calls. klrc's act claim is informational; IPC makes it enforceable with per-hop re-signing.
Google Cloud Agent Identity Complementary (informative) - Google assigns per-agent SPIFFE identities with mTLS + DPoP credential binding, but does not specify delegation chain propagation through internal service hops. IPC fills this gap: after the agent authenticates with its SPIFFE credential at the platform boundary, IPC carries the full delegation chain through downstream services.

11.1. Detailed Comparison with AIP

The Agent Identity Protocol (AIP) is the most comprehensive existing specification for AI agent identity. IPC and AIP operate at different architectural layers:

Table 10
Aspect AIP IPC
Purpose Agent proves its identity to a relying party Platform carries verified identity through its internal chain
Who creates the artifact? Agent self-issues Credential Tokens Platform trust boundary creates IPC
Agent involvement Active - agent implements the protocol None - agent is not aware of IPC
Re-signing at hops Not supported - agent-signed once Re-signed at every trust boundary
Protocol support HTTP only (Bearer/DPoP) HTTP, gRPC, Kafka
Verification model Registry lookup Locally-verifiable - no per-request STS calls
Registry dependency Required for Tier 2+ No registry needed

Both support delegation chains, depth limits, expiry attenuation, cascade revocation, and circular detection - confirming shared problem understanding with different architectural solutions.

When to use which:

  • Use AIP when the agent directly presents credentials to a relying party and the relying party can verify against a registry.
  • Use IPC when identity must propagate transparently through platform infrastructure services that the agent does not directly interact with, especially across protocol boundaries (HTTP -> gRPC -> Kafka).
  • Use both when the agent authenticates at the platform boundary using AIP, and the platform then propagates identity internally using IPC.

11.2. Mapping to Authorization Propagation Requirements

Tallam [Tallam2026] formalizes "authorization propagation" as a workflow-level property of multi-agent systems and derives seven structural requirements (R1-R7) for any sufficient authorization architecture. IPC addresses these requirements as follows:

Table 11
Requirement Description IPC Coverage
R1 Agent principals as first-class authorization subjects [Full] entityId + entityType - agents are named, typed principals
R2 Delegation explicit, bounded, auditable [Full] delegationChain + authorizedBy + re-signing proves each hop validated
R3 Authorization at every data retrieval boundary [Full] Re-signing at every trust boundary; each service independently verifies
R4 Aggregation policies expressible and enforceable [Partial] IPC provides identity trace; aggregation logic is delegated to the policy engine
R5 Authorization traces workflow-scoped and self-contained [Full] IPC is a single, self-contained artifact carrying the full delegation chain, inspectable at any hop
R6 Temporal validity as policy decision [Full] Configurable expiresAt + dual-layer revocation (event-streaming + expiry)
R7 Recovery traceable through synthesis graph [Full] sessionId + delegationChain enable identification of all downstream results derived from a specific authorization

IPC does not claim to solve aggregation inference (R4) in the general case - this is acknowledged as an open research problem. However, IPC provides the prerequisite infrastructure: a self-contained, per-hop verified identity trace that makes the set of contributing sources inspectable after the fact, which is necessary for any aggregation policy enforcement.

11.3. Conformance Classes

This specification defines three conformance classes:

IPC Creator (e.g., API Gateway): - MUST create IPCs conforming to the format in Section 2 - MUST sign with EdDSA (Ed25519) for multi-hop deployments (Section 2.6) - MUST enforce expiry attenuation for sub-delegated IPCs (Section 5.3) - MUST set audience for Trust Level 2 IPCs (Section 2.1) - MUST populate all REQUIRED claims - MUST publish its own public key via the key discovery endpoint (Section 3.6) - MUST support key discovery as a key publisher; SHOULD support key discovery as a key consumer if it also verifies upstream IPCs

Trust Boundary (e.g., intermediate service that re-signs): - MUST execute the verification algorithm (Section 3.2) before re-signing - MUST execute the re-signing algorithm (Section 3.3) - MUST NOT modify any claims other than issuer, kid, and signature - MUST reject IPCs that fail any verification step - MUST maintain upstream public keys (via Section 3.6 or static config) - MUST publish its own public key via the key discovery endpoint (Section 3.6) - MUST support key discovery as both a key consumer (querying upstream endpoints) and a key publisher (exposing own keys)

Enforcement Point (e.g., terminal service): - MUST execute the verification algorithm (Section 3.2) - MUST enforce all applicable rules from Section 5 - MUST check revocation state if revocation infrastructure is deployed - MUST maintain a replay cache for sessionId (Section 8.1) - MUST reject Trust Level 1 IPCs for authorization decisions - MAY skip re-signing if it is the terminal service in the chain

An implementation MAY fulfill multiple conformance classes simultaneously (e.g., a service that both re-signs and makes enforcement decisions).

Appendix A. Acknowledgements

The authors thank the reviewers who provided feedback on early drafts of this specification.

Appendix B. References

B.1. Normative References

  • [RFC 2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", March 1997.
  • [RFC 4648] Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", October 2006.
  • [RFC 5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", STD 68, January 2008.
  • [RFC 5905] Mills, D., et al., "Network Time Protocol Version 4: Protocol and Algorithms Specification", June 2010.
  • [RFC 6648] Saint-Andre, P., et al., "Deprecating the 'X-' Prefix and Similar Constructs in Application Protocols", June 2012.
  • [RFC 6750] Jones, M., et al., "OAuth 2.0 Bearer Token Usage", October 2012.
  • [RFC 7519] Jones, M., et al., "JSON Web Token (JWT)", May 2015.
  • [RFC 7518] Jones, M., "JSON Web Algorithms (JWA)", May 2015.
  • [RFC 8037] Liusvaara, I., "CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in JSON Object Signing and Encryption (JOSE)", January 2017.
  • [RFC 8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", May 2017.
  • [RFC 8259] Bray, T., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, December 2017.
  • [RFC 8615] Nottingham, M., "Well-Known Uniform Resource Identifiers (URIs)", May 2019.
  • [RFC 8693] Jones, M., et al., "OAuth 2.0 Token Exchange", January 2020.
  • [RFC 8785] Rundgren, A., et al., "JSON Canonicalization Scheme (JCS)", June 2020.
  • [RFC 9110] Fielding, R., et al., "HTTP Semantics", June 2022.

B.2. Informative References

  • [RFC 7515] Jones, M., et al., "JSON Web Signature (JWS)", May 2015.
  • [RFC 7662] Richer, J., "OAuth 2.0 Token Introspection", October 2015.
  • [RFC 6749] Hardt, D., "The OAuth 2.0 Authorization Framework", October 2012.
  • [RFC 8126] Cotton, M., et al., "Guidelines for Writing an IANA Considerations Section in RFCs", June 2017.
  • [RFC 9449] Fett, D., et al., "OAuth 2.0 Demonstrating Proof of Possession (DPoP)", September 2023.
  • [RFC 8610] Birkholz, H., et al., "Concise Data Definition Language (CDDL): A Notational Convention to Express Concise Binary Object Representation (CBOR) and JSON Data Structures", June 2019.
  • [RFC 9562] Davis, K., et al., "Universally Unique IDentifiers (UUIDs)", May 2024.
  • [RFC 8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital Signature Algorithm (EdDSA)", January 2017.
  • [RFC 8941] Nottingham, M. and P-H. Kamp, "Structured Field Values for HTTP", February 2021.
  • [I-D.draft-singla-agent-identity-protocol] Singla, P., "Agent Identity Protocol (AIP): Decentralized Identity and Delegation for AI Agents", Work in Progress, Internet-Draft, draft-singla-agent-identity-protocol-03, June 2026.
  • [I-D.draft-ni-wimse-ai-agent-identity] Ni, Y. and C. P. Liu, "WIMSE Applicability for AI Agents", Work in Progress, Internet-Draft, draft-ni-wimse-ai-agent-identity-02, February 2026.
  • [I-D.draft-gudlab-agentid-protocol] "AgentID Protocol", Work in Progress, Internet-Draft.
  • [I-D.draft-klrc-aiagent-auth] Kataria, L., et al., "AI Agent Authentication and Authorization", Work in Progress, Internet-Draft, draft-klrc-aiagent-auth-00, March 2026. Note: Informational draft (Ping Identity, AWS, Zscaler, Defakto) composing SPIFFE, WIMSE, OAuth, and OIDC for agent authentication. Thesis: existing standards are sufficient for agent auth; no new protocol needed.
  • [I-D.draft-ietf-oauth-transaction-tokens] Tulshibagwale, A., Fletcher, G., and P. Kasselman, "Transaction Tokens", Work in Progress, Internet-Draft, draft-ietf-oauth-transaction-tokens-09, July 2026. Note: OAuth WG adopted draft (In WG Last Call) defining short-lived, transaction-bound JWTs for propagating immutable authorization context through call chains within a Trust Domain.
  • [GCP-Agent-Identity] Google Cloud, "Agent Identity overview", https://cloud.google.com/iam/docs/agent-identity-overview, 2026.
  • [Tallam2026] Tallam, K., "Authorization Propagation in Multi-Agent AI Systems: Identity Governance as Infrastructure", arXiv:2605.05440, May 2026. Note: This is an arXiv preprint that has not undergone formal peer review. It is cited for its problem formalization (R1-R7 requirements), not as a normative authority.
  • [SPIFFE] CNCF, "Secure Production Identity Framework for Everyone"
  • [W3C-Baggage] W3C, "Baggage Specification"

Author's Address

Ravi Kant Sharma
Ericsson
Ireland