Authentication
Operator console (apps/admin) signs in with email OTP + JWT by default. INTERNAL_API_KEY is CI / bootstrap only. Tenant identity (users + accounts.password_hash) is separate from admin roles.
Admin login
Section titled “Admin login”- Open
#/loginand enter an allowed admin email (ALLOWED_ADMIN_EMAILS/SUPER_ADMIN_EMAILS). - Super admin:
POST /api/v1/auth/loginissues a JWT immediately. Regular admin: receive a 6-digit OTP andPOST /api/v1/auth/otp/verify. - The JWT is stored in
localStorageand sent asAuthorization: Beareron/admin/*. Signing key is Worker secretAUTH_SECRET(HS256).
CI / bootstrap: paste INTERNAL_API_KEY under the collapsed key form. Optional display name is sent as X-Admin-Actor so audit rows are not only internal.
curl https://<esim-api-host>/api/v1/admin/me \ -H "X-Internal-Api-Key: ${INTERNAL_API_KEY}" \ -H "X-Admin-Actor: ci-bot"Allowed admin emails (OTP path): ALLOWED_ADMIN_EMAILS. Super admins: SUPER_ADMIN_EMAILS.
| Surface | Roles |
|---|---|
| Admin (API key or JWT) | super_admin | admin | viewer |
Tenant (users.role) | owner | admin | member | viewer |
ProtectedRoute and requireAdminRoles enforce the admin roles. Unauthenticated or under-privileged sessions redirect to /login. An internal API key authenticates as super_admin.
Secrets
Section titled “Secrets”Values live in GitHub Organization / Repository Secrets and Worker secrets. See the repo root .env.example (key names only).