Realms
A realm is one OIDC issuer — a self-contained login service with its own users, sign-in methods, assurance policy, and signing keys. This page covers creating realms and every setting on the realm settings page.
Creating a realm
In the admin console, open Realms and use the Create Realm card. There is a single field:
- Name — a display label (e.g. Production). It does not affect any URL and can be changed later.
On submit, SemAuth automatically:
- generates a random 12-character issuer ID (DNS-safe, ~62 bits of entropy) and serves the realm at
{issuer_id}.semauth.app; - generates an active RSA 2048 signing key (algorithm
RS256); - sets the defaults: magic link enabled, passkey enabled, and minimum assurance = AAL1 ("Any factor").
The realms list shows each realm's Name, Issuer ID (and custom domain if set),
Status (Active / Disabled), and action links: Apps · Users · IDPs · Events · Settings · Discovery.
The Discovery link opens the realm's /.well-known/openid-configuration.
Realm settings
Open Settings for a realm (/realms/{issuer_id}/settings). There are three cards.
Realm name
Change the display name. The value is trimmed and cannot be empty. Renaming never changes the issuer ID or any URL — it is purely cosmetic.
Authentication policy
The Minimum assurance dropdown sets the lowest authentication strength a sign-in must reach before this realm will issue tokens. Three options:
| Option | Meaning | Satisfied by |
|---|---|---|
| Any factor (AAL1) | Any single factor is enough. | Magic link, passkey, or federation. |
| Require MFA (AAL2) | Multi-factor required. | A passkey (today). One-time-code MFA arrives later. |
| Require phishing-resistant (AAL3) | A phishing-resistant credential required. | A passkey. |
Guardrail. Because a passkey is currently the only factor stronger than AAL1, you
can only select Require MFA or Require phishing-resistant when passkeys are
enabled for the realm. Trying to set an unreachable floor is rejected ("This assurance level requires passkeys…")
so you can't accidentally lock every user out at /authorize.
The full model — how each sign-in method maps to an assurance level, what the acr claim contains,
and how step-up works — is on the Assurance levels page.
Enabling/disabling magic link & passkey. The two sign-in-method toggles
(magic_link_enabled, passkey_enabled) are currently only changeable through the
Admin API (PATCH /v1/realms/{issuer_id}), not from the settings
page. New realms have both enabled.
Custom domain
Serve a realm at your own brand-aligned hostname (e.g. idp.acme.com) instead of the default
{issuer_id}.semauth.app. Once active, the custom domain becomes the realm's iss, its
JWKS URL, the link host in magic-link emails, the passkey binding (rp_id), and the federation
callback target. The default host stays reachable as a fallback.
Set it before onboarding users. Changing the custom domain later invalidates every outstanding access token, refresh token, and passkey credential on the realm (the passkey binding is tied to the hostname). Configure it first, then onboard.
How to configure it
- At your DNS provider, add a CNAME record pointing your chosen hostname at the realm's
canonical target shown in the UI:
your-host CNAME {issuer_id}.semauth.app. - Back in the settings card, enter the custom domain and click Verify CNAME & activate.
SemAuth resolves the CNAME chain (up to 5 hops) using public DNS resolvers only (Cloudflare 1.1.1.1 and Google 8.8.8.8) and activates the domain only if it points at the canonical target. TLS certificates are issued automatically on first request.
Rules and common errors:
- Apex domains are refused — use a subdomain (
idp.acme.com), notacme.com. - Platform-owned domains (anything under
semauth.cc/semauth.app) are refused. - If verification fails with "no CNAME record found" or a mismatch, the record may not have propagated yet — DNS changes can take a few minutes; retry shortly.
To remove it, click Clear custom domain. The realm reverts to its canonical host; tokens issued for the custom hostname become invalid.
Signing keys
Each realm has its own RSA 2048 key pair used to sign ID tokens and JWT access tokens with RS256.
The active public key is published at the realm's /jwks.json, which is how resource servers and your
apps verify tokens statelessly. Private keys are stored encrypted at rest. Keys are generated automatically at
realm creation, and Kovilo rotates them without downtime: a new key is published alongside the current one before
it is used, and the old one stays published until the tokens it signed have expired. Always select the
verification key by the token's kid.
Limits
An organization can hold up to 10 realms (a disabled realm still counts; an erased one does not), of which up to 3 may carry a custom domain, changed at most 5 times per hour. Within a realm: up to 100 clients, 50 applications and 10 external identity providers. An organization can declare up to 20 APIs. Going over a limit is refused with an explanatory error.
Disabling a realm
A disabled realm stops resolving — its hostname no longer serves OIDC endpoints: no sign-in, no tokens.
Re-enabling restores it. Use the Status switch in the realm's Settings, or the
Admin API (PATCH /v1/realms/{issuer_id} with enabled).
Erasing a realm
When you retire a realm for good, erase it to destroy the personal data of every one of its users. Disable the realm first; the Erase realm panel then appears in its Settings. Type the realm's issuer ID to confirm.
- All user accounts in the realm are removed, and their personal data in your audit trails becomes permanently unreadable. The receipt tells you how many accounts were removed.
- An erased realm stays disabled and can never be re-enabled. Its settings page shows the date it was erased.
- It no longer counts toward your organization's realm limit, and its custom domain is released for use on another realm.
This cannot be undone. To erase a single person rather than a whole realm, see Erasing personal data.