# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-structural-signals-indicate-a2a-agent-card-verification-has-silently-degraded-into-mutual-tls-trust-conformance-theater.md
# Remnant Warrant:    https://corpusfetch.com/c/what-structural-signals-indicate-a2a-agent-card-verification-has-silently-degraded-into-mutual-tls-trust-conformance-theater.warrant.json
# Markdown SHA-256:   167a49fe4a0bda7f2381ed8b1691f7c9c25148b3a07b53f908f12ee506c8fb70
# Code AST SHA-256:   7c28edf94b04ef6148f7233035bd1bef4585cb14aa78f0f7ae6814f48781c483
# Verified Classes:   CardAuthorityError
# Defensive Guards:   5 fail-closed invariants (zero external deps)

class CardAuthorityError(RuntimeError):
    """Raised when an A2A agent card is authentic but not authoritative for the invoked skill."""
    pass


def verify_card_authority(card: dict, invoked_skill: str, caller_trust_store: dict) -> None:
    url_origin = _origin(card["url"])
    jku = card.get("signatures", [{}])[0].get("header", {}).get("jku")
    if jku is None:
        raise CardAuthorityError("card has no external jku; signature is self-referential")
    if _origin(jku) == url_origin:
        raise CardAuthorityError(
            f"JWKS origin {_origin(jku)} equals card origin {url_origin}; "
            "signature proves self-consistency only, not authority"
        )
    if "skills" not in card.get("signatures", [{}])[0].get("protected_covers", []):
        raise CardAuthorityError("signed payload does not cover skills[]; skill set is unbound")
    kid = card["signatures"][0]["header"].get("kid")
    if kid not in caller_trust_store:
        raise CardAuthorityError(f"unknown kid {kid!r}; refusing local-trust-store fallback")
    if invoked_skill not in card.get("skills", []):
        raise CardAuthorityError(f"invoked skill {invoked_skill!r} not present in signed card")
    # Only here is the card authoritative for this skill.
    return None


def _origin(url: str) -> str:
    from urllib.parse import urlsplit
    s = urlsplit(url)
    return f"{s.scheme}://{s.netloc}"
