Skip to content

Commit 8e7e36a

Browse files
authored
feat: add Authority Envelope SDK wrappers (RFC-008) (#60)
* feat: add Authority Envelope SDK wrappers (RFC-008) Adds envelope creation, derivation, and transport header generation to the Python SDK, wrapping the new gRPC surface added in capiscio-core PR #70. Changes: - Regenerate proto stubs with CreateEnvelope, DeriveEnvelope, BuildTransportHeaders, VerifyEnvelopeChain RPCs and 8 new messages - Add low-level gRPC client wrappers in _rpc/client.py: - create_envelope(): Create root envelopes (§6.1) - derive_envelope(): Derive child envelopes with hash linking (§6.3) - build_transport_headers(): Encode chains to HTTP headers (§15.1-§15.3) - Add high-level SimpleGuard API methods: - create_envelope(): Simplified root envelope creation - derive_envelope(): Simplified child envelope derivation - make_delegation_headers(): Combined badge + chain headers for requests - Add 14 unit tests covering all envelope operations Tests cover: basic creation, constraints serialization, enforcement mode, error propagation, derivation with narrowing, header generation with/without badge maps. * fix(sdk): type annotation and protobuf version consistency - Use list[str] instead of bare list in make_delegation_headers - Align simpleguard_pb2.py runtime version check to 6.33.5 to match all other generated pb2 files and the protobuf>=6.33.5 dependency
1 parent 1fa41ce commit 8e7e36a

5 files changed

Lines changed: 741 additions & 6 deletions

File tree

capiscio_sdk/_rpc/client.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,125 @@ def init(
13461346
"registered": response.registered,
13471347
}, None
13481348

1349+
def create_envelope(
1350+
self,
1351+
key_id: str,
1352+
subject_did: str,
1353+
capability_class: str,
1354+
delegation_depth_remaining: int,
1355+
issuer_badge_jti: str,
1356+
txn_id: str = "",
1357+
expires_in_seconds: int = 3600,
1358+
constraints_json: str = "",
1359+
subject_badge_jti: str = "",
1360+
enforcement_mode_min: str = "",
1361+
) -> tuple[str, str, str, Optional[str]]:
1362+
"""Create a root Authority Envelope (RFC-008 §6.1).
1363+
1364+
Args:
1365+
key_id: Key to sign with (from key store)
1366+
subject_did: DID of the agent receiving authority
1367+
capability_class: e.g., "tools.database.read"
1368+
delegation_depth_remaining: How many further delegations allowed
1369+
issuer_badge_jti: JTI of the issuer's badge
1370+
txn_id: Transaction ID (auto-generated if empty)
1371+
expires_in_seconds: TTL from now (default: 3600)
1372+
constraints_json: Optional JSON constraints object
1373+
subject_badge_jti: JTI of the subject's badge (optional)
1374+
enforcement_mode_min: Optional minimum enforcement mode
1375+
1376+
Returns:
1377+
Tuple of (envelope_jws, envelope_id, issuer_did, error_message)
1378+
"""
1379+
request = simpleguard_pb2.CreateEnvelopeRequest(
1380+
key_id=key_id,
1381+
subject_did=subject_did,
1382+
capability_class=capability_class,
1383+
delegation_depth_remaining=delegation_depth_remaining,
1384+
txn_id=txn_id,
1385+
expires_in_seconds=expires_in_seconds,
1386+
constraints_json=constraints_json,
1387+
issuer_badge_jti=issuer_badge_jti,
1388+
subject_badge_jti=subject_badge_jti,
1389+
enforcement_mode_min=enforcement_mode_min,
1390+
)
1391+
response = self._stub.CreateEnvelope(request)
1392+
error = response.error_message if response.error_message else None
1393+
return response.envelope_jws, response.envelope_id, response.issuer_did, error
1394+
1395+
def derive_envelope(
1396+
self,
1397+
parent_envelope_jws: str,
1398+
key_id: str,
1399+
subject_did: str,
1400+
capability_class: str,
1401+
delegation_depth_remaining: int,
1402+
issuer_badge_jti: str,
1403+
expires_in_seconds: int = 1800,
1404+
constraints_json: str = "",
1405+
subject_badge_jti: str = "",
1406+
enforcement_mode_min: str = "",
1407+
) -> tuple[str, str, str, Optional[str]]:
1408+
"""Derive a child Authority Envelope from a parent (RFC-008 §6.3).
1409+
1410+
Args:
1411+
parent_envelope_jws: Parent envelope JWS
1412+
key_id: Key to sign child with
1413+
subject_did: DID of the next delegate
1414+
capability_class: Must be equal or narrower than parent
1415+
delegation_depth_remaining: Must be < parent's depth
1416+
issuer_badge_jti: JTI of the child issuer's own badge
1417+
expires_in_seconds: TTL from now (default: 1800)
1418+
constraints_json: Must be equal or more restrictive
1419+
subject_badge_jti: JTI of the subject's badge
1420+
enforcement_mode_min: Optional minimum enforcement mode
1421+
1422+
Returns:
1423+
Tuple of (envelope_jws, envelope_id, parent_authority_hash, error_message)
1424+
"""
1425+
request = simpleguard_pb2.DeriveEnvelopeRequest(
1426+
parent_envelope_jws=parent_envelope_jws,
1427+
key_id=key_id,
1428+
subject_did=subject_did,
1429+
capability_class=capability_class,
1430+
delegation_depth_remaining=delegation_depth_remaining,
1431+
expires_in_seconds=expires_in_seconds,
1432+
constraints_json=constraints_json,
1433+
subject_badge_jti=subject_badge_jti,
1434+
issuer_badge_jti=issuer_badge_jti,
1435+
enforcement_mode_min=enforcement_mode_min,
1436+
)
1437+
response = self._stub.DeriveEnvelope(request)
1438+
error = response.error_message if response.error_message else None
1439+
return response.envelope_jws, response.envelope_id, response.parent_authority_hash, error
1440+
1441+
def build_transport_headers(
1442+
self,
1443+
chain: list[str],
1444+
badge_map: Optional[dict[str, str]] = None,
1445+
) -> tuple[str, str, str, Optional[str]]:
1446+
"""Build RFC-008 §15 transport headers for a delegation chain.
1447+
1448+
Args:
1449+
chain: Ordered JWS array [root, ..., leaf]
1450+
badge_map: DID → badge JWS for intermediate agents
1451+
1452+
Returns:
1453+
Tuple of (authority_header, authority_chain_header, badge_map_header, error_message)
1454+
"""
1455+
request = simpleguard_pb2.BuildTransportHeadersRequest(
1456+
chain=chain,
1457+
badge_map=badge_map or {},
1458+
)
1459+
response = self._stub.BuildTransportHeaders(request)
1460+
error = response.error_message if response.error_message else None
1461+
return (
1462+
response.authority_header,
1463+
response.authority_chain_header,
1464+
response.badge_map_header,
1465+
error,
1466+
)
1467+
13491468

13501469
class MCPClient:
13511470
"""Client wrapper for MCPService (RFC-006 Tool Authority + RFC-007 Server Identity).

0 commit comments

Comments
 (0)