Space Agent’s authentication layer is file-backed and serverless in the sense that there is no separate auth database. User credentials, active sessions, and browser-owned encryption key records all live as sealed files in theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/agent0ai/space-agent/llms.txt
Use this file to discover all available pages before exploring further.
L2/<username>/meta/ folder of the layered app tree. The backend holds the key material needed to unseal those files — so the files alone are never sufficient for unauthorized access — but the logical storage paths are part of the same customware layer model that governs every other writable file in the system.
User Storage Layout
Each user’s auth-relevant files live under their logicalL2 root:
| Path | Contents |
|---|---|
L2/<username>/user.yaml | User metadata: full_name, display preferences |
L2/<username>/meta/password.json | Sealed SCRAM verifier envelope — backend-keyed, not self-sufficient |
L2/<username>/meta/logins.json | Active session verifier records plus signed metadata including sessionId |
L2/<username>/meta/user_crypto.json | Wrapped browser-owned user master key record |
L2/<username>/mod/ | User-owned module overrides |
app/L2/... by default, or to CUSTOMWARE_PATH/L2/... when the CUSTOMWARE_PATH environment variable is configured.
Backend-Only Auth Secrets
The backend auth keys are never stored in the logical app tree:SPACE_AUTH_PASSWORD_SEAL_KEY
Seals and unseals SCRAM password verifier envelopes.
SPACE_AUTH_SESSION_HMAC_KEY
Signs and verifies session records in
meta/logins.json. Also used to derive the userCrypto session wrapping key.SPACE_AUTH_DATA_DIR
Override root for local auth key storage and per-user
userCrypto server shares.server/data/auth_keys.json
Local fallback when
SPACE_AUTH_DATA_DIR is unset. This file is gitignored.node space supervise injects these keys into child serve processes so CLI-created users and supervised runtime children all share one verifier-sealing key source.
Session Contract
Space Agent issues anHttpOnly, SameSite=Strict cookie on successful login.
| Property | Value |
|---|---|
| Cookie name | space_session |
HttpOnly | Yes — not accessible to JavaScript |
SameSite | Strict |
| Path | / |
| Max age | 30 days |
meta/logins.json, never the plaintext session secret.
Each session record includes a backend-generated sessionId that the browser uses to bind its session-scoped userCrypto cache to the active login. Unsigned or expired records are rejected; revocation deletes the stored record immediately.
Authentication Modes
- Multi-user (default)
- Single-user (SINGLE_USER_APP=true)
- LOGIN_ALLOWED=false
- Guest users (ALLOW_GUEST_USERS=true)
Users log in at
/login. On success the server issues the space_session cookie and the browser begins the userCrypto unlock flow. The /logout route clears the cookie and redirects to /login.userCrypto — Per-User Browser Key
userCrypto provides session-scoped encryption for browser-owned secrets such as API keys. The design ensures that neither the server alone (which holds the wrapped key record) nor the client alone (which holds the session-derived wrapping key) can decrypt the user’s master key without a successful authenticated session.
How It Works
Login
/api/login returns the wrapped key record from meta/user_crypto.json plus a one-time server share. The browser combines them to reconstruct the master key for this session.Key storage
The unlocked master key lives in per-tab
sessionStorage (keyed by username + sessionId). An encrypted localStorage blob is also maintained so a new tab can restore the key without re-running the full login flow.Session key endpoint
/api/user_crypto_session_key returns the current session-derived wrapping key by HMACing the live sessionId with the backend session secret. The browser uses this key to encrypt/decrypt the localStorage blob without storing the wrapping key at rest.Security Properties
What protects the user crypto key?
What protects the user crypto key?
- Backend compromise (app tree only) is not enough:
meta/user_crypto.jsondoes not include the backend-only server share. - Password knowledge alone is not enough: the browser also needs the backend-only server share released during a successful login.
- Admin/CLI password reset invalidates
user_crypto.jsonand deletes the backend server share — it cannot silently rewrap the key. meta/user_crypto.jsonmay include a backend-sealed server-share envelope for multi-instance recovery, but that envelope is not usable without the backend auth keys.- Accounts with a wrapped record but no recoverable server share are treated as
invalidated(notmissing) so login does not silently replace a key that may still protect existing ciphertext.
Frontend API
TheuserCrypto surface is exposed at space.utils.userCrypto:
Password Management
Self-Service Password Change
Authenticated users change their password through/api/password_change, not by directly writing meta/password.json. The endpoint:
- Validates the current password against the opened sealed verifier.
- Rewrites
meta/password.jsonwith the new sealed verifier. - Rewraps
meta/user_crypto.jsonwhen the current session has unlocked browser crypto. - Clears
meta/logins.json— all active sessions are invalidated. - Clears the current browser auth cookie.
The
password CLI command also clears all active sessions when setting a new password. This is intentional: since the browser crypto key is rewrapped, old sessions cannot decrypt values encrypted with the previous key.Admin / CLI Reset
Admin or CLI password resets rewrite the sealed verifier and clear active sessions but cannot rewrap the browser-owned key. They invalidateuser_crypto.json and delete the backend server share instead. The user will see locked placeholders for any previously encrypted values until they log in and re-enter the affected secrets.
L2 History and Auth Files
WhenCUSTOMWARE_GIT_HISTORY=true, each L2/<username>/ root is a local Git repository. The L2 .gitignore intentionally excludes auth files:
- Rollback never restores an old password verifier, old session records, or an old wrapped browser key.
- Rolling back to a past commit cannot log a user out or silently downgrade their crypto state.
meta/auth files are always preserved at their current state regardless of which commit HEAD points to.
Entry Points
| URL | Purpose |
|---|---|
/login | Public login form (or disabled copy when LOGIN_ALLOWED=false) |
/logout | Clears space_session and redirects to /login |
/enter | Firmware-backed launcher; authenticated sessions and SINGLE_USER_APP only |
/api/login_challenge | Reports ready, missing, or invalidated userCrypto state |
/api/login | Issues session cookie and returns wrapped key record + server share |
/api/password_change | Authenticated self-service password rotation |
/api/user_crypto_session_key | Returns session-derived wrapping key for localStorage blob |