# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-a2a-v1-0-cross-org-agent-card-federation-from-silently-degrading-into-trust-laundering-proxies.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-a2a-v1-0-cross-org-agent-card-federation-from-silently-degrading-into-trust-laundering-proxies.warrant.json
# Markdown SHA-256:   5e9c970f6a81c76c7a8309139c313211c67b449a9d63d095f0add9ff2dabce09
# Code AST SHA-256:   350ed2f4c03326ba07e570299573ffb0f2f75ff6441d322d2900321686824003
# Verified Classes:   AgentCardFederationError, ResolvedCard
# Defensive Guards:   4 fail-closed invariants (zero external deps)

import time
from dataclasses import dataclass
from urllib.parse import urlparse


class AgentCardFederationError(RuntimeError):
    """Raised when a federated Agent Card violates origin-binding invariants."""
    pass


@dataclass
class ResolvedCard:
    fetch_origin: str          # host the card was actually fetched from
    provider_url: str          # card.provider.url as signed
    signed_issued_at: float    # epoch seconds, inside signed payload
    signature_verified: bool
    consumer_max_age_s: float


def admit_federated_card(card: ResolvedCard, now: float | None = None) -> None:
    """
    Enforce the origin-binding + signed-freshness invariant for a cross-org card.
    Raises AgentCardFederationError on any violation; returns None on success.
    """
    now = time.time() if now is None else now

    if not card.signature_verified:
        raise AgentCardFederationError(
            f"card from {card.fetch_origin!r} failed signature verification"
        )

    fetch_host = urlparse(card.fetch_origin).hostname
    provider_host = urlparse(card.provider_url).hostname
    if fetch_host != provider_host:
        # This is a relayed card. It is NOT admissible into the direct-trust slot.
        raise AgentCardFederationError(
            "relayed card rejected: fetch origin "
            f"{fetch_host!r} != signed provider origin {provider_host!r}; "
            "route through relay-trust slot with enforced freshness, not direct trust"
        )

    age = now - card.signed_issued_at
    if age < 0:
        raise AgentCardFederationError(
            f"card from {fetch_host!r} has issuedAt in the future (skew {age:.0f}s)"
        )
    if age > card.consumer_max_age_s:
        raise AgentCardFederationError(
            f"stale card from {fetch_host!r}: age {age:.0f}s exceeds "
            f"consumer max {card.consumer_max_age_s:.0f}s (signature still valid, "
            "but capability set may be withdrawn)"
        )
