// Grants are data: sets of tool groups stored on the tenant, the user and // the agent. Policy text never names a tenant. entity ToolGroup; entity Tenant = { read_groups: Set, write_groups: Set, }; entity User in [Tenant] = { read_groups: Set, write_groups: Set, }; // The workload. Its identity comes from its certificate, not from the prompt. entity Agent = { read_groups: Set, write_groups: Set, }; // A tool bound to one tenant's data. entity Tool in [ToolGroup] = { tenant: Tenant, }; // Built by the gateway from verified facts, never copied from the request body. type CallContext = { user: User, // the human the chain started with tenant: Tenant, // the tenant bound at authentication depth: Long, // agent-to-agent hand-offs so far; the relay counts, never the agent // The scope of the exchanged token this hop holds. Required on every call: at // depth 0 it is what the user's request handed to the first agent. delegated_read_groups: Set, delegated_write_groups: Set, // The exact call and the clock, both stamped by the gateway and never read from the // request body. request_hash is sha256 over tenant, user, agent, tool, action and the // canonical arguments, so an approval is bound to one call and checked again when the // call runs. request_hash: String, now: datetime, approved_by?: User, // set only by the approval step approval_for?: String, // the request hash the approver saw approval_expires?: datetime, // when the approval stops being an approval }; // Actions are risk levels. They are enumerated, never negated. action read, write, destroy appliesTo { principal: Agent, resource: Tool, context: CallContext, };