IAM
iam.svc is the platform’s authentication and identity service: it authenticates a caller against one tenant’s identities, mints that tenant’s token, and serves the identity entities and control plane behind it.
The model
Section titled “The model”Five pieces, in the order a request meets them.
| Piece | What it is |
|---|---|
| Realm | A named binding of an identity entity (users, patient, vendor) to the provider names allowed against it. Declared in realms.yaml, not a database table. |
| Provider | A named auth-method declaration — name, type, template, nothing else. It is a name gate, not credentials. |
| Claims builder | Every flow that authenticates a user funnels through one builder: same reserved claims, same tenant keyring, same token policy. |
| Token | PASETO v4.public signed with the tenant’s own Ed25519 key. Verification dual-accepts PASETO and legacy EdDSA JWT. |
| Session | Stateless (access token + rotating refresh token) or managed (opaque server-side reference exchanged per request). One preset picks which. |
A realm resolves from the login body’s realm, else the default realm, else the literal users.
The minted sub is the row id in that realm’s entity, and the realm claim rides along.
The request path
Section titled “The request path”Every route except /ready and /health runs the same chain:
security headers + 1 MiB body cap → request timeout → tenant middleware (X-Customer, X-Product, X-Env, X-Tenant → CPET key) → auth middleware (Bearer PASETO/JWT, or ApiKey <id>.<secret>) → routerThe tenant key selects everything downstream: connection pool, composed schema, script runtime,
behavior config and signing keyring are all built and cached per CPET. A token is verified against
the keyring of the tenant the request names, and its tenant claim must match that CPET — there is
no cross-tenant token and no operator exception.
Miss a header and the tenant middleware answers 400 with the plain-text body invalid tenant —
not JSON. Name a CPET with no resolvable datastore config and the failure surfaces later, as
500 engine_unavailable. Both are covered in Errors.
Routes mount at the router root. There is no /v2 prefix. Full surface:
HTTP API reference.
What the block owns, what you configure
Section titled “What the block owns, what you configure”| The block owns | You configure |
|---|---|
| Password verification (argon2id), refresh rotation with family-wide reuse detection | Which entity each realm authenticates, and under which provider names |
| TOTP enrolment and the MFA gate | Access-token lifetime and custom claims |
| Token minting, key custody, rotation, per-tenant JWKS | Session preset, concurrency ceiling, idle and absolute timeouts |
| Managed-session registry, revocation, CPET binding | Named authorization predicates over identities |
| Per-(tenant, IP) rate limiting on brute-forceable auth routes | Per-CPET pools, schemas, and the product-file source |
| The control plane, operator grants and impersonation | Identity fields, via schema extension |
Identity fields are ordinary schema fields, so the columns a user may log in with are declared on
the field itself (useforauth, useforidentity) rather than in an auth config. See
Identity.
The six definition files
Section titled “The six definition files”A product author writes an iam/ folder, served through meta (or
meta.localdir in development). Six file types are read; anything else in the folder is ignored by
the behavior loader.
| File | Declares |
|---|---|
realms.yaml | Realms: entity, allowed provider names, active, default |
providers.yaml | Provider names (name, type, template) |
token.yaml | expiry, static claims, row-derived claims_from |
session.yaml | preset, and the advanced model/timeout/concurrency axes |
access.yaml | Named authorization predicates |
config.yaml | Free-form service properties and feature toggles |
Each composes in four layers: built-in defaults ⊕ the product’s iam/ file ⊕ the tenant’s
tenant_extensions row at /<type>.yaml ⊕ the tenant node’s session: block. Merging is
last-write-wins per named item and per set field. A missing layer is normal; a malformed layer
aborts the tenant build.
What is implemented but not wired
Section titled “What is implemented but not wired”The shipping binary never registers an OAuth provider or a WebAuthn relying party, and there is no configuration key that does it:
| Surface | What a caller gets |
|---|---|
GET /auth/oauth/:provider/start and /callback | 404 unknown_provider, for every provider name |
POST /auth/webauthn/register/*, /assert/* | 503 webauthn_disabled |
Two more limits worth knowing before you design around them:
- There is no OTP login provider. No OTP over SMS, email or WhatsApp, and no OTP second factor. MFA has exactly one method, TOTP.
crypto_suite: v3andpreset: fedrampdo not change the signing primitive. They parse and compose, but every token minted is PASETO v4.public over Ed25519; the timeout and session-model parts of the preset do take effect.
Where things sit
Section titled “Where things sit”| Item | Value |
|---|---|
| Binary | iam.svc |
| Dev port | 5030 |
| Route prefix | none — /auth/login, /rest/:entity, /schema |
| Unauthenticated, header-free routes | GET /ready, GET /health |
| Product source | the iam/ folder, via meta or meta.localdir |
| Storage | one PostgreSQL pool per CPET; database-per-tenant or schema-per-tenant |
| Signing keys | Vault-custodied, inline PEM (dev), or generated per tenant on first use |
| Control plane | the reserved tenant <customer>:<product>:<env>:superadmin, one per product and environment |
One process serves any product. Only iam — the service’s own identity — is registered at boot;
every other product name is registered on first sighting of it in X-Product.
Continue with
Section titled “Continue with”- Authentication — realms, providers, and every login flow that ships
- Tokens and sessions — claims, key custody, refresh rotation, managed sessions
- Authorization — roles, access rules, API-key capabilities, the operator plane
- Identity — the identity entities and their CRUD surface
- Operating — boot config, bootstrap, tenant onboarding, failure modes
- HTTP API reference — every route, request and response
- Errors — the external error-code table