Policy attachments

A policy attachment is the join row between a policy and a principal (a user, group, role, or service account). Attach to grant; detach to revoke.

A principal's effective permissions are the union of every policy attached, directly or via group membership. There is no "deny by absence" — only explicit Deny statements override Allows.

All endpoints require a bearer admin JWT — see Authentication.

Endpoints

Method Path Purpose
POST /v1/iam/policy-attachments Attach a policy to a principal
GET /v1/iam/policy-attachments List attachments, filterable
DELETE /v1/iam/policy-attachments/:id Detach

Attach a policy

POST /v1/iam/policy-attachments

Request body

Field Type Required Description
policyId string (pol_…) yes The policy to attach. Custom or system.
principalType user | group | role | service_account yes Kind of target.
principalId string yes The usr_…, grp_…, rol_…, or svc_…. Must belong to the active workspace (system policies are workspace-agnostic).

Response201 Created. The attachment row.

{
  "data": {
    "id": "pat_01KPG…",
    "policyId": "pol_system_huudis_readonly",
    "principalType": "group",
    "principalId": "grp_01KPG…"
  }
}

Errors

Status error.code When
409 ALREADY_ATTACHED The exact (policy, principalType, principalId) tuple already exists. Detach first if you want to replace.
400 VALIDATION_ERROR Invalid principalType, or the principal doesn't exist in this workspace.

List attachments

GET /v1/iam/policy-attachments

Returns attachments, filterable via query params.

Query parameters

Param Type Description
policyId string Only attachments of this policy.
principalType enum Only attachments to this kind of principal.
principalId string Only attachments of this principal.

Combine them. For example, ?principalType=user&principalId=usr_01KPG… returns every policy attached directly to that user (not policies inherited via group membership — for the effective view, call POST /v1/authz/check).

Response

{
  "data": [
    {
      "id": "pat_01KPG…",
      "policyId": "pol_system_huudis_readonly",
      "principalType": "group",
      "principalId": "grp_01KPG…",
      "policy": {
        "id": "pol_system_huudis_readonly",
        "name": "HuudisReadOnly",
        "scope": "system",
        "description": "All read-only actions in Huudis.",
        "document": { … }
      }
    }
  ]
}

The expanded policy field is included so the dashboard can render policy names and descriptions without a second query.

Detach a policy

DELETE /v1/iam/policy-attachments/:id

Removes the attachment. The principal loses that policy's statements from their effective set immediately — subsequent authz/check calls will deny anything that was only allowed by this policy.

204 No Content.

The attachment object

Field Type Description
id string (pat_…) Stable attachment ID.
policyId string (pol_…) The attached policy.
principalType enum user, group, role, service_account.
principalId string The bound principal.
policy object Expanded policy (list response only).

Effective permissions

When you call POST /v1/authz/check, Huudis assembles the effective policy set for the principal:

  1. Direct attachments where principalType matches the calling principal's kind.
  2. For users only: every policy attached to a group the user belongs to.
  3. For assumed-role sessions: every policy attached to the role — the underlying principal's own policies are excluded, by design.

The set of statements is concatenated and evaluated. Deny always wins over Allow; default is Deny.

Inspecting "why did this fail?"

The authz-check response carries a matchedSid field naming the statement that triggered the decision (when the policy author set a Sid). Pair with GET /v1/iam/policy-attachments?principalId=… and the policy document to reconstruct exactly which statement applied:

huudis iam policy-attachments list --principal-id usr_01KPG…
huudis iam policies get pol_01KPG…

For repeated debugging sessions, the audit log records each authz_check invocation with the resolved decision and matchedSid in metadata.

Cross-attachment patterns

  • One policy, many groups. Common. HuudisReadOnly attached to every "read-only" group.
  • Per-environment guardrails. A Deny policy attached to a group "Prod-only" that forbids destructive actions when forjio:WorkspaceSlug is not production.
  • MFA-required group. Attach an explicit-deny policy that triggers when forjio:MfaPresent is false. Members of the group can do nothing until they enrol.

Events

No events for attachment CRUD. Audit-log entries (iam.policy_attached, iam.policy_detached) cover both operations with the full policy ID and principal in metadata.

The reserved (not-yet-emitted) huudis.iam.policy_attached.v1 webhook event is in the catalog for future use; see webhook event catalog.

Next

  • Policies — the documents you're attaching.
  • Authz — the live check that uses these attachments.
  • Roles — a common attachment target.