Skip to content

Authentication modes

Pitot supports local sign-in and OIDC, selected by PITOT_AUTH_MODE. Both use the same roles and capabilities, browser sessions and account suspension rules.

Active users sign in with an emailed code when SMTP is configured. Without SMTP, an operator with access to the control database mints a single-use link with pitot login-link <email>. Administrators manage accounts and roles through the HTTP API. There is no user-management browser page. See account profiles and suspension for account APIs and local recovery for invitation delivery and recovery.

PITOT_SELF_SIGNUP allows verified email addresses to create viewer accounts. It defaults to false and requires complete SMTP configuration. Self-signup is available only after the first administrator completes setup. Viewers can read all telemetry, manage their own dashboards and run the built-in agent. The built-in agent sends requests to your configured model provider and can incur charges. Enabling self-signup grants that access to anyone who can verify an email address.

An identity provider handles sign-in. Group and domain allowlists are evaluated on every login, including for linked identities. Removing a user’s eligibility blocks their next login; it does not revoke a browser session already issued. Suspending the account in Pitot blocks access and revokes its sessions.

Pitot starts even when the identity provider is unreachable, so ingest, MCP and existing sessions keep working. Discovery retries in the background with backoff capped at 1 minute. Until it succeeds, the OIDC sign-in routes return 503 and the oidc check in /readyz reads degraded; no restart is needed. Invalid configuration, including a provider that answers for a different issuer at startup, still fails startup.

If neither PITOT_OIDC_ALLOWED_GROUPS nor PITOT_OIDC_ALLOWED_DOMAINS is configured, no eligibility allow policy is enforced. Membership is then managed locally, unless a role mapping is set; see Who owns accounts.

PITOT_OIDC_AUTO_PROVISION creates eligible accounts on first sign-in after administrator setup. It requires an allowed group or domain. The role comes from the configured group mapping, falling back to PITOT_OIDC_DEFAULT_ROLE (viewer).

PITOT_OIDC_EMAIL_VERIFICATION=required requires a verified email. The issuer policy trusts the configured issuer and requires an allowlist. See Microsoft Entra ID group overage if group claims are omitted.

If PITOT_OIDC_ADMIN_GROUPS or PITOT_OIDC_OPERATOR_GROUPS is configured, the provider reapplies the mapped role on every login. Users outside mapped groups receive PITOT_OIDC_DEFAULT_ROLE. A role change revokes browser sessions and writes an audit event in the same transaction.

Without a role-group mapping, administrators manage roles inside Pitot and OIDC sign-in preserves them. Reconciliation cannot demote the last active administrator; promote a second administrator before applying that mapping.

Role mapping moves account ownership to the identity provider. GET /api/auth/status reports the mode as account_ownership, and Settings explains it to signed-in users.

account_ownershipConfigurationBrowser user routes refuse
localNo role mapping, with or without a SCIM tokenNothing
identity_provider_rolesRole mapping and PITOT_OIDC_AUTO_PROVISION, no SCIM tokenCreating a user and changing a role
identity_provider_roles_no_provisioningRole mapping, no PITOT_OIDC_AUTO_PROVISION, no SCIM tokenCreating a user and changing a role
identity_provider_directoryRole mapping and a SCIM tokenCreating and deleting a user; changing a role, status or email

To add users under a role mapping, set PITOT_OIDC_AUTO_PROVISION with an allowed group or domain, or configure a SCIM token. With neither, nobody new can get an account: POST /api/users is refused and a first login is denied. Pitot still starts, logs a warning, and Settings says so.

A refused request returns 403 with the code managed_by_identity_provider and changes nothing. On PATCH /api/users/{id}, malformed JSON returns 400 because the body must be decoded to see whether email is present. SCIM, login-time role reconciliation and provisioning, display-name edits, POST /api/users/{id}/access/revoke, first-administrator setup and pitot login-link --break-glass keep working in every mode. Without a SCIM token, administrators can still suspend, reactivate, delete and change the email of a user.

Entra can omit the groups claim when an account has too many memberships. If the token signals group overage and any group-based policy is configured, Pitot denies login. It does not fetch group membership from Graph. Configure the application registration to emit only groups assigned to the application. Domain-only allow policies are unaffected.

Administrators create active accounts with POST /api/users, supplying email, display_name and role. Edit profiles with PATCH /api/users/{id}; change roles with PATCH /api/users/{id}/role and {"role":"viewer"}. Suspend with PATCH /api/users/{id}/status and {"status":"suspended"}; restore with {"status":"active"}. Unknown fields and invalid statuses return 400. Email, role and status changes revoke browser sessions. Suspension blocks local and OIDC sign-in and revokes the user’s OAuth tokens. Reactivation revokes any that remain, so a reactivated user signs in again. The last active administrator cannot be demoted, suspended or deleted. With an OIDC role mapping some of these routes are refused; see Who owns accounts.

GET /api/users returns a users array. DELETE /api/users/{id} removes an account and cascades to its sessions, OAuth grants and owned dashboards. Use suspension when the account and its data should remain.

POST /api/users/{id}/access/revoke revokes all browser sessions and OAuth tokens for that user. It preserves the account. A user who remains eligible can sign in again.

These routes accept only a browser session with the admin role. Pitot has no API key for user administration. Ingest tokens, metrics tokens and MCP OAuth tokens cannot substitute for that session.

Sign in as an administrator. In browser developer tools, find an authenticated request and copy its session cookie into a temporary Netscape-format cookie jar supported by curl. Keep it private and outside source control. A script can use that jar while the session remains valid:

Terminal window
curl --fail-with-body --cookie "$COOKIE_JAR" \
https://pitot.example.com/api/users
curl --fail-with-body --cookie "$COOKIE_JAR" \
-H 'Pitot-Request: 1' -H 'Content-Type: application/json' \
-X POST https://pitot.example.com/api/users \
--data '{"email":"[email protected]","display_name":"New user","role":"viewer"}'

Replace the origin and set COOKIE_JAR to that private jar. Mutating requests need Pitot-Request: 1 or a matching Origin/Referer; cross-site browser requests are rejected. Session expiry or revocation requires signing in again. Delete the temporary jar when finished and sign out to revoke that session.

Set PITOT_SCIM_TOKEN (or PITOT_SCIM_TOKEN_FILE) so an identity provider or automation can create, update and deactivate accounts. It requires PITOT_AUTH_MODE=oidc and at least 32 characters. Without a token the routes do not exist.

With a token, Pitot prints no setup banner and /api/auth/setup returns 410; the first administrator comes from SCIM.

Pitot implements a small subset of SCIM 2.0 under /scim/v2. Send Authorization: Bearer <token>. Responses and errors use application/scim+json and the RFC 7644 formats.

  • POST /scim/v2/Users creates an account from externalId, userName (the email address) and one roles value of admin, operator or viewer. The account and its identity at the configured issuer are written together, so the first OIDC login needs no match by email. Repeating the request returns the existing account with 200 and changes nothing.
  • GET /scim/v2/Users?filter=externalId eq "<subject>" finds an account. userName eq "<email>" also works. Other filters return invalidFilter.
  • PATCH /scim/v2/Users/{id} changes active, the role, the email and displayName. Resending the current values writes nothing.

An email held by a different account returns 409. While there are no users, only an active administrator can be created. Deactivating or demoting the last active administrator returns 409.

Deactivation revokes the user’s browser sessions and MCP tokens. Reactivation restores neither, and keeps the identity link. See docs/authentication.md for the full contract.

Run with the server’s environment and data directory:

Terminal window
pitot login-link [email protected]
# For Docker:
docker exec pitot pitot login-link [email protected]

The URL is printed to stderr, expires in 15 minutes and works once. Issuance and redemption are audited. The command supports an online server but refuses missing or suspended users, a missing control database and OIDC mode without --break-glass.

Creating a local user without SMTP returns 201, invite_delivery: "not_configured" and login_link_required: true. Mint a link for that active user and deliver it through a trusted channel. If configured SMTP fails delivery, the API reports an error. Repair SMTP after regaining access.

If the only administrator is suspended or has an unusable address, stop Pitot and back up the control database. Repair that existing account’s email or status in SQLite, preserving its ID and owned data. Restart and mint a login link for the active account.

If the identity provider is unavailable, run this with the server’s environment and data directory to issue a one-time link for an active administrator:

Terminal window
pitot login-link --break-glass [email protected]
# For Docker:
docker exec pitot pitot login-link --break-glass [email protected]

Set PITOT_AUTH_CODE_SECRET with at least 32 characters before you need this. OIDC mode does not require it at startup, but without it the command fails and the server keeps login links disabled. Pitot starts while the provider is unavailable, so this also works after a restart during the outage.

The command refuses other roles, suspended users and local mode. The URL expires in 15 minutes and works once. In OIDC mode, the server accepts only these links and re-checks that the account is still an active administrator. Issuing and redeeming write the distinct audit events login_link.break_glass_issued and login_link.break_glass_redeemed. If the redemption event cannot be written, Pitot creates no session and the request fails; the link is spent, so issue a new one.

/api/auth/link/verify is rate limited per client address. Behind a reverse proxy, set PITOT_TRUSTED_PROXY_CIDRS; otherwise every request shares the proxy’s address and one limit, and other traffic can block recovery.

Open Connect telemetry → Connected agents in the account menu to review and disconnect one OAuth client. Disconnecting revokes that client’s access and refresh tokens for your account. Reconnect by authorizing it again. Other clients and browser sessions remain connected. See MCP connections for API routes.

See namespace access limits. Dashboard access is scoped to the owner for every role, including administrators.