/** * Ory Permission Language (OPL) schema for the Agent Security permission model. * * Native agent and shell tools are allowed by default and may be explicitly * blocked. The deny is modeled natively in OPL as a permit: * * use = !blockedSubjects.includes(subject) * * Argus always checks the **`use` permit**, so a missing block allows and a * matching `blockedSubjects` relation denies. This model must be applied to the * project for checks to resolve. * * ## Naming conventions * * Every name here follows Ory Permissions' documented conventions * (https://www.ory.com/docs/keto/concepts/namespaces#naming-conventions): * * - **Namespaces** are TypeScript classes named after the **singular** form of * the type they describe, in UpperCamelCase — `AgentTool`, `ShellTool`, * `Delegation`, `PermissionMode`, `User`, `Agent`, `SubAgent`, `Session`. * - **Relations** are **plural** nouns naming the set of subjects on the far end * of the edge, so each reads as an English sentence — `User:bob is in users of * AgentTool:Bash`, `Agent:abc is in credentials of Delegation:agent:`. * - **Permits** are verbs, like Keto's own `view` — hence `use` for "may this * subject use this tool", which is the question the gate actually asks. * - A namespace holds a **homogeneous** type of object. That is why delegation * nodes and the posture sentinels are *not* tool objects: `AgentTool` holds * only tools, `Delegation` only delegation nodes, `PermissionMode` only the * posture anchors. A single namespace holding all three is the case the * convention doc calls out as incorrect, and it forced one class to carry a * grab bag of unrelated relations. * * A relation and a permit cannot share a name in OPL — which is exactly why the * grant relation is the plural `users` and the permit the singular verb `use`. */ /** * The relation that grants a subject use of a tool object — the set of subjects * that may use it. Written in the Ory Console (Agent Security); the plugin only * ever reads it. Reads as `User:bob is in users of AgentTool:Bash`. */ export declare const RELATION_USERS = "users"; /** * The relation that records a delegation edge on a {@link DELEGATION_NAMESPACE} * node (`user→agent`, `agent→subagent`) — the set of subjects that delegated to * it. Written by the agent-security broker, never by the runtime; declared here * so the local Keto mount (which loads this OPL) accepts the tuples. */ export declare const RELATION_DELEGATES = "delegates"; /** * The relation that explicitly blocks subjects from a tool object * (deny-override). A participle has no natural plural, so the relation names the * set it holds: `User:bob is in blockedSubjects of AgentTool:curl`. */ export declare const RELATION_BLOCKED_SUBJECTS = "blockedSubjects"; /** * The relation recording which OAuth2 clients a delegation node ran as: * `Delegation:agent:#credentials@(Agent:)`. Written by the * broker, never by the runtime, and never checked — a delegation node is keyed * on (identity, harness, host) so that it survives credential rotation, which * means the node alone cannot say which credential acted and two clients for one * triple collapse onto it. This edge carries that missing axis, for audit and so * a reader can join a node to its client exactly rather than by inference. */ export declare const RELATION_CREDENTIALS = "credentials"; /** * The relation holding the subjects that are in **enforce** mode. Read (never * written by the runtime) via a `checkPermission` on the * {@link PERMISSION_MODE_OBJECT} anchor: an admin grants * `PermissionMode:mode#enforcedSubjects@` (directly, or to a * subject-set / group for a project-wide flip) to promote that principal from * the default `observe` posture to `enforce`. Absence ⇒ `observe`. Named for the * set it holds, since `enforce` is a verb and has no plural noun form. */ export declare const RELATION_ENFORCED_SUBJECTS = "enforcedSubjects"; /** * Default permission namespace / OPL class name for harness tool objects * (`AgentTool:Bash`). Singular, because the namespace describes *one tool*, and * an object in it is one. Mirrors `ORY_PERMISSION_NAMESPACE`. */ export declare const DEFAULT_NAMESPACE = "AgentTool"; /** * Default Keto namespace for decomposed shell-command sub-tools (issue #76) — * one object per program/builtin word (`ShellTool:curl`). Rendered with the same * grant/block model as {@link DEFAULT_NAMESPACE}, so an explicit * `ShellTool:curl#blockedSubjects` overrides a `users` grant. Mirrors * `resolveShellCommandNamespace()` in tool-catalog.ts. */ export declare const DEFAULT_SHELL_NAMESPACE = "ShellTool"; /** * Namespace for delegation nodes — the current-state graph of who delegated to * whom (`Delegation:agent:`, `Delegation:subagent:|`). Its own * namespace because a delegation node is not a tool: mixing the two put * heterogeneous objects in one namespace and made the tool class carry * delegation relations that no tool object ever uses. The broker owns both the * objects and the tuples; the plugin treats a node id as opaque. */ export declare const DELEGATION_NAMESPACE = "Delegation"; /** * Namespaces for **references to** delegation nodes — the parent pointer on a * delegation edge (issue #225). * * `Agent` and `SubAgent` name a *principal identity*: an OAuth2 client that * authenticates, and the subject an admin grants or blocks. A delegation edge's * `delegates` subject is a different kind of thing — a pointer at another node * in the graph, keyed by `|||`. Both were * addressed under `Agent` / `SubAgent`, so one namespace carried two meanings: * * Agent: a principal identity * Agent:||| a graph-node reference * * Nothing written today is ambiguous, because a Hydra client id is a UUID and a * join key always contains separators — but that disjointness is a property of * the *data*, not of the model, and it is reachable-breakable through * `ORY_AGENT_SUBJECT_ID` (guarded separately in #241). The costlier problem is * silent: an admin, or a Console UI, that reads a node id off the delegation * graph and writes it as a grant subject produces a tuple that is accepted, * stored, and never matches any check — the same failure the SubjectSet-vs- * SubjectID invariant exists to prevent, one level up. That surface is four * relations wide (`users`, `blockedSubjects`, `members`, `enforcedSubjects`). * * Splitting the two meanings apart makes the wrong-form grant name a namespace * that has no `users` relation at all, rather than looking plausible. It also * lets the broker's downward walk use the right namespace per level instead of * guessing `Agent` for every level — the latent inconsistency that would become * a real bug the moment nested sub-agents ship. */ export declare const AGENT_NODE_NAMESPACE = "AgentNode"; /** Sub-agent counterpart of {@link AGENT_NODE_NAMESPACE}. */ export declare const SUBAGENT_NODE_NAMESPACE = "SubAgentNode"; /** * Namespace for the shared role a project grants tools to, rather than granting * every identity directly (`AgentRole:agent-security-users#members@(User:)`, * with each tool granted to `(AgentRole:agent-security-users#members)`). Written * by the Ory Console and the Agent Security broker's enrollment; the plugin only * ever resolves it as part of the `use` permit. * * Its own namespace because a role is not a tool. That is also what makes **one** * role tuple enough: while the role was a tool object, a membership had to be * written once per tool namespace (Keto matches a subject-set exactly, and the * role object's namespace had to be the same as the granting object's), so the * same role existed twice and could drift. */ export declare const AGENT_ROLE_NAMESPACE = "AgentRole"; /** Group of registered agent credentials sharing a harness key. */ export declare const AGENT_TYPE_NAMESPACE = "AgentType"; /** Group of registered sub-agent credentials sharing a type name. */ export declare const SUBAGENT_TYPE_NAMESPACE = "SubAgentType"; /** The relation naming a role's members. Reads as `User:bob is in members of AgentRole:`. */ export declare const RELATION_MEMBERS = "members"; /** * Namespace for Agent Security enrollment settings — currently whether a newly * connected agent's identity is auto-enrolled into the shared role. Stored as an * **opt-out** (`Enrollment:autoEnroll#optedOutSubjects@(Enrollment:project#optedOutSubjects)`) * so absence means on, which is what a project provisioned before the setting * existed reads as. Written and read by the Console and the broker. */ export declare const ENROLLMENT_NAMESPACE = "Enrollment"; /** The object the auto-enroll setting hangs off. */ export declare const AUTO_ENROLL_OBJECT = "autoEnroll"; /** The relation holding the subjects that opted out of a setting. */ export declare const RELATION_OPTED_OUT_SUBJECTS = "optedOutSubjects"; /** * Namespace for the server-read deny posture. Its own namespace for the same * reason as {@link DELEGATION_NAMESPACE}: the posture anchor is not a tool, and * once it stops sharing a namespace with tool names it no longer needs a * collision-proof sentinel spelling either — hence the plain * {@link PERMISSION_MODE_OBJECT} / {@link PROJECT_POSTURE_OBJECT} object ids * rather than the double-underscore guards an earlier release used. */ export declare const PERMISSION_MODE_NAMESPACE = "PermissionMode"; /** * The object an enforce-mode grant hangs off. The permission mode is resolved as * `checkPermission(PermissionMode:mode#enforcedSubjects@)`. */ export declare const PERMISSION_MODE_OBJECT = "mode"; /** * Object standing in for **every principal on the project**, used as the subject * of a project-wide posture grant: * * PermissionMode:mode#enforcedSubjects@(PermissionMode:project#enforcedSubjects) * * Keto has no wildcard subject, and a per-principal grant cannot cover a * principal that does not exist yet — so an agent connecting after an admin * turned on enforce would resolve `observe` and silently escape enforcement * until someone re-applied the grant for it. Checking this fixed sentinel first * makes the posture a property of the project: one relation governs every * principal, present and future. Per-principal grants remain meaningful as * exceptions (enforce one user while the project is still observing). */ export declare const PROJECT_POSTURE_OBJECT = "project"; /** * The native-tool permit Argus checks: `!blockedSubjects`, evaluated by Keto. */ export declare const PERMIT_USE = "use"; /** * The module OPL sources import their type symbols from. Keto's parser * recognizes this name (and the equivalent `@ory/permission-namespace-types`) * and strips the import — no npm dependency is pulled at runtime. */ export declare const OPL_IMPORT_MODULE = "@ory/keto-namespace-types"; /** * Every type symbol the rendered OPL references, and therefore must import: * `Namespace` (every class implements it), `SubjectSet` (the relation unions * allow set expansion), and `Context` (the `use` permit is typed * `(ctx: Context)`). This is the canonical set — any code that assembles or * merges the OPL imports exactly these. */ export declare const OPL_IMPORT_SYMBOLS: readonly ["Namespace", "SubjectSet", "Context"]; /** * The principal namespaces every subject is addressed under (SubjectSets): * `User`, `Agent`, `SubAgent`, and the `Session` fallback. The object * namespaces' relations are typed by them, so they must be defined for checks to * resolve rather than `NotFound`. * * These are **identities** — things that authenticate and that an admin can * grant or block. Graph-node references are a separate kind and live in * {@link NODE_REFERENCE_NAMESPACES}; keeping the two lists apart is what stops a * node id from being written where an identity belongs (#225). */ export declare const PRINCIPAL_NAMESPACES: readonly ["User", "Agent", "SubAgent", "Session"]; /** * Namespaces holding references to delegation-graph nodes. Declared so the * `delegates` relation that is typed by them resolves, and deliberately *not* * part of {@link PRINCIPAL_NAMESPACES}: a node reference is never a grant, block, * role-membership, or posture subject. */ export declare const NODE_REFERENCE_NAMESPACES: readonly ["AgentNode", "SubAgentNode"]; /** Render the canonical import line for a from-scratch OPL file. */ export declare function renderOplImport(): string; /** Render a principal-namespace class (an empty `implements Namespace`). */ export declare function renderPrincipalClass(name: string): string; /** * Render a native tool-namespace class body: the `blockedSubjects` relation and * the default-allow `use` permit Argus checks. The single definition is used for * both AgentTool and ShellTool; MCP namespaces remain grant-based elsewhere. */ export declare function renderToolNamespaceClass(namespace?: string): string; /** * Render the delegation-node class: who delegated to this node, and which OAuth2 * clients it ran as. Nothing is ever checked here — the graph is an audit trail — * but the relations must be declared for the broker's writes to be accepted. */ export declare function renderDelegationNamespaceClass(namespace?: string): string; /** * Render the role class: a reusable group whose members can be blocked together. */ export declare function renderAgentRoleNamespaceClass(namespace?: string): string; /** Render a harness/sub-agent type group whose members inherit posture. */ export declare function renderAgentTypeNamespaceClass(namespace: string, memberNamespace: string): string; /** * Render the enrollment-settings class: which subjects opted out of a setting. * An opt-out rather than an opt-in, so absence reads as "on". */ export declare function renderEnrollmentNamespaceClass(namespace?: string): string; /** * Render the permission-mode class: the set of subjects an admin has promoted * from `observe` to `enforce`. One relation, read by every gate. */ export declare function renderPermissionModeNamespaceClass(namespace?: string): string; /** * Render the complete OPL namespace-definition file for the Agent Security * model. * * This is the model an Ory project must have for Agent Security checks to * resolve. On a hosted project it is applied in the Ory Console (Agent * Security) — the plugin never pushes it. This renderer exists so the **local * dev stack** can mount the identical definition into its own Keto config, * keeping local and hosted on one definition, and so the model can be printed * for reference. * * `namespace` is the tool namespace (OPL class name); defaults to * {@link DEFAULT_NAMESPACE}. Operators who override `ORY_PERMISSION_NAMESPACE` * must provision the schema under the same name so the class the checks address * exists. The `Delegation` and `PermissionMode` namespaces are not * operator-configurable — they hold project-wide state, not per-deployment tool * objects. * * The output is the TypeScript-flavored OPL source Keto parses (the import is * recognized and stripped by Keto's parser — no npm dependency is pulled at * runtime). It is written verbatim into the local stack's keto config mount. */ export declare function buildAgentSecurityOpl(namespace?: string, shellNamespace?: string): string;