# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-agent-sandbox-reject-a-capability-delegation-that-is-transitively-valid-but-crosses-a-syscall-policy-epoch.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-agent-sandbox-reject-a-capability-delegation-that-is-transitively-valid-but-crosses-a-syscall-policy-epoch.warrant.json
# Markdown SHA-256:   d8b33ecfc05280c525a1e7ae853622980f1c04abecab07c91a5730ce9b39b54e
# Code AST SHA-256:   e3841c94d2dbd38f11b9be9662faf801a85b9001bca0b944d6116aa07092fb24
# Verified Classes:   PolicyEpochMismatch, Capability, PolicyEpoch
# Defensive Guards:   3 fail-closed invariants (zero external deps)

import hmac
import hashlib
from dataclasses import dataclass


class PolicyEpochMismatch(RuntimeError):
    """Raised when a delegated capability's mint epoch does not match the
    receiving task's bound policy epoch, or when the capability class is not
    permitted under that epoch. Must be raised at delegation time, never
    deferred to syscall execution."""


@dataclass(frozen=True)
class Capability:
    cap_id: str
    cap_class: str          # e.g. "net:connect", "fs:write"
    epoch_id: int           # policy epoch under which this token was minted
    mac: bytes              # HMAC over (cap_id, cap_class, epoch_id)


@dataclass(frozen=True)
class PolicyEpoch:
    epoch_id: int
    allowed_classes: frozenset


def _mac(secret: bytes, cap_id: str, cap_class: str, epoch_id: int) -> bytes:
    msg = f"{cap_id}|{cap_class}|{epoch_id}".encode()
    return hmac.new(secret, msg, hashlib.sha256).digest()


def mint(secret: bytes, cap_id: str, cap_class: str, epoch_id: int) -> Capability:
    return Capability(cap_id, cap_class, epoch_id, _mac(secret, cap_id, cap_class, epoch_id))


def accept_delegation(
    secret: bytes,
    token: Capability,
    receiver_epoch: PolicyEpoch,
) -> Capability:
    """Validate a delegated capability against the receiver's bound policy epoch.

    Returns the token unchanged iff it is authentic AND minted under the
    receiver's epoch AND its class is permitted there. Otherwise raises
    PolicyEpochMismatch so the orchestrator re-mints under receiver_epoch.
    """
    expected = _mac(secret, token.cap_id, token.cap_class, token.epoch_id)
    if not hmac.compare_digest(expected, token.mac):
        # Forgery is a different failure; do not conflate with epoch skew.
        raise PolicyEpochMismatch(f"capability {token.cap_id} failed MAC verification")

    if token.epoch_id != receiver_epoch.epoch_id:
        raise PolicyEpochMismatch(
            f"capability {token.cap_id} minted under epoch {token.epoch_id}, "
            f"receiver bound to epoch {receiver_epoch.epoch_id}; "
            f"re-mint required before delegation"
        )

    if token.cap_class not in receiver_epoch.allowed_classes:
        raise PolicyEpochMismatch(
            f"capability class {token.cap_class} not permitted under "
            f"epoch {receiver_epoch.epoch_id}"
        )

    return token


# Example: rolling narrowing of policy must not be bypassed by delegation.
if __name__ == "__main__":
    secret = b"orchestrator-shared-secret"
    e41 = PolicyEpoch(41, frozenset({"net:connect", "fs:write"}))
    e42 = PolicyEpoch(42, frozenset({"fs:write"}))  # net:connect narrowed away

    parent_token = mint(secret, "cap-7", "net:connect", e41.epoch_id)

    # Parent (E41) delegates to child spawned under E42:
    try:
        accept_delegation(secret, parent_token, e42)
    except PolicyEpochMismatch as exc:
        print(f"delegation rejected at boundary: {exc}")
        # Orchestrator re-mints under the receiver's epoch; the narrowed
        # policy of E42 now correctly applies to the child.
        try:
            accept_delegation(secret, mint(secret, "cap-7", "net:connect", e42.epoch_id), e42)
        except PolicyEpochMismatch as exc2:
            print(f"re-mint also denied (class narrowed out): {exc2}")
