SpiceboxToolkit

Group agentprimitives.authzed.com · Scope Cluster · Short names sbxtk

SpiceboxToolkit is the machine-readable description of one CLI a sandbox tool can be built from: its subcommands, flags, env, parser config and default per-tool authz policy. A SpiceboxToolspec narrows a toolkit down to the specific tool an agent is actually handed.

Cluster-scoped. Reconciled by pkg/controllers/spiceboxtoolkit, which detects collisions with the built-in toolkits and stamps a cli-kind pin baseline onto status.

Spec

FieldTypeDescription
spec.docsstringDocs is prose about the CLI, surfaced to authors.
spec.env *objectEnv describes the environment variables the binary reads. It documents and wires them — notably which are sensitive — and is not a filter on the environment the binary actually receives. Mirrors pkg/tools/toolspec/toolkit.Toolkit.Env.
spec.env.allowed *[]objectAllowed DESCRIBES the environment variables the binary reads. It is not an enforcement boundary: nothing strips a variable for being absent here. The environment a tool call runs with is composed by the ToolCall controller from the broker-resolved credentials, ToolCall.spec.env, SpiceboxSession.spec.defaultEnv, this toolkit's envDefaults and the sandbox container's own env. What an entry drives is keyed off Sensitive: redaction wherever the call is surfaced, which credential a setup flow must obtain, and which names a SpiceboxClass may not shadow through its own envDefaults. Adding a variable here documents and wires it; omitting one does not keep it out of the binary's environment.
spec.env.allowed[].credentialstringCredential is the name of the credential (in the agent's identity catalog) that fills this env var. When empty, resolution falls back to a name derived from the env var (lowercased, "_"→"-"). Only meaningful when Sensitive is true.
spec.env.allowed[].descriptionstringDescription is the user-facing explanation of what this variable carries.
spec.env.allowed[].name *stringName is the environment variable name.
spec.env.allowed[].promptstringPrompt is free-text the LLM-fallback setup agent uses as system context when no provider matches. Optional.
spec.env.allowed[].providerstringProvider names a provider in the /providers/ library that satisfies this env's auth needs. Used by oap agent setup-identity to dispatch the right setup flow. Optional.
spec.env.allowed[].sensitivebooleanSensitive marks the value as credential material: it is resolved from an identity, masked in output, and never logged.
spec.env.allowed[].titlestringTitle is the short user-facing display name for the credential this env var carries ("GitHub"). Shown as the bold header of a credential-request row. Takes precedence over the provider catalog; when empty the catalog (via Provider) or a humanized credential name is used. Only meaningful when Sensitive is true.
spec.envDefaultsmap[string]stringEnvDefaults is static, non-secret environment this toolkit sets on every invocation of its binary, so a CLI's own hardening travels with the description of that CLI. It is a floor: the runner stamps it onto each ToolCall's spec.env, and the toolcall controller demotes any key still carrying this declared value below SpiceboxSession.spec.defaultEnv and SpiceboxClass.spec.envDefaults, so an operator setting the same key keeps winning. Values are never secret. A key that Env.Allowed marks sensitive is rejected: those come from an AgentIdentity credential, and a static default for the same key would either fail the call (ToolCallEnvShadowsAgent) or replace the credential with a fixed string. Mirrors pkg/tools/toolspec/toolkit.Toolkit.EnvDefaults.
spec.globalFlags[]objectGlobalFlags are flags accepted before any subcommand.
spec.globalFlags[].descriptionstringDescription is the user-facing explanation of the flag.
spec.globalFlags[].long *stringLong is the flag's long form, without leading dashes.
spec.globalFlags[].optionalValuebooleanOptionalValue declares that the binary accepts this flag's value only in the attached --flag=value form (git's PARSE_OPT_OPTARG, pflag's NoOptDefVal), so the parser must not consume the following token as the value — the binary won't. Mirrors pkg/tools/toolspec/toolkit.Flag.OptionalValue; invalid on type "bool".
spec.globalFlags[].sensitivebooleanSensitive keeps the flag's value out of logs and approval prompts.
spec.globalFlags[].shortstringShort is the single-letter form, without its dash; empty if none.
spec.globalFlags[].splitOnstringSplitOn, when non-empty, splits one supplied value into repeated flag occurrences on this separator.
spec.globalFlags[].type *stringType is the value type; "bool" means the flag takes no value.
spec.globalFlags[].values[]stringValues, when non-empty, restricts the flag to this enumeration.
spec.name *stringName is the toolkit identifier a SpiceboxToolspec references.
spec.parser *objectParser selects how an invocation's argv is validated and bound.
spec.parser.kind *stringKind selects the argv parser: declarative drives off this spec, builtin names a compiled-in parser. (enum: declarative | builtin)
spec.parser.namestringName identifies the builtin parser; unused when Kind is declarative.
spec.permissionobjectPermission is the toolkit-wide default per-tool authz policy. Subcommands may override individually via their own Permission block. Mirrors pkg/tools/toolspec/toolkit.Toolkit.Permission.
spec.permission.checkobjectPermissionCheck names the SpiceDB resource and permission to evaluate. Required when StateImpact is Readonly / Readwrite / External; forbidden when Stateless / Passthrough.
spec.permission.check.enforceModestringEnforceMode controls deny-finality under permissive toolAuthMode. Empty → EnforceInherit (slice-1 default).
spec.permission.check.extractionPromptstringExtractionPrompt, when set, is an English fragment the runner includes in the extraction LLM's system prompt for this resource type. Tool spec authors write this once per toolspec/MCPServer file. The runner aggregates ExtractionPrompts across all tools whose Check targets the same resourceType (dedup'd, concatenated). The AgentClass's BoundEntityType.ExtractionPrompt overrides entirely if set.
spec.permission.check.grantBindsArgs[]stringGrantBindsArgs, when set, restricts the slice-2 grant's arguments_hash caveat binding to only the listed top-level arg keys. Default (nil/empty) hashes the full arg map, meaning the grant satisfies only the exact same call. Setting it to e.g. ["repo"] lets a single approval cover every subsequent call against the same repo regardless of other args (pr number, etc.) — useful for readonly tools where the resource is the load-bearing input. The hash is an HMAC-SHA256 under the per-session args-hash key minted by the AgentSession reconciler into the per-session Secret and read by the runner at startup. Caveat contexts therefore cannot be predicted or minted outside the controller+runner trust domain — neither the agent nor a confused deputy with SpiceDB write access can forge a valid binding for arguments that were never requested. Note: hashes of previously-requested (including denied) calls remain observable in session status and channel payloads, so a SpiceDB-write attacker could still replay those; the keying narrows that surface to exactly the calls a human has already seen. See pkg/authz/guardian/grants.ArgsHashFiltered for the filtering contract. NOTE: the hash no longer BINDS the grant. A slot grant is keyed on (instance, permission), which is what makes it safe to reuse across calls: a grant shaped for one permission cannot be spent on another against the same id. The filtered hash is still computed and published on the approval request (ToolApprovalDetails.ArgsHash) so an approver — and the audit log — can see exactly which call was asked about.
spec.permission.check.permission *stringPermission is the SpiceDB permission (or relation) name to Check (e.g. "read", "write", "admin"). Static. Pattern-validated at admission for the same reason as ResourceType. Mirrors BoundEntityType.Permission.
spec.permission.check.resourceIDExprstringResourceIDExpr is a CEL string expression evaluated against args that yields the SpiceDB resource id. Exactly one of ResourceIDTemplate or ResourceIDExpr must be set: the template form for simple {arg} interpolation, the Expr form for nested-arg extraction.
spec.permission.check.resourceIDHintstringResourceIDHint is shown to the AGENT when the resource id cannot be resolved from the call's arguments. That failure is usually the caller's to fix — it named no resource, or named it in a form the check cannot read — and the agent is the only party who can retry. Without a hint the message is the raw resolution error prefixed "internal:", which reads as a system fault and tells it not to bother. Written by whoever authors the check, because only they know what the call should have looked like. Example, for a git push whose remote must be a URL so the repository can be authorized: "name the remote as a full https:// URL, not a shorthand like origin".
spec.permission.check.resourceIDTemplatestringResourceIDTemplate is a string with {arg} placeholders that interpolate from the tool call's args (e.g. "{owner}/{repo}"). Resolved at runtime by ResolveTemplate; validated at AgentClass-reconcile time so every {arg} matches a parameter the tool declares.
spec.permission.check.resourceIDTransforms[]stringResourceIDTransforms names registered transforms applied IN ORDER to the resolved template string before SpiceDB sees it: lowercase, remove_spaces, spicedb_object_id, basename, sha256. See transforms.go.
spec.permission.check.resourceType *stringResourceType is the SpiceDB definition the Check runs against (e.g. "github_repo", "channel"). Static — no template interpolation. Pattern-validated at admission because this value is a component of a permsurface.Handle, which appears in approved plan ceilings and audit records. Mirrors BoundEntityType.ResourceType.
spec.permission.stateImpact *stringStateImpact describes the policy mode for a tool / subcommand.
spec.permission.toolNamestringToolName lets Layer 2's CheckScope evaluate the tool allow/deny axis. Wire from the registered tool name. Empty string skips the Layer 2 tool check (existing behavior).
spec.siteURLstringSiteURL is the toolkit-backed service's user-facing homepage. identityd discovers a favicon from this URL for the credential rows backed by this toolkit's sensitive env vars (e.g., a github-token row resolves through the github toolkit). Optional; unset renders the deterministic generated fallback icon. Must be http or https; admission rejects other schemes.
spec.spicedbSchemaobjectSpiceDBSchema declares the SpiceDB resource types this toolkit's permission checks name, so the toolkit ships the definitions its own checks depend on. Mirrors pkg/toolspec/toolkit.Toolkit.SpiceDBSchema; the guardian composes it alongside MCPServer and SpiceDBBootstrap fragments.
spec.spicedbSchema.rawZedstringRawZed is appended verbatim to the composed schema after Resources are emitted. Use this for SpiceDB schema features the structured form can't represent: subject-relations (e.g. slack_user#user) and unions (e.g. user | agent). The composer does not parse or validate RawZed beyond schema-load time; malformed text fails the SpiceDB WriteSchema call.
spec.spicedbSchema.resources[]objectResources lists structured SpiceDB definitions contributed by this fragment. Each resource is emitted with its declared relations and permissions; the composer dedupes by name across fragments.
spec.spicedbSchema.resources[].approverPermissionstringApproverPermission names the permission an approver must hold on an instance for their approval to count. Required when Standing is required, and must be empty when it is session-only — a type nothing governs has no permission to name, and accepting one there would read as governance that is never consulted. Declared rather than assumed. This was hardcoded to "owner" at four call sites, which silently made two very different types look identical to the approval router: crm_company's owner is a real computed permission backed by seeded tuples, while git_repo's owner is a bare relation declared only so the checks are answerable and never populated by anything. The router could not tell governance from an artifact of schema shape, so a push to a git remote resolved to an empty owner-set and became unapprovable forever. Naming it also lifts the assumption that the approving permission is called "owner": a type may route approval through maintainer, admin, or any permission its own schema defines.
spec.spicedbSchema.resources[].displayobjectDisplay declares how an INSTANCE of this resource type presents on an approval card — an icon, and how to turn its raw value (a URL, typically) into a short label. Absent means the card falls back to the wire type name, exactly as before this field existed.
spec.spicedbSchema.resources[].display.iconstringIcon names a glyph from a CLOSED, code-defined registry — never a URL and never free-form. A vendor-specific type (github_repo) may name the vendor's own mark; a host-agnostic type (git_repo) must name a generic one, because claiming a vendor mark would lie the moment an instance points somewhere else (a self-hosted remote). An unrecognized name renders no icon — never a fallback image, never a guess.
spec.spicedbSchema.resources[].display.labelstringLabel names a deriver from a CLOSED, code-defined set that turns an instance's raw value into a short display label — "url_path", "b64url_path", "last_segment", or "none" (the set registered in pkg/authz/plangate's labelDerivers, which is the authority; this list is prose and has drifted from it once). Not a template and not a regex: nothing here can produce a label the code did not author, and the deriver never sees anything an agent did not itself write into the plan (the instance value), so a declaration can shorten a value's presentation but can never fabricate one. An unrecognized name derives nothing, and the card falls back to Name.
spec.spicedbSchema.resources[].display.namestringName is the human name for this resource TYPE — "Git repository", never the wire handle "git_repo". Shown when no per-instance label can be derived (Label is "none", unset, or the deriver produces nothing for this instance's value).
spec.spicedbSchema.resources[].name *stringName is the SpiceDB definition name; the composer dedupes on it.
spec.spicedbSchema.resources[].permissions[]objectPermissions are the resource's computed permissions.
spec.spicedbSchema.resources[].permissions[].expr *stringExpr is the right-hand side of permission <name> = …, pasted verbatim.
spec.spicedbSchema.resources[].permissions[].name *stringName is the permission name.
spec.spicedbSchema.resources[].permissions[].planningNotestringPlanningNote is one line of guidance the agent reads while DECLARING a plan, rendered next to the handles it may declare. The knowledge that shapes a good plan is toolkit-specific, so it lives with the toolkit rather than in the runner's generic prompt. git_repo splits its permissions across two instances — read/write key on the checked-out copy, fetch/push on the remote URL — so a phase declaring write without read can change files it cannot open, and the first read interrupts the human with an amendment the plan should have carried from the start. It shapes what the agent DECLARES, never what anything grants: a note is prose the model reads at plan time, and no authorization decision reads it. A phase that ignores its guidance still gets the gate it earned.
spec.spicedbSchema.resources[].permissions[].titlestringTitle is the phrase a human reads on an approval card instead of the permission's handle. perm:push:git_repo is a WIRE FORMAT; asking somebody to decide on it makes the decision slower and worse exactly where care matters most. Declared here because this is where the permission itself is declared, so one declaration serves every channel and the CLI. Absent is fine and common: the card detokenizes the handle instead, which shows no plumbing and invents no English.
spec.spicedbSchema.resources[].relations[]objectRelations are the resource's relations, emitted verbatim.
spec.spicedbSchema.resources[].relations[].name *stringName is the relation name as it appears in the composed schema.
spec.spicedbSchema.resources[].relations[].subjectType *stringSubjectType must be a bare resource-type name (e.g. "user", "hubspot_owner") declared in this same SpiceDBSchema or the implicit "user" type. Wildcards ("user:*") are expressed via the separate Wildcard field. Subject-relation forms ("team#member") are NOT representable. Exactly one SubjectType per Relation — SpiceDB unions like relation viewer: user | team#member cannot be modeled.
spec.spicedbSchema.resources[].relations[].wildcardbooleanWildcard, when true, makes the relation accept ANY subject of SubjectType — emitted as relation <name>: <subjectType>:* in the composed schema. Use sparingly: a wildcard relation effectively grants the underlying-resource permission to every subject of that type, so any per-call gating must come from a different layer (e.g. stateImpact: external on the tool, which routes every call through the approval flow regardless of the SpiceDB Check result).
spec.spicedbSchema.resources[].standing *stringStanding declares whether SpiceDB is AUTHORITATIVE for this resource type — that is, whether an approver can be expected to already hold a permission on an instance of it. REQUIRED, with no default. - required SpiceDB governs who may approve. The approver pool is ApproverPermission on the named instance, and an EMPTY pool is a final refusal — nobody can approve what nobody governs. - session-only No local permission governs approval for this type, so the session's own approvers decide and their decision IS the authority. Everything downstream is unchanged: the grant is still written, still expiring, still session-scoped and revocable, and the tool-call Check still runs. There is deliberately NO default. A default is a hole you open by omission: defaulting to session-only silently widens who may approve a type somebody forgot to classify, and defaulting to required makes a forge-governed type permanently unbindable because nothing writes a SpiceDB tuple for every git remote. Neither failure announces itself, so the author states the answer. Same reasoning as AP_CLUSTER_KIND, which also refuses to default. Choosing is a question about the RESOURCE, not about convenience: does a permission on this instance already say who may speak for it? A CRM company with seeded owner tuples: yes, required. A git remote whose permissions live at the forge: no, session-only. A cluster or namespace admin can force any type to required with SettingsLimits.RequireStandingFor, which no fragment may widen past. (enum: session-only | required)
spec.streamFormatstringStreamFormat names a registered parser in pkg/tools/toolkitstream/registry. When non-empty AND a subcommand has Mode stream or interactive, the sandbox tool routes the bridge's stdout through the named parser before publishing KindToolSessionEvent envelopes. Empty (default) preserves the raw KindToolSessionDelta path. Mirrors pkg/tools/toolspec/toolkit.Toolkit.StreamFormat. Example: "claude-stream-json".
spec.subcommands *[]objectSubcommands are the invocations this toolkit describes; anything absent here is not expressible as a tool.
spec.subcommands[].descriptionstringDescription is the user-facing explanation of what the subcommand does.
spec.subcommands[].effects *objectEffects is the declared effect profile driving approval and authz gating.
spec.subcommands[].effects.creds *objectCreds is which credentials the invocation needs or rewrites.
spec.subcommands[].effects.creds.required *[]stringRequired names credentials the invocation needs to succeed.
spec.subcommands[].effects.creds.writes *[]stringWrites names credentials the invocation may overwrite.
spec.subcommands[].effects.destructive *booleanDestructive means the invocation can remove or overwrite state.
spec.subcommands[].effects.filesystem *objectFilesystem is what the invocation may touch on disk.
spec.subcommands[].effects.filesystem.paths *[]stringPaths names the filesystem locations the invocation touches.
spec.subcommands[].effects.network *objectNetwork is where the invocation may reach.
spec.subcommands[].effects.network.destinations *[]stringDestinations names the hosts the invocation contacts; empty means none.
spec.subcommands[].effects.reads *[]stringReads names the resource kinds the invocation reads.
spec.subcommands[].effects.writes *[]stringWrites names the resource kinds the invocation mutates.
spec.subcommands[].flags[]objectFlags are the flags this subcommand accepts beyond the global ones.
spec.subcommands[].flags[].descriptionstringDescription is the user-facing explanation of the flag.
spec.subcommands[].flags[].long *stringLong is the flag's long form, without leading dashes.
spec.subcommands[].flags[].optionalValuebooleanOptionalValue declares that the binary accepts this flag's value only in the attached --flag=value form (git's PARSE_OPT_OPTARG, pflag's NoOptDefVal), so the parser must not consume the following token as the value — the binary won't. Mirrors pkg/tools/toolspec/toolkit.Flag.OptionalValue; invalid on type "bool".
spec.subcommands[].flags[].sensitivebooleanSensitive keeps the flag's value out of logs and approval prompts.
spec.subcommands[].flags[].shortstringShort is the single-letter form, without its dash; empty if none.
spec.subcommands[].flags[].splitOnstringSplitOn, when non-empty, splits one supplied value into repeated flag occurrences on this separator.
spec.subcommands[].flags[].type *stringType is the value type; "bool" means the flag takes no value.
spec.subcommands[].flags[].values[]stringValues, when non-empty, restricts the flag to this enumeration.
spec.subcommands[].modestringMode declares how the runner dispatches this subcommand. Mirrors pkg/tools/toolspec/toolkit.Subcommand.Mode. Values: "" (sync), "stream" (streaming output, no stdin bridge), "interactive" (streaming output + live stdin pipe).
spec.subcommands[].path *[]stringPath is the subcommand words after the binary, e.g. ["remote","add"].
spec.subcommands[].permissionobjectPermission overrides the toolkit-level default permission for this subcommand. Required by the AgentClass validator's "enforcing" mode when Effects.Destructive or Effects.Writes flag the subcommand as state-mutating. Mirrors pkg/tools/toolspec/toolkit.Subcommand.Permission.
spec.subcommands[].permission.checkobjectPermissionCheck names the SpiceDB resource and permission to evaluate. Required when StateImpact is Readonly / Readwrite / External; forbidden when Stateless / Passthrough.
spec.subcommands[].permission.check.enforceModestringEnforceMode controls deny-finality under permissive toolAuthMode. Empty → EnforceInherit (slice-1 default).
spec.subcommands[].permission.check.extractionPromptstringExtractionPrompt, when set, is an English fragment the runner includes in the extraction LLM's system prompt for this resource type. Tool spec authors write this once per toolspec/MCPServer file. The runner aggregates ExtractionPrompts across all tools whose Check targets the same resourceType (dedup'd, concatenated). The AgentClass's BoundEntityType.ExtractionPrompt overrides entirely if set.
spec.subcommands[].permission.check.grantBindsArgs[]stringGrantBindsArgs, when set, restricts the slice-2 grant's arguments_hash caveat binding to only the listed top-level arg keys. Default (nil/empty) hashes the full arg map, meaning the grant satisfies only the exact same call. Setting it to e.g. ["repo"] lets a single approval cover every subsequent call against the same repo regardless of other args (pr number, etc.) — useful for readonly tools where the resource is the load-bearing input. The hash is an HMAC-SHA256 under the per-session args-hash key minted by the AgentSession reconciler into the per-session Secret and read by the runner at startup. Caveat contexts therefore cannot be predicted or minted outside the controller+runner trust domain — neither the agent nor a confused deputy with SpiceDB write access can forge a valid binding for arguments that were never requested. Note: hashes of previously-requested (including denied) calls remain observable in session status and channel payloads, so a SpiceDB-write attacker could still replay those; the keying narrows that surface to exactly the calls a human has already seen. See pkg/authz/guardian/grants.ArgsHashFiltered for the filtering contract. NOTE: the hash no longer BINDS the grant. A slot grant is keyed on (instance, permission), which is what makes it safe to reuse across calls: a grant shaped for one permission cannot be spent on another against the same id. The filtered hash is still computed and published on the approval request (ToolApprovalDetails.ArgsHash) so an approver — and the audit log — can see exactly which call was asked about.
spec.subcommands[].permission.check.permission *stringPermission is the SpiceDB permission (or relation) name to Check (e.g. "read", "write", "admin"). Static. Pattern-validated at admission for the same reason as ResourceType. Mirrors BoundEntityType.Permission.
spec.subcommands[].permission.check.resourceIDExprstringResourceIDExpr is a CEL string expression evaluated against args that yields the SpiceDB resource id. Exactly one of ResourceIDTemplate or ResourceIDExpr must be set: the template form for simple {arg} interpolation, the Expr form for nested-arg extraction.
spec.subcommands[].permission.check.resourceIDHintstringResourceIDHint is shown to the AGENT when the resource id cannot be resolved from the call's arguments. That failure is usually the caller's to fix — it named no resource, or named it in a form the check cannot read — and the agent is the only party who can retry. Without a hint the message is the raw resolution error prefixed "internal:", which reads as a system fault and tells it not to bother. Written by whoever authors the check, because only they know what the call should have looked like. Example, for a git push whose remote must be a URL so the repository can be authorized: "name the remote as a full https:// URL, not a shorthand like origin".
spec.subcommands[].permission.check.resourceIDTemplatestringResourceIDTemplate is a string with {arg} placeholders that interpolate from the tool call's args (e.g. "{owner}/{repo}"). Resolved at runtime by ResolveTemplate; validated at AgentClass-reconcile time so every {arg} matches a parameter the tool declares.
spec.subcommands[].permission.check.resourceIDTransforms[]stringResourceIDTransforms names registered transforms applied IN ORDER to the resolved template string before SpiceDB sees it: lowercase, remove_spaces, spicedb_object_id, basename, sha256. See transforms.go.
spec.subcommands[].permission.check.resourceType *stringResourceType is the SpiceDB definition the Check runs against (e.g. "github_repo", "channel"). Static — no template interpolation. Pattern-validated at admission because this value is a component of a permsurface.Handle, which appears in approved plan ceilings and audit records. Mirrors BoundEntityType.ResourceType.
spec.subcommands[].permission.stateImpact *stringStateImpact describes the policy mode for a tool / subcommand.
spec.subcommands[].permission.toolNamestringToolName lets Layer 2's CheckScope evaluate the tool allow/deny axis. Wire from the registered tool name. Empty string skips the Layer 2 tool check (existing behavior).
spec.subcommands[].permissionVariants[]objectPermissionVariants branch this subcommand's authority on its ARGUMENTS: CEL When over the parsed args, first match wins, Permission is the fallback. Mirrors pkg/toolspec/toolkit.Subcommand.PermissionVariants and matches MCPServerTool.PermissionVariants exactly — the same idea should not grow a second vocabulary.
spec.subcommands[].permissionVariants[].check *objectCheck is the Permission applied when When matches. Same shape as the slice-1 singular Permission's Check, plus the new EnforceMode and ResourceIDExpr fields.
spec.subcommands[].permissionVariants[].check.checkobjectPermissionCheck names the SpiceDB resource and permission to evaluate. Required when StateImpact is Readonly / Readwrite / External; forbidden when Stateless / Passthrough.
spec.subcommands[].permissionVariants[].check.check.enforceModestringEnforceMode controls deny-finality under permissive toolAuthMode. Empty → EnforceInherit (slice-1 default).
spec.subcommands[].permissionVariants[].check.check.extractionPromptstringExtractionPrompt, when set, is an English fragment the runner includes in the extraction LLM's system prompt for this resource type. Tool spec authors write this once per toolspec/MCPServer file. The runner aggregates ExtractionPrompts across all tools whose Check targets the same resourceType (dedup'd, concatenated). The AgentClass's BoundEntityType.ExtractionPrompt overrides entirely if set.
spec.subcommands[].permissionVariants[].check.check.grantBindsArgs[]stringGrantBindsArgs, when set, restricts the slice-2 grant's arguments_hash caveat binding to only the listed top-level arg keys. Default (nil/empty) hashes the full arg map, meaning the grant satisfies only the exact same call. Setting it to e.g. ["repo"] lets a single approval cover every subsequent call against the same repo regardless of other args (pr number, etc.) — useful for readonly tools where the resource is the load-bearing input. The hash is an HMAC-SHA256 under the per-session args-hash key minted by the AgentSession reconciler into the per-session Secret and read by the runner at startup. Caveat contexts therefore cannot be predicted or minted outside the controller+runner trust domain — neither the agent nor a confused deputy with SpiceDB write access can forge a valid binding for arguments that were never requested. Note: hashes of previously-requested (including denied) calls remain observable in session status and channel payloads, so a SpiceDB-write attacker could still replay those; the keying narrows that surface to exactly the calls a human has already seen. See pkg/authz/guardian/grants.ArgsHashFiltered for the filtering contract. NOTE: the hash no longer BINDS the grant. A slot grant is keyed on (instance, permission), which is what makes it safe to reuse across calls: a grant shaped for one permission cannot be spent on another against the same id. The filtered hash is still computed and published on the approval request (ToolApprovalDetails.ArgsHash) so an approver — and the audit log — can see exactly which call was asked about.
spec.subcommands[].permissionVariants[].check.check.permission *stringPermission is the SpiceDB permission (or relation) name to Check (e.g. "read", "write", "admin"). Static. Pattern-validated at admission for the same reason as ResourceType. Mirrors BoundEntityType.Permission.
spec.subcommands[].permissionVariants[].check.check.resourceIDExprstringResourceIDExpr is a CEL string expression evaluated against args that yields the SpiceDB resource id. Exactly one of ResourceIDTemplate or ResourceIDExpr must be set: the template form for simple {arg} interpolation, the Expr form for nested-arg extraction.
spec.subcommands[].permissionVariants[].check.check.resourceIDHintstringResourceIDHint is shown to the AGENT when the resource id cannot be resolved from the call's arguments. That failure is usually the caller's to fix — it named no resource, or named it in a form the check cannot read — and the agent is the only party who can retry. Without a hint the message is the raw resolution error prefixed "internal:", which reads as a system fault and tells it not to bother. Written by whoever authors the check, because only they know what the call should have looked like. Example, for a git push whose remote must be a URL so the repository can be authorized: "name the remote as a full https:// URL, not a shorthand like origin".
spec.subcommands[].permissionVariants[].check.check.resourceIDTemplatestringResourceIDTemplate is a string with {arg} placeholders that interpolate from the tool call's args (e.g. "{owner}/{repo}"). Resolved at runtime by ResolveTemplate; validated at AgentClass-reconcile time so every {arg} matches a parameter the tool declares.
spec.subcommands[].permissionVariants[].check.check.resourceIDTransforms[]stringResourceIDTransforms names registered transforms applied IN ORDER to the resolved template string before SpiceDB sees it: lowercase, remove_spaces, spicedb_object_id, basename, sha256. See transforms.go.
spec.subcommands[].permissionVariants[].check.check.resourceType *stringResourceType is the SpiceDB definition the Check runs against (e.g. "github_repo", "channel"). Static — no template interpolation. Pattern-validated at admission because this value is a component of a permsurface.Handle, which appears in approved plan ceilings and audit records. Mirrors BoundEntityType.ResourceType.
spec.subcommands[].permissionVariants[].check.stateImpact *stringStateImpact describes the policy mode for a tool / subcommand.
spec.subcommands[].permissionVariants[].check.toolNamestringToolName lets Layer 2's CheckScope evaluate the tool allow/deny axis. Wire from the registered tool name. Empty string skips the Layer 2 tool check (existing behavior).
spec.subcommands[].permissionVariants[].when *stringWhen is a CEL boolean expression with args in scope.
spec.subcommands[].positional[]objectPositional declares the subcommand's positional arguments, in order.
spec.subcommands[].positional[].afterDashDashbooleanAfterDashDash marks this slot as where post--- arguments start binding, for a CLI that overloads -- as a separator rather than a plain option terminator (git's log [<rev>] [-- <path>…]). Mirrors pkg/tools/toolspec/toolkit.Positional.AfterDashDash; at most one positional per subcommand may set it.
spec.subcommands[].positional[].name *stringName is the argument name the tool schema exposes to the agent.
spec.subcommands[].positional[].requiredbooleanRequired rejects an invocation that omits this argument.
spec.subcommands[].positional[].splitOnstringSplitOn, when non-empty, splits one supplied value into several argv entries on this separator.
spec.subcommands[].positional[].type *stringType is the value type used to validate and render the argument.
spec.subcommands[].positional[].values[]stringValues, when non-empty, restricts the argument to this enumeration.
spec.subcommands[].timeoutstringTimeout overrides the mode-derived per-call execution budget for this subcommand (a Go duration string, e.g. "10m" for a slow clone). Empty applies the mode default: 30m for stream/interactive, 5m for sync. Mirrors pkg/tools/toolspec/toolkit.Subcommand.Timeout; a non-parseable or non-positive value is rejected when the toolkit is loaded.
spec.target *objectTarget identifies the binary this toolkit drives.
spec.target.binary *stringBinary is the executable name as invoked inside the sandbox.
spec.target.pinnedBinaryHashstringPinnedBinaryHash, when set, asserts the sha256 of the toolkit binary ("sha256:…"). RECORDED, not verified: in-sandbox measurement needs a sandbox exec primitive that does not exist yet.
spec.target.versionProbeobjectVersionProbe is how to ask the binary its version; nil skips the check.
spec.target.versionProbe.args *[]stringArgs is the argv appended to the binary to make it print its version.
spec.target.versionProbe.extract *objectExtract is how to pull the version string out of that output.
spec.target.versionProbe.extract.kind *stringKind selects the extraction strategy applied to the probe's output. (enum: regex | json | line1)
spec.target.versionProbe.extract.patternstringPattern is the regex or JSON path Kind applies; unused for line1.
spec.target.versionRangestringVersionRange is the semver range the description is valid for; empty accepts any version.
spec.toolkitRevision *stringToolkitRevision is the description's own revision, bumped when the declared surface changes.
spec.versionstringVersion is the upstream CLI version this description was written against.
* required

Status

Status is controller-owned (observed state).

FieldTypeDescription
status.conditions[]objectConditions carries Valid; see SpiceboxToolkitConditionValid.
status.conditions[].lastTransitionTime *string (date-time)lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
status.conditions[].message *stringmessage is a human readable message indicating details about the transition. This may be an empty string.
status.conditions[].observedGenerationinteger (int64)observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. (min 0)
status.conditions[].reason *stringreason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.
status.conditions[].status *stringstatus of the condition, one of True, False, Unknown. (enum: True | False | Unknown)
status.conditions[].type *stringtype of condition in CamelCase or in foo.example.com/CamelCase.
status.observedGenerationinteger (int64)ObservedGeneration is the spec generation this status reflects.
status.pinobjectPin is the recorded pin baseline in the common PinRecord shape: Digest = pinnedBinaryHash assertion (if set), Version = versionRange (audit metadata). Strength reflects the declared ref: frozen iff PinnedBinaryHash is set, named iff VersionRange is set, else unpinned.
status.pin.detailsmap[string]stringDetails carries kind-specific extras (toolCount, registry host, …).
status.pin.digeststringDigest is the immutable identity: git sha, sha256:… image digest, canonical tool-manifest hash, or binary hash.
status.pin.kind *stringKind is the pinning registry kind name (skill, image, mcp, cli, …). Validated against the registry by controllers, not by a CRD enum, so new kinds register without an API change.
status.pin.observedAtstring (date-time)ObservedAt is when the recording controller observed this identity.
status.pin.strength *stringStrength is the syntactic pin strength of the declared ref. (enum: frozen | named | unpinned)
status.pin.versionstringVersion is the human-readable identity: tag, serverInfo.version, or version-probe output.
* required