Turn on email sign-in
app.identity.mode: local makes Prism its own front door: only allowlisted
emails can sign in, each sign-in mails a link that works once, and owning the
mailbox is the whole credential — there are no passwords anywhere in it. This
is the multi-user path for a deployment with no IdP integration yet, and it is
replaced later by easyauth or iap — see
Sign in with Entra ID — without anyone losing their access.
Their saved work is a separate question, and worth knowing before you choose:
saved prompts, conversations and feedback key on the identity the front door
asserts, which under local mode is the email address and under an IdP is
usually a provider-issued id. Switching therefore hands everyone a clean slate
unless the proxy is configured to keep asserting the address; the last section
of the Entra page has the setting.
Identity and roles is the reference for what each value means. This is the order to do them in, and the check that stops you discovering a problem at the worst moment.
Why the order matters. Every value below can be right, every pod can be running, the self test can be green on every other line — and the install is still dead on arrival if your relay will not deliver to the one person who can let anybody else in. There is no password to fall back on and no second door. So the relay is proved before anyone relies on it, and the recovery path is worth reading before you need it rather than after.
1. Decide who the bootstrap administrators are
app.auth.superAdmins is a list of emails, and it is the only way anyone signs
in at first: those people log in, then add everybody else on the App users
page (/admin → App users).
Pick at least two, at different mailboxes. This is the cheapest insurance available against the recovery procedure at the bottom of this page, and the one moment where adding a colleague costs nothing.
2. Gather what the relay needs
| Value | What it is |
|---|---|
smtp.host | your relay's hostname |
smtp.port | 587 for STARTTLS, 465 for implicit TLS |
smtp.tls | starttls, implicit, or none. Any other value fails at render, so an unknown setting cannot quietly mean cleartext. |
smtp.fromAddress | the sender. It must be an address your relay accepts MAIL FROM for — this is the single most common reason a relay refuses. |
smtp.username | only if your relay authenticates. Empty means no AUTH, which is normal for a relay that trusts network position. |
smtp-password | the Secret key for that AUTH password, if there is one. Never a values file. |
app.auth.baseUrl | the external URL your users browse to. The emailed link points at it and the pod cannot infer it — get this wrong and links arrive pointing somewhere nobody can reach. |
Two more with sensible defaults: app.auth.magicLinkMinutes (how long a link
stays valid) and app.auth.sessionDays (how long the cookie lasts).
Certificates are verified by default, chain and hostname, on both TLS
modes. If your relay presents a private-CA certificate the pod cannot verify,
the right fix is at the relay. smtp.tlsVerify: false keeps TLS but trusts
whoever answers — on the connection that carries sign-in links, so take that
knowingly or not at all. For a relay with no TLS, smtp.tls: none is the
honest setting rather than an unverified handshake.
3. Confirm the pod can reach the relay
Outbound to smtp.host:smtp.port from the namespace, which is a firewall
rule your cluster may not already have — mail relays are often on a different
path from HTTPS egress:
nc -z -w 5 <relay host> <relay port> && echo open
Run it from a pod in the target namespace, for the reason in Prove the egress: a laptop that can reach the relay proves nothing about what the namespace is permitted to do.
4. Upgrade
helm upgrade <release> <chart> -n <namespace> -f <your values file> \
--set app.identity.mode=local \
--set app.auth.baseUrl=https://<your host> \
--set smtp.host=<relay> --set smtp.fromAddress=<sender>
The modes are mutually exclusive and the app refuses to start with more than one configured. If anything required is missing, the install fails at render or the app refuses to boot with a log line naming every missing value — which is deliberate. A pod that came up anyway would serve a sign-in page whose emails go nowhere.
5. Prove the relay before anyone depends on it
Run the self test. Its smtp relay line connects, negotiates
TLS, authenticates if a username is set, and then asks the relay — address by
address — whether it would deliver to each bootstrap administrator. It does
this with MAIL FROM/RCPT TO and then RSET/QUIT, never sending a
message, so running it repeatedly cannot spam anyone.
The per-address detail is the point. A relay that accepts your domain in general and refuses one specific address is a green line on any check that stops at "the relay answered", and a dead install for the person that address belongs to.
6. Sign in, then hand out access
Open the app, request a link, click it. One thing worth knowing before you raise a support call about it: corporate mail scanners that pre-fetch links do not consume the token. The link opens a confirmation page, and only its "Continue to Prism" button — a form POST, which scanners never submit — spends it. A scanned link still works when the person clicks it.
From /admin → "App users", add everyone else. They are ordinary users; only
the emails in app.auth.superAdmins start as super admins.
What an upgrade does to the admin list
Seeding is additive, never reconciling. Every boot creates a super-admin row for any listed email that lacks one, and touches nothing else. Two consequences that surprise people:
- Shortening the list revokes nobody. Revoking an administrator is done
in-app — demote or remove on
/admin→ App users — and for a bootstrap admin you must also remove them fromapp.auth.superAdmins, or the next restart re-adds them. - Re-listing a demoted admin re-promotes nobody. Seeding never touches an
existing row, so an address the allowlist has seen before keeps whatever role
it has now. The skipped seed is logged as
seed_skipped_existing, which is why an upgrade can honestly report having added nobody.
One rule is enforced on top: the last super admin can never be removed or demoted. Hand an install over by promoting the new owner first, then demoting yourself.
Locked out — nobody can sign in
The break-glass path needs cluster access rather than a Prism login, which is
what makes it break-glass. Add an email to app.auth.superAdmins and
helm upgrade.
It must be an address the allowlist has never seen — per the rule above, a listed address that was demoted in-app stays demoted, so re-listing the person who locked themselves out achieves nothing. Use a fresh one.
For a diagnostic report while locked out, the self-test CLI runs through
kubectl exec with no sign-in at all — see
the self test.
If the link never arrives
Three places, in this order: the self test's smtp relay line; the app's
app_events table or its log, which records every refused request — an address
not on the allowlist, an address rate-limited (three links per address per 15
minutes), or a delivery failure as auth_email_failed with the relay's own
error; and then spam.
The sign-in page deliberately says "check your email" in all of those cases rather than telling an unauthenticated visitor which addresses exist. The operator's records are where the truth is.
When auth_email_failed carries a TLS error, the symptom decides the fix and
the two look alike:
Local sign-in separates a certificate the
pod cannot verify from a handshake that stalls, which want opposite settings.
If you are stuck: send the self test screenshot — the smtp relay line
carries the relay host, port, TLS mode and sender, and never the password — and
the auth_email_failed event if there is one. Between them they distinguish a
relay that refused from a message that was accepted and lost downstream, which
are different teams' problems.