How must an instance handle self-delegation when a principal delegates to its own identities?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session receives a delegation from a principal that resolves to the same underlying principal as the delegatee (self-delegation via identity fragmentation, subsidiary delegation, or role-based identity), and the session must determine whether the delegation is meaningful, additive, or a no-op. Does not apply to strictly distinct-principal delegations or cases with no identity resolution.
Core Distinction
The separation between **delegation event** (the delegation occurred) and **delegation effect** (the delegatee's authority actually increased). Relying on the event creates the **Self-Delegation Null Effect**: the principal delegates to itself under a different identity, the delegation appears valid, but the delegatee's effective authority is unchanged—creating the appearance of authorization without actual trust expansion.
What Was Observed
In multi-identity agent architectures, failing to handle self-delegation produces three primary failure modes:
- **Circular Authority Claims:** A principal delegates authority to identity B, which delegates back to identity A, which delegates to B, forming a circular chain that appears to grant escalated authority but actually grants nothing new.
- **Audit Trail Pollution:** Self-delegations clutter the audit trail with events that appear to grant authority but do not, confusing auditors who cannot quickly determine the actual effective authority.
- **Privilege Escalation Disguise:** A malicious agent uses self-delegation to claim escalated authority, arguing that "the principal delegated," when actually the principal merely re-assigned to itself.
Recorded Elements
- **`principal_resolution_receipt`:** For each delegation, the cryptographic proof of the underlying principal behind both delegator and delegatee, allowing the receiver to detect self-delegation.
- **`effective_authority_delta_receipt`:** For each delegation, the exact delta in effective authority (what the delegatee can do that the delegator could not before the delegation), distinguishing meaningful from null delegations.
- **`self_delegation_classification_signature`:** For detected self-delegations, the classification (`NO_OP`, `ROLE_CHANGE`, `AUTHORITY_REDUCTION`, `AUTHORITY_EXPANSION`), documenting the delegation's actual effect.
- **`circular_delegation_detection_receipt`:** For delegation chains, the cryptographic proof of any cycles (A→B→A), flagging circular self-delegations.
Condition of Validity
* **Applies when:** Principals can have multiple identities, delegations between them are possible, and downstream consumers rely on delegation semantics. * **Does not apply when:** Principals have strictly one identity each, or delegations are always to distinct principals.
Decision Boundary
- **Is the delegation from a principal to itself (under any identity)?** If no, standard delegation applies. If yes, proceed.
- **Does the delegation grant authority the principal did not have in the delegator identity?** If yes, this is a role change, not a self-delegation. If no, this is a null delegation.
- **Does the delegation chain contain cycles?** If yes, the delegation is circular and grants no net authority. Flag accordingly.
- **Conversely, when can a self-delegation be treated as meaningful?** Only when the delegation adds role-based authority (e.g., principal's personal identity delegating to their service role identity, where the role has distinct permissions), AND the delta is documented in `effective_authority_delta_receipt`.
Operational Effect
- **Circular Authority Prevention:** Cycles are detected and flagged.
- **Audit Clarity:** Null delegations are classified separately, keeping the audit trail clean.
- **Privilege Disguise Prevention:** Self-delegation cannot be used to claim authority that was not granted.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class SelfDelegationVerdict(Enum):
DISTINCT_PRINCIPALS = auto()
MEANINGFUL_ROLE_DELEGATION = auto()
NULL_SELF_DELEGATION = auto()
CIRCULAR_DELEGATION = auto()
@dataclass(frozen=True)
class DelegationEvent:
delegator_identity: str
delegatee_identity: str
delegator_principal: str
delegatee_principal: str
authority_delta: frozenset[str]
chain_depth: int
seen_in_chain: frozenset[str]
@dataclass(frozen=True)
class SelfDelegationPolicy:
allow_role_delegation: bool
@dataclass(frozen=True)
class SelfDelegationManifest:
verdict: SelfDelegationVerdict
directive: str
manifest_digest: str
class SelfDelegationGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_delegation(
cls, event: DelegationEvent, policy: SelfDelegationPolicy
) -> SelfDelegationManifest:
# Gate 1: Distinct principals
if event.delegator_principal != event.delegatee_principal:
digest = cls._hash(json.dumps({
"delegator": event.delegator_identity,
"delegatee": event.delegatee_identity,
"delegator_principal": event.delegator_principal,
"delegatee_principal": event.delegatee_principal
}, sort_keys=True))
return SelfDelegationManifest(
verdict=SelfDelegationVerdict.DISTINCT_PRINCIPALS,
directive=f"PROCEED: Distinct principals, delegation valid.",
manifest_digest=digest
)
# Gate 2: Circular delegation
if event.delegator_identity in event.seen_in_chain and event.delegatee_identity in event.seen_in_chain:
return SelfDelegationManifest(
verdict=SelfDelegationVerdict.CIRCULAR_DELEGATION,
directive=f"HALT: Circular delegation between {event.delegator_identity} and {event.delegatee_identity}.",
manifest_digest=cls._hash(f"CIRCULAR:{event.delegator_identity}")
)
# Gate 3: Role delegation
if policy.allow_role_delegation and event.authority_delta:
digest = cls._hash(json.dumps({
"delegator": event.delegator_identity,
"delegatee": event.delegatee_identity,
"principal": event.delegator_principal,
"delta": tuple(sorted(event.authority_delta))
}, sort_keys=True))
return SelfDelegationManifest(
verdict=SelfDelegationVerdict.MEANINGFUL_ROLE_DELEGATION,
directive=f"PROCEED: Same principal, role delegation adds {sorted(event.authority_delta)}.",
manifest_digest=digest
)
# Gate 4: Null delegation
return SelfDelegationManifest(
verdict=SelfDelegationVerdict.NULL_SELF_DELEGATION,
directive=f"PROCEED_NO_OP: Self-delegation by {event.delegator_principal} adds no authority.",
manifest_digest=cls._hash(f"NULL:{event.delegator_identity}:{event.delegatee_identity}")
)