Administrator authentication
SubLane guide: administrator authentication.
SubLane uses global local-login identities and workspace memberships with owner, administrator or member roles. The first-run administrator owns the initial workspace. Sessions are shared across workspaces, while permissions are scoped to the selected workspace. See member access for permissions, invitation links, and account lifecycle. Personal gateway keys are available through the separate API key workflow. Password lifecycle and the local recovery command are described in team controls.
First-time setup
Start SubLane normally and open the UI. If the database has no administrator, /setup shows a welcome page explaining the setup flow. Select Start setup to open /setup/admin. First enter the administrator username and password, then continue to a separate step to name the first workspace. Submitting the second step creates both records and signs you in. No separate initialization key is required.
The setup wizard uses /setup/admin; refreshing it returns to the administrator step. Both setup routes are unavailable after initialization: signed-out visitors go to login, and signed-in users go to their role-appropriate workspace. No onboarding state or account credentials are stored in browser storage.
Complete setup locally before opening the instance to other users. Until initialization completes, anyone who can access the setup page can create the first administrator. The default listener and Compose host port remain bound to loopback.
Usernames use 3–32 ASCII letters, numbers, underscores, or hyphens, starting with a letter or number. They are case-insensitive and stored in lowercase. New passwords use 8–20 Unicode characters and are not trimmed. Login continues to accept existing passwords up to the previous 128-character limit.
The administrator, first workspace, ownership membership, and first session are committed in one transaction. A guarded insert ensures concurrent setup requests cannot replace the first administrator. Initialization closes after the transaction succeeds, and restarting the service does not reopen it.
Legacy development databases from before the consolidated prerelease schema are not upgraded automatically. Preserve the old directory before starting a new instance. Databases already on the consolidated schema receive the additive invitation migration. A leftover setup.key from an earlier development build is unused by the new instance.
Deleting or replacing the database is not a password-recovery procedure. Use the local reset-admin-password --password-stdin command for an existing administrator, or the authenticated change/reset flows described in team controls.
Sessions
- Login uses an opaque, cryptographically random 256-bit token in an HttpOnly, SameSite=Lax, path-root cookie.
- SQLite stores only the token's SHA-256 digest.
- Sessions expire after 12 hours, without sliding renewal.
- At most five sessions are retained per user. A new login replaces only that user's oldest excess session.
- Logout revokes the current session in SQLite before reporting success and clearing the cookie.
- Sessions survive a normal process restart.
- The browser stores theme and language preferences locally, but never session tokens.
The UI waits for the server's session state before showing protected pages. It rechecks state on focus and periodically, and returns to login when a protected API request reports an expired session. Logout cancels pending queries and clears private cached data. Background session checks also clear private data when the user ID or role changes.
Two-factor authentication and passkeys
All users can manage these methods in Preferences (/preferences). They belong to the global login identity and apply across workspaces.
- Authenticator 2FA: select Enable 2FA, confirm your password, scan the QR code or enter the setup key in an authenticator, then confirm a fresh six-digit code. Save the ten recovery codes immediately. Each recovery code substitutes for one authenticator code and can be used once. Codes are shown only when generated; replacing them invalidates all previous codes.
- Password sign-in: after enabling 2FA, a correct password requires a fresh authenticator or unused recovery code before a session is issued. Codes use 30-second periods with one period of clock tolerance; already-used periods cannot be replayed.
- Passkeys: add a named passkey using the device prompt. Up to ten discoverable passkeys can be registered. The browser requires device verification, such as a fingerprint, face or PIN. Sign in with a passkey creates a session without a password or separate TOTP code.
Adding or deleting a passkey, disabling 2FA, and replacing recovery codes require the current password and an enabled second factor. Security changes sign out other browser sessions and invalidate pending enrollment. The current session remains signed in. Password changes and remote member password resets preserve enrolled factors. The local administrator recovery command clears the administrator's TOTP, recovery codes and passkeys as well as revoking sessions; keep host access restricted. See team controls.
Passkeys require HTTPS, except for localhost development, and a browser supporting the WebAuthn JSON APIs. Use a stable hostname: a passkey is tied to that relying-party hostname. Configure SUBLANE_PUBLIC_URL to the exact external origin behind a reverse proxy. A different hostname needs newly registered passkeys.
TOTP secrets are encrypted with the instance's credentials.key; recovery codes are stored as SHA-256 digests. The matching key is included inside new password-encrypted backups; preserve the backup password separately. Optional SUBLANE_CREDENTIALS_KEY_FILE places the live key outside the data directory; see backup and restore. Restore retains enrolled factors but clears sessions and unfinished browser challenges. Enrollment and passkey challenges expire after five minutes, and passkey challenges are single-use and browser-bound. No authenticator setup keys or recovery codes are retained in browser storage.
Reverse proxies and HTTPS
For a network deployment, terminate HTTPS at your reverse proxy and set the externally visible origin:
SUBLANE_PUBLIC_URL=https://sublane.example.com ./bin/sublaneThe origin must contain only the scheme, host, and optional port. Paths, credentials, query parameters, and fragments are rejected. An HTTPS public origin enables Secure cookies even when the internal proxy connection uses HTTP.
Without this setting, same-origin checks use the request's Host and actual TLS state. Forwarded host, protocol, and peer headers are not implicitly trusted. Vite explicitly preserves the original Host on its API proxy for local development.
Keep the backend bound to loopback or a private network behind the proxy. Use HTTPS for network access; the local HTTP default is for development.
Request protection and resource limits
All management routes under /api/ require a valid enabled administrator session by default, except the explicit authentication endpoints. Personal /api/keys and the explicit /api/me/requests, /api/me/password, /api/me/security, /api/me/totp, /api/me/recovery-codes, /api/me/passkeys, /api/me/limits and /api/me/usage endpoints are session-protected for both roles and enforce ownership. Members can read their own session state and sign out, but cannot access administrator management APIs. Static SPA assets remain public; rendering the application bundle does not grant access to management data.
Mutations require an exact same-origin Origin header, reject cross-site Fetch Metadata, and accept JSON only. Auth bodies are limited to 4 KiB, or 64 KiB for passkey completion, and have a five-second read deadline.
Password and passkey login share a five-attempt-per-minute budget per peer. Setup, invitation registration and personal security mutations use a separate enrollment budget with the same limit. Both are shared across workspaces and bounded to 1,024 tracked peers. Verification codes also have a separate account-wide budget: five failed attempts per minute, cleared by a successful verification, with at most 1,024 tracked accounts. Responses include Retry-After when throttled; state is process-local. Configure trusted proxy CIDRs explicitly to use forwarded client addresses. Untrusted forwarded headers never change the peer identity.
Password derivation is limited to two concurrent operations. Argon2id uses 19 MiB, two iterations, and one lane, following the OWASP password storage baseline. These limits are not a total-process memory guarantee.
API
| Endpoint | Access | Result |
|---|---|---|
GET /api/auth/state | Public | initialized and the current user, or null |
POST /api/auth/setup | Uninitialized instances only | Create the administrator, named first workspace, and a session |
POST /api/auth/login | Username/password | Create a new session |
POST /api/auth/register | Valid invitation link | Create a member login and session in the invited workspace |
POST /api/auth/passkey/begin | Public, same origin | Begin a discoverable passkey assertion |
POST /api/auth/passkey/finish | Public, same origin | Verify a passkey and create a session |
GET /api/me/security | Enabled user | Own factor status and passkey metadata |
POST /api/me/totp | Enabled user with password proof | Begin authenticator setup |
POST /api/me/totp/confirm | Same session and challenge | Enable 2FA and return recovery codes |
DELETE /api/me/totp | Enabled user with password and factor proof | Disable 2FA |
POST /api/me/recovery-codes | Enabled user with password and factor proof | Replace recovery codes |
POST /api/me/passkeys/begin | Enabled user with password and factor proof | Begin passkey registration |
POST /api/me/passkeys/finish | Same session and challenge | Register the verified credential |
DELETE /api/me/passkeys/{id} | Credential owner with password and factor proof | Delete an owned passkey |
POST /api/auth/logout | Same-origin request | Revoke the current token and clear its cookie |
GET /api/system | Administrator session | Instance status |
GET/POST /api/members | Administrator session | Paginated list or member creation |
POST /api/members/invitations | Administrator session | Create a one-use invitation valid for seven days |
PATCH /api/members/{id} | Administrator session | Enable or disable a member |
GET /healthz, GET /readyz | Public | Liveness/readiness |
Setup and login accept username and password; login also accepts code when 2FA is enabled; the setup form checks password confirmation before submitting. Logout accepts an empty JSON object. Authenticated state responses include the persisted user ID, username, and admin or member role, and never contain tokens or password hashes. Error responses contain stable codes for localized frontend messages.
For command-line HTTP clients, supply the correct Origin header and JSON content type explicitly. Do not put credentials in shell history or share them in bug reports.