How should an instance handle identity bridging between two protocols that use different trust roots?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session needs to bridge its identity between two protocols that use different trust roots (e.g., OIDC issuer A and OIDC issuer B, SPIFFE and OAuth, or on-chain identity and off-chain identity), and the receiving protocol must verify the bridged identity despite not sharing a trust root with the origin. Does not apply to single-protocol sessions or protocols that already share a trust anchor.
Core Distinction
The separation between **origin identity** (the identity established under the source protocol) and **bridged identity** (the presentation of that identity under the target protocol). Relying on origin identity alone creates the **Trust Root Divergence**: the target protocol's verifier does not trust the source protocol's root CA or issuer, so it cannot validate the bridged identity, and the agent is treated as unauthenticated.
What Was Observed
In cross-protocol agent architectures, failing to properly bridge identity produces three primary failure modes:
- **Untrusted Signature:** The agent presents an identity token signed by Protocol A's root, and Protocol B's verifier rejects it because Protocol A is not in B's trust store.
- **Bridging Collapse:** The agent uses a bridge service that re-issues tokens, but the bridge is compromised or misconfigured, and the target protocol accepts fraudulent bridged identities.
- **Attribution Loss:** The bridged identity is accepted but does not preserve the origin identity attributes (roles, scopes), so downstream authorization decisions fail.
Recorded Elements
- **`cross_protocol_bridge_attestation`:** A cryptographic proof, signed by a bridge service trusted by the target protocol, that maps the origin identity to a target identity while preserving its attributes.
- **`origin_root_attestation`:** The cryptographic proof of the origin identity's signature chain (issuer root), allowing the target protocol to verify origin if it chooses to.
- **`bridge_trust_anchor_signature`:** The signature of the bridge from the target protocol's trusted anchor, establishing that the bridge is authorized.
- **`attribute_preservation_receipt`:** The cryptographic proof that the origin's roles, scopes, and other attributes are preserved verbatim in the bridged identity.
Condition of Validity
* **Applies when:** The agent operates across two protocols with different trust roots, and a bridge is available (or can be established) that is trusted by both sides. * **Does not apply when:** Both protocols share a trust root, or the agent's identity does not need to transfer between protocols.
Decision Boundary
- **Is the agent bridging identity between two protocols?** If no, standard identity verification applies. If yes, proceed.
- **Is there a bridge service trusted by the target protocol?** If yes, use it with the `cross_protocol_bridge_attestation`. If no, the target protocol cannot verify the bridged identity. Escalate or fall back to alternative authentication.
- **Does the `attribute_preservation_receipt` confirm origin attributes are preserved?** If yes, downstream authorization can proceed. If no, the bridge is lossy. Reject or re-bridge.
- **Conversely, when can an agent bypass the bridge?** Only when the target protocol accepts origin-root tokens directly (shared trust store) or when the target protocol's verifier can independently validate the origin root (out-of-band trust establishment).
Operational Effect
- **Cross-Protocol Auth:** Enables identity to transfer between protocols without shared roots.
- **Bridge Trust:** Ensures the bridge is authorized by the target protocol.
- **Attribute Preservation:** Prevents lossy bridging that strips authorization attributes.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class BridgeVerdict(Enum):
NATIVE_TRUST = auto()
BRIDGE_VERIFIED = auto()
BRIDGE_UNTRUSTED = auto()
ATTRIBUTES_LOST = auto()
@dataclass(frozen=True)
class BridgeState:
origin_protocol: str
target_protocol: str
bridge_attestation: str | None
bridge_trusted_by_target: bool
attribute_preservation_receipt: str | None
@dataclass(frozen=True)
class BridgePolicy:
require_attribute_preservation: bool
@dataclass(frozen=True)
class BridgeManifest:
verdict: BridgeVerdict
directive: str
manifest_digest: str
class CrossProtocolIdentityGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_bridge(
cls, state: BridgeState, policy: BridgePolicy
) -> BridgeManifest:
# Gate 1: Same protocol
if state.origin_protocol == state.target_protocol:
digest = cls._hash(json.dumps({
"protocol": state.origin_protocol,
"status": "NATIVE"
}, sort_keys=True))
return BridgeManifest(
verdict=BridgeVerdict.NATIVE_TRUST,
directive=f"PROCEED: Same protocol {state.origin_protocol}, no bridging needed.",
manifest_digest=digest
)
# Gate 2: Bridge presence
if not state.bridge_attestation:
return BridgeManifest(
verdict=BridgeVerdict.BRIDGE_UNTRUSTED,
directive=f"HALT: No bridge attestation {state.origin_protocol} -> {state.target_protocol}.",
manifest_digest=cls._hash(f"NO_BRIDGE:{state.origin_protocol}")
)
# Gate 3: Bridge trust
if not state.bridge_trusted_by_target:
return BridgeManifest(
verdict=BridgeVerdict.BRIDGE_UNTRUSTED,
directive=f"HALT: Bridge not trusted by {state.target_protocol}.",
manifest_digest=cls._hash(f"UNTRUSTED:{state.target_protocol}")
)
# Gate 4: Attribute preservation
if policy.require_attribute_preservation and not state.attribute_preservation_receipt:
return BridgeManifest(
verdict=BridgeVerdict.ATTRIBUTES_LOST,
directive=f"HALT: Attribute preservation unverified. Bridged identity may lack roles/scopes.",
manifest_digest=cls._hash(f"ATTR_LOST:{state.origin_protocol}")
)
# Gate 5: Verified
digest = cls._hash(json.dumps({
"origin": state.origin_protocol,
"target": state.target_protocol,
"attestation": state.bridge_attestation,
"attributes": state.attribute_preservation_receipt or "NONE"
}, sort_keys=True))
return BridgeManifest(
verdict=BridgeVerdict.BRIDGE_VERIFIED,
directive=f"PROCEED: Bridged {state.origin_protocol} -> {state.target_protocol}.",
manifest_digest=digest
)