Roles

A role is a temporarily-assumable bundle of permissions. Where a group extends a user's standing permissions, a role is something a user (or service account) assumes explicitly via POST /v1/authz/assume-role and gets short-lived credentials for.

This is Huudis's analog of AWS STS. Use roles when:

  • A service needs scoped, short-lived credentials rather than a long-lived access key (e.g. a daily cron that should only have huudis:audit:read for one hour).
  • You want cross-principal delegation: service account svc_A is trusted to assume role BillingReader, regardless of whether svc_A itself holds the underlying policy.
  • You want a clean audit trail: every AssumeRole call generates an audit-log entry with the assumed credential's accessKeyId, and every call made with that credential is taggable back to the original principal.

All endpoints require a bearer admin JWT — see Authentication.

Endpoints

Method Path Purpose
POST /v1/iam/roles Create a role
GET /v1/iam/roles List roles in the active workspace
GET /v1/iam/roles/:id Retrieve one role
DELETE /v1/iam/roles/:id Delete a role

To assume a role and get session credentials, see authz.

Create a role

POST /v1/iam/roles

Request body

Field Type Required Description
name string (1–120) yes Role name, unique per workspace. Convention: PascalCase.
description string (≤500) no Operator note.
trustPolicy object yes Who is allowed to assume this role. See Trust policy below.
maxSessionDurationSec integer (900–43200) no Max duration the resulting credentials can last. Default 3600 (1 hour). Hard cap 43200 (12 hours).

Response201 Created. The full role.

List roles

GET /v1/iam/roles
{
  "data": [
    {
      "id": "rol_01KPG…",
      "accountId": "acc_01KPG…",
      "name": "BillingReader",
      "description": "Read invoices for daily ETL job.",
      "trustPolicy": { … },
      "maxSessionDurationSec": 3600,
      "createdAt": "2026-04-01T00:00:00.000Z"
    }
  ]
}

Retrieve a role

GET /v1/iam/roles/:id

Returns the role's full trust policy. Use this when previewing who can assume it before making a change.

Delete a role

DELETE /v1/iam/roles/:id

Hard-deletes. Active assumed-role sessions issued by this role keep working until they expire — deletion does not retroactively revoke them. Use POST /v1/iam/assumed-sessions/:id/revoke to kill live sessions explicitly.

204 No Content.

Trust policy

The trust policy says who is allowed to assume the role. It's a regular policy document with Principal blocks — the inverse of a permission policy.

{
  "Version": "2026-01-01",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "User": ["usr_01KPG30SPWNKDQ9G40NET6QKA2"],
        "ServiceAccount": ["svc_01KPG30TZK…"]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Recognised Principal keys:

Key Matches Example
User Workspace member by usr_… ["usr_01KPG…"]
ServiceAccount Service account by svc_… ["svc_01KPG…"]
Role Another role by rol_… (role chaining) ["rol_01KPG…"]
Group IAM group by grp_… ["grp_01KPG…"]
* Wildcard. Use the value "*", e.g. { "*": "*" }.

Action is always sts:AssumeRole. You can omit it — an empty Action matches every operation against the role.

Explicit deny wins in trust policies, just like permission policies.

What the assumed credentials look like

When POST /v1/authz/assume-role succeeds, it returns:

{
  "credentials": {
    "accessKeyId": "ASIA0123456789ABCDEF",
    "secretAccessKey": "BASE64_SECRET_HERE",
    "sessionToken": "BASE64_TOKEN_HERE",
    "expiresAt": "2026-05-13T00:30:00.000Z"
  },
  "role": {
    "id": "rol_01KPG…",
    "name": "BillingReader",
    "arn": "forjio:huudis::acc_01KPG…:role/BillingReader"
  },
  "sessionId": "ars_01KPG…"
}

The accessKeyId always starts with ASIA (Assumed-role STS-Identity Authentication) to distinguish it from long-lived access keys (AKIA…). Use the trio (accessKeyId, secretAccessKey, sessionToken) to sign requests — see API authentication for the recipe.

Effective permissions of an assumed role

When a session is acting under a role, the effective permissions are exactly the policies attached to that role (via Attachments). The underlying principal's own permissions don't bleed in — that's the whole point. A user with full admin can assume a BillingReader role and find themselves locked out of everything except billing reads for the next hour.

Session lifecycle

Events

No events for role CRUD or assume-role. Audit-log entries (iam.assume_role, iam.role.created, iam.role.deleted, assumed_role_session_revoked) cover everything.

Next

  • Assumed sessions — live STS sessions and how to revoke them.
  • Authz — the AssumeRole endpoint itself.
  • Policies — the permission policies you attach to a role.