From 29ecae0d6afeb01e5eb4ba4e82bef8d4af89cd4f Mon Sep 17 00:00:00 2001 From: SANKET SARKAR Date: Mon, 15 Jun 2026 17:23:44 +0530 Subject: [PATCH] feat(schema): support 'actor' message kind Bundle actor.json and accept kind='actor' in validate/sign/verify so the SDK can sign registry entries with the same ADR-0001 protocol used for events and policies. Co-Authored-By: Claude Opus 4.8 Signed-off-by: SANKET SARKAR --- openagp/_schema.py | 3 +- openagp/_schemas/actor.json | 120 ++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 openagp/_schemas/actor.json diff --git a/openagp/_schema.py b/openagp/_schema.py index 340d83c..6245f27 100644 --- a/openagp/_schema.py +++ b/openagp/_schema.py @@ -26,6 +26,7 @@ class SchemaValidationError(ValueError): "decision-request.json", "decision-response.json", "discovery.json", + "actor.json", ) @@ -63,7 +64,7 @@ def validate(message: dict[str, Any], *, kind: str = "event") -> None: Raises SchemaValidationError on failure. Returns None on success. `kind` is one of: 'event', 'policy', 'decision-request', - 'decision-response', 'discovery'. + 'decision-response', 'discovery', 'actor'. """ schema_name = f"{kind}.json" if schema_name not in _SCHEMA_NAMES: diff --git a/openagp/_schemas/actor.json b/openagp/_schemas/actor.json new file mode 100644 index 0000000..4311c49 --- /dev/null +++ b/openagp/_schemas/actor.json @@ -0,0 +1,120 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://openagp.io/schemas/v0.1/actor.json", + "title": "AGP registry actor entry", + "description": "A signed registry record for an AGP-compliant actor (vendor, plane, or customer). One file per actor at registry/actors/.json. The entry is self-signed by one of the actor's advertised keys per ADR 0001; the registry merge attests that the entry is well-formed and its conformance report verifies, not that the actor's product is endorsed.", + "type": "object", + "properties": { + "agp_version": { "$ref": "common.json#/$defs/agp_version" }, + + "actor": { + "type": "object", + "description": "Actor identity. The fqdn MUST equal the entry's filename stem.", + "properties": { + "fqdn": { "$ref": "common.json#/$defs/vendor_id" }, + "kind": { "$ref": "common.json#/$defs/actor_kind" }, + "display_name": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "description": "Human-readable name shown in registry listings." + } + }, + "required": ["fqdn", "kind"] + }, + + "conformance_levels": { + "type": "array", + "items": { "$ref": "common.json#/$defs/conformance_level" }, + "minItems": 1, + "uniqueItems": true, + "description": "Levels this actor implements. L1 is implied by listing L2 or L3." + }, + + "public_keys": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "key_id": { "$ref": "common.json#/$defs/key_id" }, + "purpose": { + "type": "string", + "enum": ["event_signing", "policy_signing", "decision_signing"] + }, + "alg": { "type": "string", "const": "Ed25519" }, + "value": { + "type": "string", + "pattern": "^[A-Za-z0-9+/]+=*$", + "minLength": 44, + "maxLength": 44, + "description": "Base64-encoded Ed25519 public key (32 raw bytes -> 44 base64 chars with padding)." + }, + "valid_from": { "$ref": "common.json#/$defs/iso8601_utc" }, + "valid_until": { "$ref": "common.json#/$defs/iso8601_utc" } + }, + "required": ["key_id", "purpose", "alg", "value"] + }, + "description": "Public keys this actor signs with. The key in signature.key_id MUST appear here." + }, + + "endpoints": { + "type": "object", + "description": "AGP endpoints this actor exposes. Field set varies by actor.kind.", + "properties": { + "discovery": { "type": "string", "format": "uri" }, + "events_receive": { "type": "string", "format": "uri" }, + "policy_receive": { "type": "string", "format": "uri" }, + "policy_read": { "type": "string", "format": "uri" }, + "decision_receive": { "type": "string", "format": "uri" } + } + }, + + "decision_budget_ms": { + "type": "integer", + "minimum": 50, + "maximum": 10000, + "description": "For L3 actors: declared maximum wait for a decision response. Default 300." + }, + + "supported_action_types": { + "type": "array", + "items": { "$ref": "common.json#/$defs/action_type" }, + "uniqueItems": true + }, + + "conformance_report": { + "type": "object", + "description": "Reference to the signed agp-cts report backing the claimed conformance_levels.", + "properties": { + "url": { "type": "string", "format": "uri" }, + "hash": { "$ref": "common.json#/$defs/hash_ref" }, + "cts_version": { "type": "string", "minLength": 1 }, + "produced_at": { "$ref": "common.json#/$defs/iso8601_utc" } + }, + "required": ["url", "hash"] + }, + + "registered_at": { "$ref": "common.json#/$defs/iso8601_utc" }, + + "metadata": { + "type": "object", + "description": "Advisory, human-facing fields. Not load-bearing for the protocol.", + "properties": { + "description": { "type": "string", "maxLength": 1024 }, + "homepage": { "type": "string", "format": "uri" }, + "contact": { "type": "string", "minLength": 1 } + } + }, + + "signature": { "$ref": "common.json#/$defs/signature" } + }, + + "required": [ + "agp_version", + "actor", + "conformance_levels", + "public_keys", + "signature" + ] +}