Skip to main content

Documentation 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.

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 the 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 logical L2 root:
PathContents
L2/<username>/user.yamlUser metadata: full_name, display preferences
L2/<username>/meta/password.jsonSealed SCRAM verifier envelope — backend-keyed, not self-sufficient
L2/<username>/meta/logins.jsonActive session verifier records plus signed metadata including sessionId
L2/<username>/meta/user_crypto.jsonWrapped browser-owned user master key record
L2/<username>/mod/User-owned module overrides
Do not hand-author meta/password.json. Only backend helpers that hold the seal key can produce accepted payloads. Editing it by hand will break login for that user.
On disk these paths resolve to 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 an HttpOnly, SameSite=Strict cookie on successful login.
PropertyValue
Cookie namespace_session
HttpOnlyYes — not accessible to JavaScript
SameSiteStrict
Path/
Max age30 days
The cookie is a bearer token. In multi-user runtime the cookie value also carries the username hint so the backend loads only that user’s auth files — older token-only cookies that lack the hint are cleared. The backend stores only a verifier plus signed metadata in 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

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.
/login   → login form → space_session cookie issued
/logout  → cookie cleared → redirect 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

1

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.
2

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.
3

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.
4

Logout / stale session

space.utils.userCrypto.clearSession() removes both the sessionStorage entry and the localStorage blob. Stale blobs (wrong sessionId) force an automatic logout.

Security Properties

  • Backend compromise (app tree only) is not enough: meta/user_crypto.json does 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.json and deletes the backend server share — it cannot silently rewrap the key.
  • meta/user_crypto.json may 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 (not missing) so login does not silently replace a key that may still protect existing ciphertext.

Frontend API

The userCrypto surface is exposed at space.utils.userCrypto:
// Encrypt a sensitive value before writing to a file
const ciphertext = await space.utils.userCrypto.encryptText('sk-my-api-key');
// → 'userCrypto:base64encodedciphertext'

// Decrypt on load
const plaintext = await space.utils.userCrypto.decryptText(ciphertext);

// Check current status
const status = space.utils.userCrypto.status();

// Clear caches (e.g. after password change)
space.utils.userCrypto.clearSession();
See the Browser API reference for the full method signatures.

Password Management

Self-Service Password Change

Authenticated users change their password through /api/password_change, not by directly writing meta/password.json. The endpoint:
  1. Validates the current password against the opened sealed verifier.
  2. Rewrites meta/password.json with the new sealed verifier.
  3. Rewraps meta/user_crypto.json when the current session has unlocked browser crypto.
  4. Clears meta/logins.json — all active sessions are invalidated.
  5. 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 invalidate user_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

When CUSTOMWARE_GIT_HISTORY=true, each L2/<username>/ root is a local Git repository. The L2 .gitignore intentionally excludes auth files:
meta/password.json
meta/logins.json
meta/user_crypto.json
This means:
  • 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

URLPurpose
/loginPublic login form (or disabled copy when LOGIN_ALLOWED=false)
/logoutClears space_session and redirects to /login
/enterFirmware-backed launcher; authenticated sessions and SINGLE_USER_APP only
/api/login_challengeReports ready, missing, or invalidated userCrypto state
/api/loginIssues session cookie and returns wrapped key record + server share
/api/password_changeAuthenticated self-service password rotation
/api/user_crypto_session_keyReturns session-derived wrapping key for localStorage blob

Build docs developers (and LLMs) love