Skip to content

API Keys

API keys are the primary authentication mechanism for Ironflow’s API. Most /api/*, /ws, and ConnectRPC (/ironflow.v1.*) endpoints require a valid API key, with the exception of public endpoints: /health, /ready, /metrics, /api/v1/capabilities, /api/v1/auth/validate, /api/v1/platform/auth/login, and login routes.

Unified Key Model

Ironflow uses a single API key type with RBAC-based authorization. All keys are stored in the same api_keys table and follow the same authentication path. What a key can do is determined entirely by its assigned roles, not by its type.

Keys are differentiated only by a cosmetic prefix:

PrefixPurposeTypical Roles
ifkey_Tenant operations (functions, events, runs, etc.)admin, developer, viewer
ifplatform_Platform/cross-tenant operationsplatform_admin, platform_operator, platform_viewer

Both prefixes go through the same auth middleware and are validated identically. The prefix is a visual hint for operators — it has no effect on authorization.

All key database IDs use the ak_ prefix (e.g., ak_550e8400-e29b-41d4-a716-446655440000). This is distinct from the display prefix shown in key listings, which uses the original ifkey_ or ifplatform_ prefix plus the first 8 hex characters (e.g., ifkey_a1b2c3d4).

Key Format

API keys use a prefix followed by 32 random hex characters (16 cryptographically random bytes):

ifkey_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4 (38 chars, tenant)
ifplatform_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4 (43 chars, platform)

Keys are stored as SHA-256 hashes in the database — the raw key is never persisted. Once generated, a key can only be verified, not retrieved.

Creating Keys

Tenant Keys

Terminal window
# Org-scoped key (works across all environments)
ironflow apikey create my-key
# With specific roles
ironflow apikey create my-key --role role_abc123 --role role_def456
# Env-scoped key with expiration
ironflow apikey create my-key --env env_abc123 --expires 720h

Platform Keys

Platform keys authenticate cross-tenant operations (user management, tenant provisioning, impersonation).

Terminal window
ironflow apikey create my-platform-key --platform
Created Platform Key: my-platform-key (id: ak_h8i9j0k1)
Key: ifplatform_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Save this key — it will not be shown again.

Platform keys are assigned platform roles (platform_admin, platform_operator, platform_viewer) which control access to platform management endpoints.

The full key value is only returned once at creation time. Store it in a secrets manager.

Environment Scoping

Tenant API keys can optionally be scoped to a single environment. When an environment-scoped key authenticates, the server resolves the full hierarchy (environment → project → organization) and injects it into the request context for downstream authorization.

Terminal window
ironflow apikey create staging-key --env env_staging_abc123

Scoping rules:

  • One environment per key — A key can be scoped to at most one environment. It cannot be scoped to multiple environments.
  • Org-scoped keys — When no environment is set on the key, the EnvironmentID in the request context remains empty. Some handlers (e.g., outbox, secrets) require the caller to explicitly pass X-Ironflow-Environment or ?env in the request itself. The header/query fallback applies to JWT dashboard auth, not org-scoped API keys.
  • Platform keys — Never environment-scoped. They use impersonation (X-Ironflow-Org header) to act on behalf of specific tenants.
  • Immutable scope — A key’s environment and organization cannot be changed after creation. To change scope, create a new key and delete the old one.

Authentication

HTTP Requests

Use the Authorization header with a Bearer prefix:

Terminal window
curl -H "Authorization: Bearer ifkey_a1b2c3d4..." http://localhost:9123/api/v1/functions

WebSocket Connections

WebSocket connections cannot always set custom headers. Use the token query parameter as a fallback:

ws://localhost:9123/ws?token=ifkey_a1b2c3d4...

Both ifkey_ and ifplatform_ keys work with either method.

SDK Authentication

Both the Go and JavaScript SDKs pass the API key as a Bearer token in the Authorization header on every request.

client := ironflow.NewClient(ironflow.ClientConfig{
ServerURL: "http://localhost:9123",
APIKey: "ifkey_a1b2c3d4...",
})

Dashboard Authentication

The web dashboard uses JWT cookies for authentication, not API keys. Users log in via /api/v1/auth/login and receive an ironflow_dashboard_token cookie (24-hour TTL). The auth middleware tries API key auth first; if no key is present, it falls back to the JWT cookie.

Validation Flow

The auth middleware runs on every non-public request. Here is the full validation sequence:

sequenceDiagram
    participant Client
    participant Middleware as Auth Middleware
    participant Store as Database
    participant RBAC as RBAC Evaluator
    participant Audit as Audit Emitter
    participant Handler as Route Handler

    Client->>Middleware: HTTP request

    Note over Middleware: Check if public path<br/>(/health, /ready, /metrics, etc.)
    alt Public path
        Middleware->>Handler: Pass through (no auth)
    else Protected path
        Middleware->>Middleware: Extract API key from<br/>Authorization header or ?token param

        alt API key present
            Middleware->>Store: Lookup key by SHA-256 hash
            alt Key not found
                Middleware-->>Client: 401 Invalid API key
            else Key found
                Middleware->>Middleware: Check expiration
                alt Key expired
                    Middleware-->>Client: 401 API key expired
                else Key valid
                    Middleware->>Store: Load roles via api_key_roles
                    Middleware->>Middleware: Resolve project from environment
                    Middleware->>RBAC: Evaluate policy (roles, action, resource)
                    Middleware->>Audit: Emit authz decision (async)
                    alt Denied
                        Middleware-->>Client: 403 Insufficient permissions
                    else Allowed
                        Middleware->>Store: Update last_used_at (async)
                        Middleware->>Middleware: Build RequestContext<br/>(org, project, env, roles)
                        alt Platform key with X-Ironflow-Org header
                            Middleware->>Middleware: Handle impersonation flow
                        end
                        Middleware->>Handler: Forward with RequestContext
                        Handler-->>Client: Response
                    end
                end
            end
        else No API key
            Middleware->>Middleware: Check for JWT dashboard cookie
            alt Valid JWT
                Middleware->>Handler: Forward with JWT context
                Handler-->>Client: Response
            else No valid auth
                Middleware-->>Client: 401 Authentication required
            end
        end
    end

Key Lifecycle

Rotation

Rotation creates a new key (new ID, new raw value) while preserving the name, environment, expiration, and role assignments from the old key. The old key is deleted atomically.

Terminal window
ironflow apikey rotate ak_abc123

Rotation produces a new key ID (ak_*) and a new raw key value (ifkey_* / ifplatform_*). The raw key is only printed once — save it immediately. If your systems reference the old key ID, update them along with the raw key value.

Expiration

Keys can be created with an optional expiration duration:

Terminal window
ironflow apikey create short-lived-key --expires 720h # 30 days
ironflow apikey create ci-key --expires 24h # 1 day

The expiration is enforced at authentication time — any request with an expired key receives a 401 API key expired response. Expired keys cannot be rotated; delete them and create a new key instead.

Deletion

Terminal window
ironflow apikey delete ak_abc123

The key is immediately revoked. Any requests using this key will start receiving 401 errors.

Listing Keys

Terminal window
# List all tenant keys
ironflow apikey list
# List platform keys
ironflow apikey list --platform
# JSON output
ironflow apikey list --json
ID NAME PREFIX ROLES CREATED LAST USED EXPIRES
ak_abc123 my-key ifkey_a1b2c3d4 role_abc123 2026-01-01T00:00:00Z 2026-03-01T12:00:00Z -

The full key value is never shown in list output — only the display prefix (ifkey_ or ifplatform_ + first 8 hex chars) for identification.

Bootstrap Key

On first boot, Ironflow generates a default admin API key (ID: ak_admin_bootstrap) with the admin role. The plaintext key is written to a file (mode 0400) and the server prints the file path; it is never written to stdout or journalctl. On subsequent restarts, only the key prefix is shown.

The file path is controlled by --bootstrap-key-file:

DeploymentDefaultNotes
Single binary / dev<db-dir>/.ironflow_bootstrap_key.jsonSame dir as the SQLite database.
Meta-cluster systemd/run/ironflow/bootstrap-key.jsonSet explicitly by the unit; tmpfs (auto-clears on reboot, never persisted to disk).

Read the key with:

Terminal window
cat /path/to/bootstrap-key.json | jq -r .key

The file contains JSON: {"key":"…","key_id":"ak_admin_bootstrap","org_id":"org_default","role":"admin","timestamp":"…"}.

Read, delete, rotate

Read the file once, delete it immediately (rm -f /path/to/bootstrap-key.json), then rotate via ironflow apikey rotate ak_admin_bootstrap. Do not leave the file on disk — anything that can read the file as the ironflow user can take over the meta-cluster. The structured log event BOOTSTRAP_ADMIN_KEY_ISSUED carries the file path (file_path field) but never the key, so central log shipping is safe to enable.

If the file write fails (read-only fs, ENOSPC, etc.), the server logs BOOTSTRAP_ADMIN_KEY_ISSUED with a file_path_error field and intentionally does not print the key anywhere — the operator must reset the bootstrap key (see the meta-cluster runbook’s Reset bootstrap admin key section).


Roles

Keys derive all their permissions from assigned roles. See Roles & Permissions for the full permissions matrix.

Tenant roles (for ifkey_ keys):

RoleDescription
adminFull access — all read, write, and delete operations
developerRead and write access — can create and modify resources
viewerRead-only access — can list and read resources

Platform roles (for ifplatform_ keys):

RoleDescription
platform_adminFull access to all platform operations (wildcard)
platform_operatorTenant management, impersonation, read access to users/keys/roles
platform_viewerRead-only access to all platform resources

Custom roles can be created for fine-grained access control.


FAQ

Can a key be scoped to multiple environments?

No. A key can be scoped to at most one environment. If you need access across multiple environments, create an org-scoped key (omit --env) or create separate keys per environment for tighter security.

Can I change a key’s environment or organization after creation?

No. A key’s scope (org, environment) and name are immutable. There is no update endpoint for keys. To change scope, create a new key and delete the old one. This prevents a distributed key from silently gaining or losing access.

What can I change on an existing key?

Only role assignments can be modified (PUT /api/v1/apikeys/{id}/roles). Everything else — name, environment, organization — is fixed for the lifetime of the key.

What’s the difference between omitting --env and using --env?

  • Omit --env: org-scoped key. The environment is resolved per-request from the X-Ironflow-Environment header, giving the key flexibility across environments.
  • Use --env env_id: env-scoped key. The key is locked to that environment permanently.

How do I move a key from one environment to another?

You can’t. Delete the old key and create a new one scoped to the new environment.

What happens to expired keys?

Expired keys return 401 API key expired on every request. They cannot be rotated — delete and recreate. Expired keys remain in listings until explicitly deleted.