Badge API¶
The Badge API provides portable, verifiable identity for agents through Trust Badges—signed JWS tokens that prove an agent's identity and trust level.
RFC-002 Implementation
This API implements RFC-002: Trust Badge System for portable agent identity.
Quick Start¶
from capiscio_sdk import verify_badge, parse_badge, TrustLevel
# Verify a badge from another agent
result = verify_badge(
token,
trusted_issuers=["https://registry.capisc.io"],
audience="https://my-service.example.com",
)
if result.valid:
print(f"✅ Agent {result.claims.subject} verified") # Agent DID
print(f" Trust Level: {result.claims.trust_level.value}") # "0" to "4"
print(f" Domain: {result.claims.domain}")
print(f" IAL: {result.claims.ial}") # Identity Assurance Level
else:
print(f"❌ Verification failed: {result.error}")
Functions¶
capiscio_sdk.badge.verify_badge ¶
verify_badge(
token: str,
*,
trusted_issuers: Optional[List[str]] = None,
audience: Optional[str] = None,
mode: Union[VerifyMode, str] = VerifyMode.ONLINE,
skip_revocation_check: bool = False,
skip_agent_status_check: bool = False,
public_key_jwk: Optional[str] = None,
fail_open: bool = False,
stale_threshold_seconds: int = REVOCATION_CACHE_MAX_STALENESS,
options: Optional[VerifyOptions] = None
) -> VerifyResult
Verify a Trust Badge token.
Performs full RFC-002 verification including: - JWS signature verification - Claims validation (exp, iat, iss, sub, aud) - Revocation check (online/hybrid modes) - Agent status check (online/hybrid modes) - RFC-002 v1.3 §7.5: Staleness fail-closed for levels 2-4
| PARAMETER | DESCRIPTION |
|---|---|
token | The badge JWT/JWS token to verify. TYPE: |
trusted_issuers | List of trusted issuer URLs. If empty, all issuers accepted. TYPE: |
audience | Your service URL for audience validation. TYPE: |
mode | Verification mode (online, offline, hybrid). TYPE: |
skip_revocation_check | Skip revocation check (testing only). TYPE: |
skip_agent_status_check | Skip agent status check (testing only). TYPE: |
public_key_jwk | Override public key JWK for offline verification. TYPE: |
fail_open | Allow stale cache for levels 2-4 (WARNING: violates RFC-002 default). TYPE: |
stale_threshold_seconds | Cache staleness threshold (default: 300 per RFC-002 §7.5). TYPE: |
options | VerifyOptions object (alternative to individual args). TYPE: |
| RETURNS | DESCRIPTION |
|---|---|
VerifyResult | VerifyResult with validation status and claims. |
Example
result = verify_badge( token, trusted_issuers=["https://registry.capisc.io"], audience="https://my-service.example.com", )
if result.valid: print(f"Agent {result.claims.agent_id} verified at level {result.claims.trust_level}")
capiscio_sdk.badge.parse_badge ¶
Parse badge claims without verification.
Use this to inspect badge contents before full verification, or to extract claims for display purposes.
| PARAMETER | DESCRIPTION |
|---|---|
token | The badge JWT/JWS token to parse. TYPE: |
| RETURNS | DESCRIPTION |
|---|---|
BadgeClaims | BadgeClaims object with parsed claims. |
| RAISES | DESCRIPTION |
|---|---|
ValueError | If the token cannot be parsed. |
Example
claims = parse_badge(token) print(f"Badge for: {claims.agent_id}") print(f"Issued by: {claims.issuer}") print(f"Expires: {claims.expires_at}")
capiscio_sdk.badge.request_badge async ¶
request_badge(
agent_id: str,
*,
ca_url: str = "https://registry.capisc.io",
api_key: Optional[str] = None,
domain: Optional[str] = None,
trust_level: Union[
TrustLevel, str
] = TrustLevel.LEVEL_1,
audience: Optional[List[str]] = None,
timeout: float = 30.0
) -> str
Request a new Trust Badge from a Certificate Authority.
This sends a request to the CA to issue a new badge for the agent. The CA will verify the agent's identity based on the trust level and return a signed badge token.
Uses the capiscio-core gRPC service for the actual request.
| PARAMETER | DESCRIPTION |
|---|---|
agent_id | The agent identifier to request a badge for. TYPE: |
ca_url | Certificate Authority URL (default: CapiscIO registry). TYPE: |
api_key | API key for authentication with the CA. TYPE: |
domain | Agent's domain (required for verification). TYPE: |
trust_level | Requested trust level per RFC-002 §5: - 1 (REG): Registered - Account registration - 2 (DV): Domain Validated - DNS/HTTP proof - 3 (OV): Organization Validated - Legal entity - 4 (EV): Extended Validated - Security audit Note: LEVEL_0 (Self-Signed) is not available via CA request. TYPE: |
audience | Optional audience restrictions for the badge. TYPE: |
timeout | Request timeout in seconds (not used with gRPC). TYPE: |
| RETURNS | DESCRIPTION |
|---|---|
str | The signed badge JWT token. |
| RAISES | DESCRIPTION |
|---|---|
ValueError | If the CA returns an error. |
Example
token = await request_badge( agent_id="my-agent", ca_url="https://registry.capisc.io", api_key=os.environ["CAPISCIO_API_KEY"], domain="example.com", )
Save the token for later use¶
with open("badge.jwt", "w") as f: f.write(token)
capiscio_sdk.badge.request_badge_sync ¶
request_badge_sync(
agent_id: str,
*,
ca_url: str = "https://registry.capisc.io",
api_key: Optional[str] = None,
domain: Optional[str] = None,
trust_level: Union[
TrustLevel, str
] = TrustLevel.LEVEL_1,
audience: Optional[List[str]] = None,
timeout: float = 30.0
) -> str
Synchronous version of request_badge.
Uses the capiscio-core gRPC service for the actual request. See request_badge for full documentation.
capiscio_sdk.badge.request_pop_badge async ¶
request_pop_badge(
agent_did: str,
private_key_jwk: str,
*,
ca_url: str = "https://registry.capisc.io",
api_key: Optional[str] = None,
ttl_seconds: int = 300,
audience: Optional[List[str]] = None,
timeout: float = 30.0
) -> str
Request a Trust Badge using Proof of Possession (RFC-003).
This requests a badge using the PoP challenge-response protocol, providing IAL-1 assurance with cryptographic key binding. The agent must prove possession of the private key associated with their DID.
Uses the capiscio-core gRPC service for the actual PoP flow.
| PARAMETER | DESCRIPTION |
|---|---|
agent_did | The agent DID (did:web:... or did TYPE: |
private_key_jwk | Private key in JWK format (JSON string). TYPE: |
ca_url | Certificate Authority URL (default: CapiscIO registry). TYPE: |
api_key | API key for authentication with the CA. TYPE: |
ttl_seconds | Requested badge TTL in seconds (default: 300). TYPE: |
audience | Optional audience restrictions for the badge. TYPE: |
timeout | Request timeout in seconds (not used with gRPC). TYPE: |
| RETURNS | DESCRIPTION |
|---|---|
str | The signed badge JWT token with IAL-1 assurance. |
| RAISES | DESCRIPTION |
|---|---|
ValueError | If the CA returns an error or PoP verification fails. |
Example
import json
Load private key¶
with open("private.jwk") as f: private_key_jwk = json.dumps(json.load(f))
token = await request_pop_badge( agent_did="did:web:registry.capisc.io:agents:my-agent", private_key_jwk=private_key_jwk, ca_url="https://registry.capisc.io", api_key=os.environ["CAPISCIO_API_KEY"], )
Verify the badge has IAL-1¶
claims = parse_badge(token)
Note: IAL-1 badges have a 'cnf' claim with key binding¶
capiscio_sdk.badge.request_pop_badge_sync ¶
request_pop_badge_sync(
agent_did: str,
private_key_jwk: str,
*,
ca_url: str = "https://registry.capisc.io",
api_key: Optional[str] = None,
ttl_seconds: int = 300,
audience: Optional[List[str]] = None,
timeout: float = 30.0
) -> str
Synchronous version of request_pop_badge.
Uses the capiscio-core gRPC service for the actual PoP flow. See request_pop_badge for full documentation.
capiscio_sdk.badge.start_badge_keeper ¶
start_badge_keeper(
mode: str,
*,
agent_id: str = "",
api_key: str = "",
ca_url: str = "https://registry.capisc.io",
private_key_path: str = "",
output_file: str = "badge.jwt",
domain: str = "",
ttl_seconds: int = 300,
renew_before_seconds: int = 60,
check_interval_seconds: int = 30,
trust_level: Union[
TrustLevel, str, int
] = TrustLevel.LEVEL_1
) -> Generator[dict, None, None]
Start a badge keeper daemon (RFC-002 §7.3).
The keeper automatically renews badges before they expire, ensuring continuous operation. Returns a generator of keeper events.
| PARAMETER | DESCRIPTION |
|---|---|
mode | 'ca' for CA mode, 'self-sign' for development TYPE: |
agent_id | Agent UUID (required for CA mode) TYPE: |
api_key | API key (required for CA mode) TYPE: |
ca_url | CA URL (default: https://registry.capisc.io) TYPE: |
private_key_path | Path to private key JWK (required for self-sign) TYPE: |
output_file | Path to write badge file TYPE: |
domain | Agent domain TYPE: |
ttl_seconds | Badge TTL (default: 300) TYPE: |
renew_before_seconds | Renew this many seconds before expiry (default: 60) TYPE: |
check_interval_seconds | Check interval (default: 30) TYPE: |
trust_level | Trust level for CA mode (1-4, default: 1) TYPE: |
| YIELDS | DESCRIPTION |
|---|---|
dict | KeeperEvent dicts with keys: type, badge_jti, subject, trust_level, expires_at, error, error_code, timestamp, token TYPE:: |
Example
CA mode - production¶
for event in start_badge_keeper( mode="ca", agent_id="my-agent-uuid", api_key=os.environ["CAPISCIO_API_KEY"], ): if event["type"] == "renewed": print(f"Badge renewed: {event['badge_jti']}") elif event["type"] == "error": print(f"Error: {event['error']}")
Self-sign mode - development¶
for event in start_badge_keeper( mode="self-sign", private_key_path="private.jwk", ): print(f"Event: {event['type']}")
Classes¶
capiscio_sdk.badge.BadgeClaims dataclass ¶
Parsed badge claims from a Trust Badge token.
| ATTRIBUTE | DESCRIPTION |
|---|---|
jti | Unique badge identifier (UUID). TYPE: |
issuer | Badge issuer URL (CA) or did:key for self-signed. TYPE: |
subject | Agent DID (did:web format). TYPE: |
audience | Optional list of intended audience URLs. TYPE: |
issued_at | When the badge was issued. TYPE: |
expires_at | When the badge expires. TYPE: |
trust_level | Trust level per RFC-002 §5: - 0 (SS): Self-Signed - Development only - 1 (REG): Registered - Account registration - 2 (DV): Domain Validated - DNS/HTTP proof - 3 (OV): Organization Validated - Legal entity - 4 (EV): Extended Validated - Security audit TYPE: |
domain | Agent's verified domain. TYPE: |
agent_name | Human-readable agent name. TYPE: |
agent_id | Extracted agent ID from subject DID. TYPE: |
ial | Identity Assurance Level (RFC-002 §7.2.1): - "0": Account-attested (no key proof) - "1": Proof of Possession (key holder verified, has cnf claim) TYPE: |
raw_claims | Original JWT claims dict for advanced access. TYPE: |
has_key_binding property ¶
Check if this badge has IAL-1 key binding (ial='1' and cnf claim).
Per RFC-002 §7.2.1, IAL-1 badges MUST include a 'cnf' (confirmation) claim that cryptographically binds the badge to the agent's private key.
confirmation_key property ¶
Get the confirmation key (cnf claim) if present.
Returns the JWK thumbprint or key from the cnf claim for IAL-1 badges. Returns None for IAL-0 badges or if cnf is not present.
to_dict ¶
Convert to dictionary.
Preserves the cnf (confirmation) claim for IAL-1 badges to support round-trip serialization.
capiscio_sdk.badge.VerifyResult dataclass ¶
Result of badge verification.
| ATTRIBUTE | DESCRIPTION |
|---|---|
valid | Whether the badge is valid. TYPE: |
claims | Parsed badge claims (if valid or parseable). TYPE: |
error | Error message if verification failed. TYPE: |
error_code | RFC-002 error code if applicable. TYPE: |
warnings | Non-fatal issues encountered. TYPE: |
mode | Verification mode that was used. TYPE: |
capiscio_sdk.badge.VerifyOptions dataclass ¶
Options for badge verification.
| ATTRIBUTE | DESCRIPTION |
|---|---|
mode | Verification mode (online, offline, hybrid). TYPE: |
trusted_issuers | List of trusted issuer URLs. TYPE: |
audience | Expected audience (your service URL). TYPE: |
skip_revocation_check | Skip revocation check (testing only). TYPE: |
skip_agent_status_check | Skip agent status check (testing only). TYPE: |
public_key_jwk | Override public key (for offline verification). TYPE: |
fail_open | Allow stale cache for levels 2-4 (WARNING: violates RFC-002 default). TYPE: |
stale_threshold_seconds | Cache staleness threshold in seconds (default: 300 per RFC-002 §7.5). TYPE: |
capiscio_sdk.badge.VerifyMode ¶
Bases: Enum
Badge verification mode.
ONLINE class-attribute instance-attribute ¶
Perform real-time checks against the registry (revocation, agent status).
OFFLINE class-attribute instance-attribute ¶
Use only local trust store and revocation cache.
HYBRID class-attribute instance-attribute ¶
Try online checks, fall back to cache if unavailable.
capiscio_sdk.badge.TrustLevel ¶
Bases: Enum
Trust level as defined in RFC-002 §5.
Levels
LEVEL_0 (SS): Self-Signed - did:key, iss == sub. Development only. LEVEL_1 (REG): Registered - Account registration with CA. LEVEL_2 (DV): Domain Validated - DNS/HTTP domain ownership proof. LEVEL_3 (OV): Organization Validated - Legal entity verification. LEVEL_4 (EV): Extended Validated - Manual review + security audit.
LEVEL_0 class-attribute instance-attribute ¶
Self-Signed (SS) - did:key, iss == sub. Development only.
LEVEL_1 class-attribute instance-attribute ¶
Registered (REG) - Account registration with CA.
LEVEL_2 class-attribute instance-attribute ¶
Domain Validated (DV) - DNS/HTTP domain ownership proof.
LEVEL_3 class-attribute instance-attribute ¶
Organization Validated (OV) - Legal entity verification.
LEVEL_4 class-attribute instance-attribute ¶
Extended Validated (EV) - Manual review + security audit.
from_string classmethod ¶
Create TrustLevel from string value.
Error Codes¶
RFC-002 defines these verification error codes:
| Code | Description |
|---|---|
BADGE_MALFORMED | Token format invalid |
BADGE_SIGNATURE_INVALID | Signature verification failed |
BADGE_EXPIRED | Badge past expiration time |
BADGE_NOT_YET_VALID | Badge not yet valid (iat in future) |
BADGE_ISSUER_UNTRUSTED | Issuer not in trusted list |
BADGE_AUDIENCE_MISMATCH | Your service not in audience |
BADGE_REVOKED | Badge has been revoked |
BADGE_CLAIMS_INVALID | Required claims missing/invalid |
BADGE_AGENT_DISABLED | Agent has been disabled |
Patterns¶
Middleware Pattern¶
from capiscio_sdk import verify_badge
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
@app.middleware("http")
async def verify_badge_middleware(request: Request, call_next):
# Skip for health checks
if request.url.path == "/health":
return await call_next(request)
# Get badge from Authorization header
auth = request.headers.get("Authorization", "")
if not auth.startswith("Bearer "):
raise HTTPException(401, "Missing badge")
token = auth[7:]
result = verify_badge(
token,
trusted_issuers=["https://registry.capisc.io"],
audience=str(request.base_url),
)
if not result.valid:
raise HTTPException(401, f"Invalid badge: {result.error}")
# Attach claims to request state (matches SDK middleware pattern)
request.state.agent = result.claims
request.state.agent_id = result.claims.issuer
return await call_next(request)
Use Built-in Middleware
For production, consider using the SDK's built-in CapiscioMiddleware which handles body integrity verification and proper error responses per RFC-002 §9.1.
Trust Level Gate¶
from capiscio_sdk import verify_badge, TrustLevel
def require_trust_level(token: str, min_level: TrustLevel) -> bool:
"""Require minimum trust level for sensitive operations.
RFC-002 §5 Trust Levels:
- LEVEL_0: Self-Signed (SS) - development only
- LEVEL_1: Registered (REG) - account registration
- LEVEL_2: Domain Validated (DV) - DNS/HTTP proof
- LEVEL_3: Organization Validated (OV) - legal entity
- LEVEL_4: Extended Validated (EV) - security audit
"""
result = verify_badge(token)
if not result.valid:
return False
# TrustLevel enum values are strings "0"-"4", compare as integers
return int(result.claims.trust_level.value) >= int(min_level.value)
# Usage
if require_trust_level(token, TrustLevel.LEVEL_2):
# Allow sensitive operation (DV or higher)
pass
See Also¶
- Trust Badges Guide - CLI usage
- Badge Keeper - Auto-renewal
- Trust Model - Identity concepts