Skip to content
Talk to our solutions team

Configuration

Delivered through meta (or meta.localdir in development):

iam/
providers.yaml # auth method declarations
realms.yaml # which entities authenticate, with which providers
token.yaml # token expiry + claims
access.yaml # named authorization predicates (js/expr)
config.yaml # free-form service properties
extend/ # entity overlays (new entities, or field augments)

providers.yaml — declare methods; see Authentication for the full provider catalog:

providers:
- name: password
type: challenge
- name: magiclink
type: magic
template: magictemplate

realms.yaml — a realm binds an entity to providers; the default realm authenticates users:

realms:
- name: default
entity: users
providers: [password, magiclink]
default: true
- name: patient
entity: patient # a product-defined entity
providers: [password]

Feature toggles remove whole surfaces — e.g. realmconfig.enable.agents: false removes /rest/iamagents.

token.yaml — expiry plus static and row-derived claims:

token:
expiry: 600m
claims:
plan: gold
claims_from:
department: properties.department # dot-path into the identity row

Reserved claims (iss, sub, tenant, realm, roles, iat, nbf, exp, type, scope, act) cannot be shadowed.

Tenant extensions are the same file shapes stored as rows in the tenant’s tenant_extensions entity (path: /token.yaml or /extend/<name>.yaml, YAML body) — they merge over the product layer, augment-only.

Profiles are embedded — iam.svc config list, then config generate <name>: bootstrap-dev, bootstrap-config-server, bootstrap-vault, bootstrap-full, bootstrap-managed-session.

Key top-level (boot-plane) keys — dev port is 5030:

name: iam
host: 127.0.0.1
port: "5030"
bootstrap:
token: one-time-bootstrap-token
jwks:
issuers:
iam: "http://127.0.0.1:5030" # MUST be top-level, never under hives:
config:
url: https://config.internal:7123 # optional remote config
vault:
url: https://vault.internal:7999 # optional key custody

Tenant nodes live in the tenant hive (inline in dev, on config.svc in production). Each served CPET declares its datastore, pool, and product source:

hives:
tenant:
- path: acme:forge:prod:tenant-a
domain: app.acme.com
meta:
url: https://meta.internal:8080 # or meta.localdir: ./myproduct
dbpools:
main:
dsn: vault:tenant-a-dsn
schema: tenant_a # schema-per-tenant isolation
session: # optional managed-session posture
preset: hipaa
max_concurrent_sessions: 1
on_limit: evict_oldest
idle_timeout_s: 900
absolute_expiry_s: 43200
- path: acme:forge:prod:superadmin # the product's control plane
dbpools:
main:
dsn: vault:superadmin-dsn
schema: superadmin

Three-tier resolution per tenant:

  1. Vault secret named by tenant property jwt.signing.key (PKCS#8 Ed25519 PEM). Naming a vault secret without a vault client configured is a loud boot error.
  2. Inline PEM literal in the tenant node (dev only).
  3. Generated into the tenant’s own jwt_signing_key table on first token mint.

External keys are never persisted; key ids are RFC 7638 thumbprints.

Any chassis service verifies iam-issued tokens by declaring the issuer:

jwks:
issuers:
iam: "https://iam.internal:5030"

The chassis middleware then validates every Bearer token on protected routes:

  • Dual-accept — PASETO v4.public and legacy JWT verify through the same core; keys resolve via the tenant’s JWKS (5-minute cache, stale-key fallback).
  • Tenant pin — the token’s tenant claim must equal the request’s CPET headers. There is no superadmin exception: a control-plane token on a tenant surface is rejected.
  • SSRF guard — only issuers listed under jwks.issuers.* are ever fetched.
  • Claims land in request context; role helpers (admin, superadmin guards) compose on top. A scope claim confers authority only on type: "service" tokens — user tokens derive everything from roles.

Non-chassis consumers: fetch GET /.well-known/jwks.json with CPET headers, verify the Ed25519 signature and exp/nbf, then authorize on tenant, realm, and roles.