Permission Model

How the agent-level Permission grant system gates agent-to-agent interaction and spend, how it differs from RBAC and from tool visibility, and how both combine with the outbound mediation seam to actually enforce data boundaries.

Two distinct systems share the word "permission" in this codebase — do not conflate them:

  • Permission (hoziron-core-types::types::Permission, this document) — an agent-level grant: what an agent may do to other agents and how much it may spend. Declared in agent configuration, enforced at runtime via translation to kernel Capability values.
  • Role/Action (hoziron-core-types::auth::permissions) — the API-level RBAC model (six hardcoded roles, a compile-time permission matrix) governing who may call which HTTP endpoint. See security-auth.md.

Permission Types

The Permission enum has exactly six variants — narrower than it might sound, because it covers only agent-interaction and spend-limit grants. There is no FileRead/FileWrite/NetworkConnect/ShellExec/MemoryRead/MemoryWrite variant in this enum; file, network, and shell access are not modeled as Hoziron Permission grants at all (see Suppressed Kernel Capabilities below for what the kernel's own capability space contains that Hoziron intentionally never exposes this way). AGENT.md/COMPETENCY.md frontmatter hard-rejects a permissions: key via deny_unknown_fields.

PermissionPatternControls
AgentSpawnCreating child agents
AgentMessage(pattern)agent name/ID globSending messages to other agents matching the pattern
AgentKill(pattern)agent name/ID globTerminating other agents matching the pattern
CostLimitHourly(amount)USDMaximum hourly LLM spend
CostLimitDaily(amount)USDMaximum daily LLM spend
CostLimitMonthly(amount)USDMaximum monthly LLM spend

Permission implements Display/FromStr with a category:pattern string form (e.g. "agent:message:*", "cost:hourly:10"), used for both JSON and compact string representations in manifests and API payloads.

Enforcement Points

Permissions are enforced at multiple layers:

1. Equip-Time Validation

When equipping a competency, every Permission value in its manifest's permissions field must exist (by exact equality) in the agent's own permission set (validate_equip_permissions, crates/platform/hoziron-core/src/permission/mod.rs):

Agent permissions:       [AgentMessage("claims-*"), CostLimitHourly(10.0)]
Competency requires:     [CostLimitHourly(10.0)]
Result:                  ✓ Satisfied → equip proceeds
Agent permissions:       [AgentMessage("claims-*")]
Competency requires:     [CostLimitHourly(10.0), AgentSpawn]
Result:                  ✗ Missing: CostLimitHourly(10.0), AgentSpawn

For pattern-carrying variants (AgentMessage, AgentKill), the pattern string itself must match exactly — AgentMessage("claims-*") does not satisfy a requirement of AgentMessage("*") or vice versa; there is no glob-subsumption check, only equality.

2. Runtime Tool Filtering (ADR-052)

At execution time, two independent per-class visibility lists control which tools appear in the available_tools offered to the LLM:

  • allow_skill_tools — narrows in-process (builtin + skill) tools. Empty/absent = full default.
  • allow_integration_tools — narrows MCP (integration/contract) tools. Empty/absent = full default.

Each list operates independently. Naming tools in one class never affects the other class's visibility. This is a visibility control — it shapes what the model is offered, not what is permitted to happen. It is explicitly not a security mechanism.

Semantics:

  • Empty / absent list = full default for that class (all tools from equipped competencies or resolved servers).
  • Non-empty list = the agent is offered exactly the named tools of that class.
  • allow_integration_tools is bounded by server-level resolution (ADR-050/051): only tools from resolved servers are nameable.

Example (FNOL pipeline):

# Validation agent — only needs claim.search_history from the claims MCP server
allow_integration_tools:
  - claim.search_history

# Resolution agent — needs create, assign, and set_status
allow_integration_tools:
  - claim.create
  - claim.assign
  - claim.set_status

At equip time, a named tool that no resolved server or installed skill provides emits a warning (likely authoring error).

Important: This is NOT the trust boundary. The core mediator (ADR-042/049) is the sole data-trust enforcement point. Tool visibility cannot be relied upon for access control — it only governs the offered list for agent effectiveness.

3. Kernel Capability Enforcement

The kernel enforces capabilities at the tool execution layer. Each tool declares what capabilities it requires, and the kernel validates the agent has them before executing.

4. Resource Quota Enforcement

Cost-limit permissions are enforced via the scheduler's resource tracking:

PermissionEnforcement
CostLimitHourly(50.0)Reject invocation if hourly token cost exceeds $50
CostLimitDaily(200.0)Reject if daily cost exceeds $200
CostLimitMonthly(1000.0)Reject if monthly cost exceeds $1000

The scheduler uses a rolling 1-hour window for token tracking. Tokens are converted to cost using per-model pricing.

Child Agent Permission Inheritance

When an agent spawns a child agent (via the AgentSpawn grant), the child's permissions must be a strict subset of the parent's:

This prevents privilege escalation — a child can never have more permissions than its parent.

Permission → Kernel Capability Mapping

Permission values are translated 1:1 (with one deliberate collapse) to the forked kernel's Capability enum for enforcement (crates/platform/hoziron-core/src/permission/mod.rs):

Hoziron PermissionKernel Capability
AgentSpawnCapability::AgentSpawn
AgentMessage(pattern)Capability::AgentMessage(pattern)
AgentKill(pattern)Capability::AgentKill(pattern)
CostLimitHourly(amount)Capability::EconSpend(amount)
CostLimitDaily(amount)Capability::EconSpend(amount) — also enforced via ResourceQuota
CostLimitMonthly(amount)Capability::EconSpend(amount) — also enforced via ResourceQuota

The reverse mapping (capability_to_permission) is necessarily lossy: EconSpend always maps back to CostLimitHourly (the capability system does not distinguish hourly/daily/monthly), and a defined set of kernel capabilities has no Hoziron Permission equivalent at all and is suppressed outright:

  • OfpDiscover, OfpConnect, OfpAdvertise — the kernel's internal peer-discovery protocol, never surfaced
  • EconEarn, EconTransfer — economic-system capabilities the platform does not expose
  • NetListen, ToolInvoke, ToolAll, LlmQuery, LlmMaxTokens, EnvRead — system-level grants with no Hoziron-level equivalent

There is no FileRead/FileWrite/NetworkConnect/MemoryRead/MemoryWrite/ShellExec capability in this mapping table in either direction — those are not part of the Permission enum (see above), so no such translation exists.

Practical Example

Agent with a Claims Competency

Agent "claims-processor":
  permissions:
    - AgentMessage("claims-*")
    - CostLimitHourly(10.0)
    - CostLimitDaily(50.0)

Competency "claims-intake":
  permissions:
    - CostLimitHourly(10.0)
  skills:
    - postgresql-connector (tools: query, list_tables, execute)
    - document-ocr (tools: ocr_scan, extract_text)
  contracts:
    - claims-core   # resolved to whichever installed integration declares it

Result after equip:
  effective tool set: [query, list_tables, execute, ocr_scan, extract_text]
    plus any tools from the integration resolved for the claims-core contract,
    narrowed further by allow_skill_tools / allow_integration_tools if set
  system_prompt: agent's prompt + competency's prompt
  resource_quota: max $10/hour, $50/day (agent's own grant; the competency's
    requirement of CostLimitHourly(10.0) was already satisfied by it)

The agent's actual carrier-write authority is decided later, at the outbound mediation seam (ADR-042/047) — this equip-time step only establishes tool visibility and the coarse permission gate, never data-trust.


Related: