"""Identity domain record types and path codec.""" import json from typing import Literal, TypedDict from muse.core.types import Metadata class SignatureRecord(TypedDict): signer: str signature: str signed_at: str class IdentityRecord(TypedDict): handle: str type: Literal["human", "agent", "org"] pubkey: str | None quorum: int | None registered_at: str metadata: Metadata class RelationshipRecord(TypedDict): from_handle: str to_handle: str edge_type: Literal["spawns", "member_of"] weight: str | None authorized_by: list[SignatureRecord] # ── path encoding ────────────────────────────────────────────────────────────── def identity_path(handle: str) -> str: return f"identities/{handle}.json" def relationship_path(from_handle: str, edge_type: str, to_handle: str) -> str: return f"relationships/{from_handle}--{edge_type}--{to_handle}.json" def parse_identity_path(path: str) -> str | None: """Return handle if path is an identity path, else None.""" if path.startswith("identities/") and path.endswith(".json"): inner = path[len("identities/"):-len(".json")] if "/" not in inner and inner: return inner return None def parse_relationship_path(path: str) -> tuple[str, str, str] | None: """Return (from, edge_type, to) if path is a relationship path, else None.""" if not (path.startswith("relationships/") and path.endswith(".json")): return None inner = path[len("relationships/"):-len(".json")] parts = inner.split("--") if len(parts) != 3: return None frm, edge, to = parts if not frm or not edge or not to: return None return frm, edge, to # ── serialisation ────────────────────────────────────────────────────────────── def record_to_bytes(record: IdentityRecord | RelationshipRecord) -> bytes: return json.dumps(record, sort_keys=True, ensure_ascii=False).encode() def record_from_bytes(raw: bytes) -> IdentityRecord | RelationshipRecord: return json.loads(raw)