Role-Based Access Control (RBAC)
What you'll accomplish: Understand the role hierarchy, assign appropriate permissions, and manage API keys for teams and services.
Roles
Six roles are defined in hoziron-core-types::auth::permissions::Role:
| Role | Purpose |
|---|---|
admin | Full access — all operations, config changes, key and vault management |
operator | Agent lifecycle, workflow management, schedule management, backup create/list, integration/catalog management |
developer | Install skills/competencies, invoke agents, install catalog packages, publish packages |
viewer | Read-only access to most resources (not audit, security, or vault) |
service | Machine-to-machine — invoke agents, send messages, start workflow runs, read/write memory |
auditor | Read-only access to everything plus audit:read and security:audit — for compliance officers who must verify platform posture independently, without the ability to modify any state |
auditor is a real, first-class role (Role::Auditor in code) even though it isn't listed in hoziron-cli auth create-key --help's example text — it is accepted by --role auditor the same as the other five.
Permission matrix
This table is generated from the platform's actual PERMISSION_MATRIX (crates/platform/hoziron-core-types/src/auth/permissions.rs) — every Action variant that exists is listed below with the exact roles authorized to perform it. There is no agent:create or workflow:create action: agents and workflows enter the system only via catalog:install (install-from-package), not a runtime create endpoint.
| Action | Admin | Operator | Developer | Viewer | Service | Auditor |
|---|---|---|---|---|---|---|
agent:start | ✓ | ✓ | ||||
agent:stop | ✓ | ✓ | ||||
agent:send_message | ✓ | ✓ | ✓ | ✓ | ||
agent:list | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
agent:status | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
agent:update | ✓ | ✓ | ||||
agent:invoke | ✓ | ✓ | ✓ | ✓ | ||
workflow:run | ✓ | ✓ | ✓ | ✓ | ||
workflow:list | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
workflow:status | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
competency:install | ✓ | ✓ | ✓ | |||
competency:equip | ✓ | ✓ | ✓ | |||
competency:unequip | ✓ | ✓ | ✓ | |||
competency:manage | ✓ | ✓ | ✓ | |||
skill:install | ✓ | ✓ | ✓ | |||
skill:remove | ✓ | ✓ | ✓ | |||
config:read | ✓ | ✓ | ✓ | ✓ | ✓ | |
config:write | ✓ | |||||
config:reload | ✓ | |||||
auth:key_management | ✓ | |||||
vault:read | ✓ | |||||
vault:manage | ✓ | |||||
audit:read | ✓ | ✓ | ||||
audit:write | ✓ | |||||
schedule:create | ✓ | ✓ | ||||
schedule:delete | ✓ | ✓ | ||||
schedule:update | ✓ | ✓ | ||||
package:publish | ✓ | ✓ | ✓ | |||
registry:publish | ✓ | ✓ | ✓ | |||
registry:admin | ✓ | |||||
registry:download | ✓ | ✓ | ✓ | ✓ | ✓ | |
backup:create | ✓ | ✓ | ||||
backup:list | ✓ | ✓ | ||||
backup:restore | ✓ | |||||
daemon:shutdown | ✓ | |||||
budget:read | ✓ | ✓ | ✓ | ✓ | ✓ | |
memory:read | ✓ | ✓ | ✓ | ✓ | ||
memory:write | ✓ | ✓ | ✓ | ✓ | ||
channel:read | ✓ | ✓ | ✓ | ✓ | ✓ | |
channel:manage | ✓ | ✓ | ||||
webhook:read | ✓ | ✓ | ✓ | ✓ | ||
webhook:manage | ✓ | ✓ | ||||
integration:read | ✓ | ✓ | ✓ | ✓ | ✓ | |
integration:manage | ✓ | ✓ | ||||
security:read | ✓ | ✓ | ✓ | ✓ | ||
security:audit | ✓ | ✓ | ||||
approval:read | ✓ | ✓ | ✓ | ✓ | ✓ | |
approval:decide | ✓ | ✓ | ||||
catalog:read | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
catalog:install | ✓ | ✓ | ✓ | |||
catalog:manage | ✓ | ✓ | ||||
trigger:read | ✓ | ✓ | ✓ | ✓ | ✓ | |
trigger:manage | ✓ | ✓ | ||||
template:read | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
session:read | ✓ | ✓ | ✓ | ✓ | ✓ | |
licence:read | ✓ | ✓ | ✓ | ✓ | ✓ |
Notable patterns:
- The credential vault (
vault:read/vault:manage) is Admin-only, deliberately excludingauditor— even a compliance role cannot list or write vault entries. This is the same bar asauth:key_management. See Credentials. catalog:uninstalland destructive integration/registry removal are gated atcatalog:manage/integration:manage(Operator+), separate from the lower-barcatalog:install/integration:readused for routine installs.auditornever appears on a write action — it is the read-plus-audit role, not a superset ofoperator.audit:readis defined but not currently wired to any route —GET /security/auditis gated onsecurity:audit, notaudit:read(both land on the same Admin/Auditor pair today, so this doesn't change who can do what, but don't go looking for a separateaudit:read-gated endpoint).
Insufficient permissions return:
{
"error": "forbidden",
"message": "Role 'viewer' cannot perform 'agent:start'"
}
Key management
Create keys for different roles
# Admin key (full access)
hoziron-cli auth create-key --role admin --name "platform-admin"
# Operator key for CI/CD
hoziron-cli auth create-key --role operator --name "deploy-bot" --expires-in 90d
# Developer key for a team member
hoziron-cli auth create-key --role developer --name "alice"
# Service key for automated integrations
hoziron-cli auth create-key --role service --name "webhook-handler"
# Viewer key for monitoring dashboards
hoziron-cli auth create-key --role viewer --name "grafana-readonly"
# Auditor key for a compliance officer (read-only, plus audit trail access)
hoziron-cli auth create-key --role auditor --name "compliance-review"
Key expiration
Keys can optionally expire:
hoziron-cli auth create-key --role operator --name "temp-access" --expires-in 7d
Expired keys are treated as invalid at validation time.
Rotate keys
hoziron-cli auth rotate-key <key-id>
Rotation creates a new key and revokes the old one in a single operation.
Safety guardrails
- The last admin key cannot be revoked (prevents lockout)
- Key values are shown once at creation — never retrievable after
- Only the first 12 characters are stored as a prefix for identification
Best practices
- Use
servicerole for automation — it has minimal permissions (invoke agents, run workflows) - Set expiration on all non-admin keys
- Store keys in a secrets manager, inject via environment variables
- Create separate keys per integration (easier to revoke individually)
- Use OIDC for human users, local keys for service accounts
Next steps
Related: