Skip to content

This document was written by AI and has been manually reviewed.

Admin Guide

The admin panel is available at /admin and is visible only to users with role = admin. The first admin is created during first-run setup. Additional admins are promoted via Admin → Users → Edit User → Role → Admin.

Dashboard

Shows four summary stats:

StatDescription
Total usersAll registered accounts
OAuth appsAll registered applications
Verified domainsDomains that passed verification
Active tokensNon-expired OAuth access tokens

A panel under the stats surfaces operational warnings — most importantly, when SECRETS_KEY is bound but the D1 data hasn't been migrated yet. Click through to Settings → Danger Zone to run the one-time encryption pass.

Settings

Settings are grouped into tabs. All changes take effect immediately — no redeployment needed.

General

  • Site name — shown in the browser tab and email templates
  • Site description — shown on the login page
  • Site icon URL — link to a PNG/SVG logo
  • Registration modeopen (anyone can register), invite-only (requires an invite token), or closed (no new registrations)
  • Require email verification — users must click the verification link before logging in

Appearance

  • Accent color — hex color that drives the entire FluentUI theme. Changes are reflected immediately after saving.
  • Custom CSS — injected as a <style> block on every page. Useful for branding tweaks without forking the UI.

Security / Sessions

  • Session TTL (days) — how long a login session lasts
  • Access token TTL (minutes) — OAuth access token lifetime
  • Refresh token TTL (days) — OAuth refresh token lifetime
  • Sudo mode TTL (minutes) — after a successful 2FA step-up, subsequent challenges from the same (user, session, app) skip the TOTP/passkey prompt for this many minutes. 0 disables sudo mode entirely. The action acknowledgement checkbox is still required on every confirmation. See OAuth → Step-up 2FA.
  • Require captcha for 2FA — site-wide: every step-up confirmation must solve the active captcha. Apps can also opt in per challenge. No-op when the captcha provider is "None".
  • IPv6 rate-limit prefix — how many bits of an IPv6 address are bucketed together for rate limiting (default /64). Prevents a single /64 allocation from getting unlimited login attempts.

Bot Protection

Choose one captcha provider:

ProviderNotes
NoneNo bot protection
Cloudflare TurnstileRequires a Turnstile site key + secret. Free tier available.
hCaptchaRequires an hCaptcha site key + secret.
reCAPTCHA v3Requires a Google reCAPTCHA v3 site key + secret. Invisible.
Proof-of-WorkNo third-party service. Difficulty 20 = ~0.1–2 s on modern hardware.

Email

The email settings are split into two sub-tabs: Send and Receive.

Send

  • Email providernone, resend, mailchannels, or smtp
  • API key — for Resend or Mailchannels
  • SMTP settings — host, port, encryption, username, password (when provider is smtp)
  • From address — the sender address for verification and notification emails
  • Send test email — sends a test email to the admin's address to verify outgoing email is working

Receive

  • Email verification methods — controls how users can verify their email:
    • link — system sends a verification link to the user's email
    • send — user sends an email to verify their address (see receive provider below)
    • both — user can choose either method
  • Receive provider — how Prism receives inbound verification emails:
    • Cloudflare Email Workers — event-driven, emails trigger the worker's email() handler. Requires Cloudflare Email Routing. Users send an email to verify-<code>@<host>.
    • IMAP — Prism polls an IMAP mailbox on the cron schedule (every 6 hours by default). Works with any email provider. Users send an email with their verification code as the subject to the configured IMAP mailbox address (e.g. receive@prism.example.com).
    • None — disable inbound email (only link-based verification will work)
  • Receive host — domain for inbound verify-<code>@<host> emails (Cloudflare Email Workers only). Leave blank to default to the APP_URL hostname.
  • IMAP settings — host, port, encryption, username, password (when receive provider is imap). The IMAP username (email address) is shown to users as the destination for verification emails.
  • Test email receiving — generates a test code and address to verify inbound email is working

Domain re-verification

  • Domain reverify interval (days) — how often Prism re-checks the proof for each verified domain (DNS TXT, HTML meta tag, or .well-known file — whichever was used at add time). Default is 30 days.

Public profiles

  • Enable public profiles — master kill switch. When off, both /u/<username> and /t/<team-id> always return 404 regardless of any individual user/team opt-in. See Public Profiles.
  • User profile defaults / Team profile defaults — the per-field defaults applied to users (or teams) who haven't picked a value of their own. Changing a default propagates immediately to inheriting profiles; it never overrides an explicit user/team choice.

Team join requirements

A site-wide floor that every team is forced to meet, regardless of the team-level flag. Owners can opt their team in further, never out below the floor.

  • Default require 2FA — every team requires at least one TOTP authenticator or passkey enrolled.
  • Default require verified email — every team requires a verified primary email.

WARNING

Turning these on retroactively forces every existing member to satisfy the factor — anyone not enrolled is locked out of team operations until they do. Notify members before flipping.

Sub-teams (nested teams)

The whole sub-team feature is configurable from this same page. Defaults match how most operators want it; turn knobs off to scope the feature down. See Teams → Sub-teams for the full semantics.

  • Enable sub-teams — master switch. Off = every sub-team API returns 403, the Sub-teams tab is hidden in the UI, and parent_team_id rows in the database are ignored for inheritance (preserved but inert, so you can re-enable without data loss).
  • Maximum nesting depth — hard cap, validated 1–20. The default of 5 is enough for most orgs; raising it costs an extra DB round-trip per level on every authorization check.
  • Inherit team membership — when on (default), a member of a parent team is treated as a member of every descendant with at least the same role (effective = max(direct, inherited)). Off = direct memberships only — sub-team admins must be added explicitly.
  • Inherit verified domains — when on (default), ancestor-owned domains appear on sub-team listings as read-only entries (inherited_from = …) and a sub-team adding a sub-domain of an ancestor's verified apex is auto-verified. Off = sub-teams must re-verify any domain they want to use.
  • Show sub-teams on public profile by default — sets the default_team_profile_show_sub_teams site default. Each team can still override via Teams → <team> → Settings → Public profile → Sub-teams.

Notifications & Telegram

  • Telegram notification source — slug of an enabled Telegram OAuth source whose bot token is reused to deliver Telegram notifications. Leave empty to disable Telegram delivery (email and webhook delivery still work). See Notifications.

Diagnostics

  • Login-error retention (days) — how long failed-login rows in login_errors are kept before the cron purges them.

Danger zone

Tools that change the shape of the database. Each runs a single batched migration and is idempotent — re-running is safe.

  • Migrate secrets to Secrets Store — encrypts existing site-config secret values (captcha secret, social-source client_secrets, SMTP/IMAP passwords, GitHub README PAT, OAuth app client_secrets). Requires the SECRETS_KEY binding.
  • Migrate D1 secrets — replaces bearer-style values (PATs, OAuth tokens and codes, invite tokens, email-verify codes, 2FA codes, individual backup codes) with HMAC-SHA256 keyed hashes. The plaintext is no longer stored; user-supplied candidates are hashed for WHERE col = ? lookup.
  • Migrate teams to team-as-user rows — backfills synthetic users rows (kind = 'team') for every team so oauth_apps.owner_id joins uniformly.
  • Migrate image-proxy mappings — registers proxy mappings for any avatar / icon URLs that pre-date the closed-mapping image proxy.
  • Migrate recovery codes — re-hashes legacy plaintext backup codes.
  • Site reset — wipe and reinitialize. The destination admin signs an email acknowledgement first; a typo confirmation in the UI then triggers the wipe. This is destructive and requires a configured email provider.

OAuth Sources

Admin → OAuth Sources is where all social login providers are configured. Unlike a simple per-provider on/off toggle, each source is an independently named OAuth connection with its own slug, credentials, and display name. This allows multiple sources of the same provider type (e.g. two GitHub apps, or a Keycloak instance alongside Google).

Source fields

FieldDescription
SlugUnique URL key — appears in the callback URL as /api/connections/<slug>/callback
ProviderBase OAuth type (GitHub, Google, Microsoft, Discord, Telegram, X, Generic OIDC, Generic OAuth 2)
Display nameLabel shown on login/register buttons
Client IDOAuth application client ID
Client SecretOAuth application client secret
EnabledToggle to show/hide the source on login without deleting it

Generic OIDC sources

When provider is Generic OpenID Connect, three additional endpoint URL fields appear:

  • Issuer URL — the provider's base issuer (e.g. https://accounts.example.com). Click Discover to auto-fetch the three endpoints from {issuer}/.well-known/openid-configuration.
  • Auth URL — OAuth 2.0 authorization endpoint
  • Token URL — token exchange endpoint
  • Userinfo URL — endpoint to fetch the user profile

An optional Scopes field allows customizing the requested scopes (default: openid email profile).

Generic OAuth 2 sources

When provider is Generic OAuth 2, the same Auth URL / Token URL / Userinfo URL fields appear but there is no OIDC discovery. All three must be filled in manually.

Callback URL

Each source's callback URL is:

https://<your-prism-domain>/api/connections/<slug>/callback

Register this URL in the provider's developer console when creating the OAuth app.

For detailed per-provider setup instructions see Social Login Setup.

Invites

When registration mode is invite-only, the Invites tab lets you create and revoke invite tokens.

  • Email (optional) — restrict the invite to a specific email address
  • Max uses — leave empty for unlimited
  • Expires after (days) — optional expiry

Invite links are copyable and can be shared directly. Email delivery requires a configured email provider.

Users

The user table is searchable and sortable. Click a user row to open the detail view.

Actions on a user

ActionEffect
Change roleToggle between user and admin
DeactivatePrevents login; existing tokens remain valid until expiry
Mark email verifiedManually verify without sending an email
DeletePermanently deletes the user and all their data (cascades to sessions, apps, connections, etc.)

Deleting a user is irreversible. Their OAuth apps are also deleted, which will break any third-party integrations that used those apps.

Applications

The app table lists all OAuth apps across all users, including:

  • Owner username
  • Verification status
  • Active/inactive status

App moderation

ActionEffect
VerifyMarks the app with a verified badge visible on the consent screen
DeactivatePrevents the app from completing new authorization flows. Existing tokens continue to work.

Verified apps are shown with a checkmark on the consent screen, indicating they have been reviewed by an admin.

Teams

Admin → Teams lists every team across the instance with its owner, member count, and join-requirement flags.

ActionEffect
InspectView members, owned apps, and verified domains for the team
DisbandRemove the team. Team-owned apps are reassigned to the team's owner so they survive the cascade

disable_user_create_team hides the "New team" button from non-admins. With it on, only admins can create teams (existing teams keep working).

Request Logs

Admin → Request Logs is a paginated, filterable table of every Worker request — method, path, status, duration, IP, user agent, optional user ID (when authenticated), and the matching audit log row if any.

  • Filter by method, status range, path prefix, or user.
  • Spectate opens a tail-style live view that auto-refreshes.
  • Export CSV dumps the current filter to CSV.
  • Details for a single request shows the full request/response timing and any audit-log linkage.
  • Purge drops the entire table (or just the spectate buffer).

Request logs are independent of audit logs: a request hit may or may not result in an audit-worthy state change, and audit log entries for cron-driven actions have no associated request row.

Login Errors

Admin → Login Errors lists failed authentication attempts (wrong password, wrong TOTP, expired challenge, etc.) with their error code, identifier, IP, and metadata. The login_error_retention_days config controls how long rows are kept before the cron sweeps them.

Audit Log

The audit log is a paginated, append-only list of significant events:

EventTriggered by
user.registerSuccessful registration
user.loginSuccessful login
user.login.failedFailed login attempt
user.logoutLogout
user.deleteAccount deletion
user.password_changedPassword changed via Profile → Security
totp.enabledTOTP authenticator setup completed
totp.disabledTOTP authenticator removed
passkey.registeredNew passkey added
passkey.deletedPasskey removed
gpg.key_addedGPG public key registered
gpg.key_deletedGPG public key removed
gpg.loginSigned-in via GPG challenge
oauth.authorizeUser approved an OAuth app
oauth.tokenToken issued
oauth.consent_revokedUser revoked an app's access
oauth.2fa.verifyStep-up 2FA confirmed
oauth.2fa.sudo_revokedUser revoked a sudo grace window
team.createdTeam created
team.member_addedMember joined a team (invite or admin add)
team.member_removedMember left or was removed
team.transferredTeam ownership transferred
domain.added / verified / deletedDomain lifecycle
connection.added / removedSocial connection lifecycle
webhook.create / update / deleteWebhook lifecycle
oauth_source.create / update / deleteOAuth source lifecycle
invite.create / revokeSite invite lifecycle
admin.config.updateSite config changed
admin.user.updateAdmin changed a user
admin.user.deleteAdmin deleted a user
admin.app.updateAdmin verified or deactivated an app
admin.team.deleteAdmin disbanded a team
admin.secrets.migrateSite-config or D1 secrets migration ran
admin.reset.*Site-reset request / cancel / confirm

Each entry records the acting user_id (or null for system actions), the action, optional resource_type / resource_id, a metadata JSON object, and the ip_address.

For the full OAuth scope reference, see OAuth → Scopes and Teams → OAuth scopes.

Released under the GPL-3.0 License.