Skip to content

AgentCardSigner

The AgentCardSigner class provides methods for signing A2A Agent Cards using Sigstore's keyless signing infrastructure.

Overview

from sigstore_a2a import AgentCardSigner

# Create a signer with default settings
signer = AgentCardSigner()

# Sign an agent card
signed_card = signer.sign_agent_card("agent-card.json")

API Reference

AgentCardSigner

AgentCardSigner(
    identity_token: str | None = None,
    trust_config: Path | None = None,
    staging: bool = False,
    client_id: str | None = None,
    client_secret: str | None = None,
    use_ambient_credentials: bool = False,
    verbose: bool = False,
)

Signs A2A Agent Cards using Sigstore keyless signing.

Initialize the Agent Card signer.

PARAMETER DESCRIPTION
identity_token

Pre-obtained identity token

TYPE: str | None DEFAULT: None

staging

Use Sigstore staging environment

TYPE: bool DEFAULT: False

Source code in sigstore_a2a/signer.py
def __init__(
    self,
    identity_token: str | None = None,
    trust_config: Path | None = None,
    staging: bool = False,
    client_id: str | None = None,
    client_secret: str | None = None,
    use_ambient_credentials: bool = False,
    verbose: bool = False,
):
    """Initialize the Agent Card signer.

    Args:
        identity_token: Pre-obtained identity token
        staging: Use Sigstore staging environment
    """
    self.identity_token = identity_token
    self.staging = staging
    self.trust_config = trust_config
    self.client_id = client_id
    self.client_secret = client_secret
    self.use_ambient_credentials = use_ambient_credentials
    self.verbose = verbose

    self._signer: SigningContext | None = None
    self._issuer: Issuer | None = None

sign_agent_card

sign_agent_card(
    agent_card: AgentCard | dict[str, Any] | str | Path,
    provenance_bundle: SLSAProvenance | None = None,
) -> SignedAgentCard

Sign an A2A Agent Card.

PARAMETER DESCRIPTION
agent_card

Agent card to sign (model, dict, JSON string, or file path)

TYPE: AgentCard | dict[str, Any] | str | Path

provenance_bundle

Optional SLSA provenance bundle

TYPE: SLSAProvenance | None DEFAULT: None

RETURNS DESCRIPTION
SignedAgentCard

Signed Agent Card with verification material

RAISES DESCRIPTION
ValueError

If agent card is invalid

RuntimeError

If signing fails

Source code in sigstore_a2a/signer.py
def sign_agent_card(
    self, agent_card: AgentCard | dict[str, Any] | str | Path, provenance_bundle: SLSAProvenance | None = None
) -> SignedAgentCard:
    """Sign an A2A Agent Card.

    Args:
        agent_card: Agent card to sign (model, dict, JSON string, or file path)
        provenance_bundle: Optional SLSA provenance bundle

    Returns:
        Signed Agent Card with verification material

    Raises:
        ValueError: If agent card is invalid
        RuntimeError: If signing fails
    """
    if isinstance(agent_card, str | Path):
        if Path(agent_card).exists():
            with open(agent_card) as f:
                card_data = json.load(f)
        else:
            card_data = json.loads(str(agent_card))
    elif isinstance(agent_card, dict):
        card_data = agent_card
    elif isinstance(agent_card, AgentCard):
        card_data = agent_card.model_dump(by_alias=True)
    else:
        raise ValueError(f"Invalid agent card type: {type(agent_card)}")
    try:
        parsed_card = AgentCard.model_validate(card_data)
    except Exception as e:
        raise ValueError(f"Invalid agent card: {e}") from e

    canonical_data = canonicalize_json(card_data)

    # Create in-toto statement for agent card
    import hashlib

    # Calculate digest of the canonical data
    digest_hex = hashlib.sha256(canonical_data).hexdigest()
    digest_set = DigestSet(root={"sha256": digest_hex})

    # Create subject for the agent card
    subject = Subject(name=parsed_card.name, digest=digest_set)

    # Build the in-toto statement
    builder = StatementBuilder()
    builder = builder.subjects([subject])
    builder = builder.predicate_type("https://a2a.openwallet.dev/agentcard/v1")
    builder = builder.predicate(card_data)

    # Build the statement
    statement = builder.build()

    signing_context, issuer = self._get_signer()

    # 1) Explicitly supplied identity token
    # 2) Ambient credential detected in the environment
    # 3) Interactive OAuth flow
    try:
        if self.identity_token:
            if isinstance(self.identity_token, str):
                identity = IdentityToken(self.identity_token)
            else:
                identity = self.identity_token

        elif self.use_ambient_credentials:
            ambient_credential = detect_credential()
            identity = IdentityToken(ambient_credential)

        else:
            identity = issuer.identity_token()

        with signing_context.signer(identity, cache=True) as signer:
            bundle = signer.sign_dsse(statement)

    except Exception as e:
        raise RuntimeError(f"Failed to sign agent card: {e}") from e

    attestations = Attestations(signature_bundle=bundle.to_json(), provenance_bundle=provenance_bundle)

    signed_card = SignedAgentCard(agent_card=parsed_card, attestations=attestations)

    return signed_card

sign_file

sign_file(
    input_path: str | Path,
    output_path: str | Path | None = None,
    provenance_bundle: SLSAProvenance | None = None,
) -> Path

Sign an Agent Card file.

PARAMETER DESCRIPTION
input_path

Path to Agent Card JSON file

TYPE: str | Path

output_path

Output path for signed card (default: input_path with .signed.json)

TYPE: str | Path | None DEFAULT: None

provenance_bundle

Optional SLSA provenance bundle

TYPE: SLSAProvenance | None DEFAULT: None

RETURNS DESCRIPTION
Path

Path to signed Agent Card file

Source code in sigstore_a2a/signer.py
def sign_file(
    self,
    input_path: str | Path,
    output_path: str | Path | None = None,
    provenance_bundle: SLSAProvenance | None = None,
) -> Path:
    """Sign an Agent Card file.

    Args:
        input_path: Path to Agent Card JSON file
        output_path: Output path for signed card (default: input_path with .signed.json)
        provenance_bundle: Optional SLSA provenance bundle

    Returns:
        Path to signed Agent Card file
    """
    input_path = Path(input_path)

    if output_path is None:
        output_path = input_path.with_suffix(".signed.json")
    else:
        output_path = Path(output_path)

    signed_card = self.sign_agent_card(input_path, provenance_bundle)

    with open(output_path, "w") as f:
        json.dump(signed_card.model_dump(by_alias=True), f, indent=2, default=str)

    return output_path

Usage Examples

Basic Signing

from sigstore_a2a import AgentCardSigner

signer = AgentCardSigner()

# Sign from a file path
signed_card = signer.sign_agent_card("agent-card.json")

# Sign from a dictionary
card_data = {
    "name": "My Agent",
    "url": "https://agent.example.com",
    "protocolVersion": "0.2.9"
}
signed_card = signer.sign_agent_card(card_data)

Using Ambient Credentials (CI/CD)

signer = AgentCardSigner(use_ambient_credentials=True)
signed_card = signer.sign_agent_card("agent-card.json")

Using a Pre-obtained Identity Token

import os

signer = AgentCardSigner(
    identity_token=os.environ.get("OIDC_TOKEN")
)
signed_card = signer.sign_agent_card("agent-card.json")

Using Staging Environment

signer = AgentCardSigner(staging=True)
signed_card = signer.sign_agent_card("agent-card.json")

Using Custom Trust Configuration

from pathlib import Path

signer = AgentCardSigner(
    trust_config=Path("/path/to/trust-config.json")
)
signed_card = signer.sign_agent_card("agent-card.json")

Signing with Provenance

from sigstore_a2a import AgentCardSigner, ProvenanceBuilder

# Build provenance
provenance = ProvenanceBuilder().from_github_actions().build()

# Sign with provenance
signer = AgentCardSigner()
signed_card = signer.sign_agent_card(
    "agent-card.json",
    provenance_bundle=provenance
)

Saving Signed Cards

import json

signer = AgentCardSigner()
signed_card = signer.sign_agent_card("agent-card.json")

# Save to file
with open("signed-card.json", "w") as f:
    json.dump(signed_card.model_dump(by_alias=True), f, indent=2)

# Or use sign_file for convenience
output_path = signer.sign_file(
    "agent-card.json",
    output_path="signed-card.json"
)