# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-agent-instance-refuse-a-did-bound-agent-passport-presented-by-a-foreign-organization.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-agent-instance-refuse-a-did-bound-agent-passport-presented-by-a-foreign-organization.warrant.json
# Markdown SHA-256:   2c2b9c85e0c180b57fdffdf1c199b135381bab82c337520fa1697c4a735749a9
# Code AST SHA-256:   569f24d56b9e2a688186e110108448a9dbd18a2b048421704a48e3a6fc80dc1b
# Verified Classes:   AgentPassportBindingError, AgentPassportVerifier
# Defensive Guards:   6 fail-closed invariants (zero external deps)

class AgentPassportBindingError(RuntimeError):
    """Raised when a presented agent passport is cryptographically valid
    but not bound to the presenting agent instance."""
    pass


class AgentPassportVerifier:
    def __init__(self, trust_policy, status_list_cache, max_session_ttl_s=300):
        self.trust_policy = trust_policy          # set of trusted foreign issuer DIDs
        self.status_list_cache = status_list_cache
        self.max_session_ttl_s = max_session_ttl_s

    def admit(self, presentation, requested_scope, session_nonce):
        vc = presentation.credential
        subject_did = vc["credentialSubject"]["id"]
        pop = presentation.proof_of_possession

        # 1. Issuer trust: a valid signature by an untrusted DID is not trust.
        if vc["issuer"] not in self.trust_policy:
            raise AgentPassportBindingError(
                f"issuer {vc['issuer']} not in local trust policy"
            )

        # 2. Freshness: PoP must be bound to this session's nonce.
        if pop.get("nonce") != session_nonce:
            raise AgentPassportBindingError(
                "proof-of-possession nonce does not match session challenge"
            )

        # 3. Binding: the PoP key must be the subject DID's key, or reachable
        #    via a verifier-checked delegation chain from the subject DID.
        binding = self._resolve_binding(subject_did, pop["key_id"])
        if binding is None:
            raise AgentPassportBindingError(
                f"PoP key {pop['key_id']} is not bound to subject DID {subject_did}"
            )

        # 4. Instance vs operator: privileged scopes require an instance DID,
        #    not an operator DID that merely aggregates many agents.
        if requested_scope in PRIVILEGED_SCOPES and binding.kind != "instance":
            raise AgentPassportBindingError(
                f"scope {requested_scope} requires instance-level binding; "
                f"got {binding.kind}-level binding for {subject_did}"
            )

        # 5. Liveness: status must be current, and session lifetime capped so
        #    mid-session revocation is observed.
        if not self.status_list_cache.is_current(vc["credentialStatus"]):
            raise AgentPassportBindingError(
                f"status list for {subject_did} is stale; cannot admit"
            )
        if self.status_list_cache.is_revoked(vc["credentialStatus"]):
            raise AgentPassportBindingError(
                f"passport for {subject_did} revoked"
            )

        return Admission(
            subject_did=subject_did,
            scope=requested_scope,
            recheck_after_s=min(self.max_session_ttl_s,
                                self.status_list_cache.ttl_s),
        )

    def _resolve_binding(self, subject_did, pop_key_id):
        # Returns an instance/operator binding if pop_key_id is the subject's
        # authentication key or is chained to it by signed delegations.
        ...
        return None
