Skip to content

Authentication

Orbital has two separate authentication systems: platform authentication for infrastructure management and orb-level authentication for application users.

Purpose: Manage the Orbital platform itself - create/manage orbs Storage: NATS KV buckets (platform_admins, platform_invitations, platform_credentials) Routes: /api/_auth/* Bootstrap: Auto-creates invitation on startup if no admins exist CLI: orbital admin login/register/logout/status

Platform admins are infrastructure operators who manage orbs via /api/_orbs endpoints.

Purpose: Application-level users (operators, players, etc.) Storage: NATS entries within orb (e.g., accounts/operators) Routes: /api/{orbId}/* Bootstrap: Orb-specific (e.g., access.init primitive) CLI: orbital auth <orbId>/signin or browser at /api/<orbId>/signin

Orb users access application-specific APIs defined by the orb’s schemas and primitives.

This page focuses on platform authentication. For orb-level auth implementation, see Passkey Authentication Flow.

sequenceDiagram
    participant Admin
    participant Browser
    participant API
    participant Device

    Admin->>Browser: Follow invitation link or CLI
    Browser->>API: POST /api/_auth/passkey/begin?invite={code}
    API->>Browser: Challenge + Session ID
    Browser->>Device: Create passkey
    Device->>Admin: Biometric prompt
    Admin->>Device: Authenticate
    Device->>Browser: Signed credential
    Browser->>API: POST /api/_auth/passkey/complete
    API->>Browser: Success + Platform JWT
sequenceDiagram
    participant Admin
    participant Browser
    participant API
    participant Device

    Admin->>Browser: Click "Sign in" or orbital admin login
    Browser->>API: POST /api/_auth/passkey/begin
    API->>Browser: Challenge + Session ID
    Browser->>Device: Sign challenge
    Device->>Admin: Biometric prompt
    Admin->>Device: Authenticate
    Device->>Browser: Signed response
    Browser->>API: POST /api/_auth/passkey/complete
    API->>Browser: Success + Platform JWT
import { startRegistration } from '@simplewebauthn/browser';
// Step 1: Begin registration with invitation
const invitationCode = 'abc123xyz';
const beginResponse = await fetch(`/api/_auth/passkey/begin`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
invite: invitationCode
}),
});
const { sessionId, options } = await beginResponse.json();
// Step 2: Create credential with device
const credential = await navigator.credentials.create({ publicKey: options.publicKey });
// Step 3: Complete registration
const completeResponse = await fetch('/api/_auth/passkey/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId,
credential: /* formatted credential */
}),
});
const { token, admin } = await completeResponse.json();
// Store token for authenticated requests to /api/_orbs
import { startAuthentication } from '@simplewebauthn/browser';
// Step 1: Begin login
const beginResponse = await fetch('/api/_auth/passkey/begin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: '[email protected]' }),
});
const { sessionId, options } = await beginResponse.json();
// Step 2: Sign challenge with device
const credential = await navigator.credentials.get({ publicKey: options.publicKey });
// Step 3: Complete login
const completeResponse = await fetch('/api/_auth/passkey/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId,
assertion: /* formatted credential */
}),
});
const { token, admin } = await completeResponse.json();
// Token is used for /api/_orbs management endpoints

Bootstrap Mode (First-Time Platform Setup)

Section titled “Bootstrap Mode (First-Time Platform Setup)”

When deploying Orbital for the first time with no platform admins, the system enters bootstrap mode to allow initial admin registration.

  1. Server starts with no platform admins → creates bootstrap invitation automatically
  2. Bootstrap invitation logged to console with token and signin URL
  3. Invitation endpoints accessible without authentication (only in bootstrap mode)
  4. First admin registers using invitation token → bootstrap mode disabled
  5. Management endpoints (/api/_orbs) now require platform admin JWT

The recommended way to bootstrap:

Terminal window
# 1. List invitations (no authentication required in bootstrap mode)
orbital invite ls
# Output:
# ● abc123xyz
# Status: active
# Expires: 2025-01-12T10:30:00Z
# 2. Register with invitation token (opens browser)
orbital admin register abc123xyz
# Browser opens → complete passkey registration → first platform admin created
# 3. Authenticate the CLI
orbital admin login
# 4. Verify authentication
orbital admin status

For programmatic access:

Terminal window
# Get bootstrap invitation (works in bootstrap mode - no auth required)
curl https://your-api.com/api/_auth/invitations
# Response:
# [
# {
# "token": "abc123xyz",
# "status": "active",
# "createdAt": "2025-01-05T10:30:00Z",
# "expiresAt": "2025-01-12T10:30:00Z"
# }
# ]

Then open the registration URL in a browser:

https://your-api.com/api/_auth/signin?invite=abc123xyz

Bootstrap mode is safe because:

  • Empty system - No data exists to protect when adminCount = 0
  • Invitation ≠ access - Still requires passkey registration (phishing-resistant)
  • Self-disabling - Endpoints return 401 after first admin exists
  • Time-limited - Bootstrap invitations expire in 7 days
  • Audit trail - All bootstrap access attempts are logged
  • NATS-native - All data stored in NATS KV buckets, no external database

Key difference from previous approach: No bootstrap secret header needed. The system simply checks admin count - if zero, allow unauthenticated access to invitation listing. This is simpler and just as secure.

Development/Staging:

Terminal window
# Start server
docker compose up -d
./orbital serve
# Check logs for bootstrap invitation
# 2025-01-05T10:30:00Z WARN platform bootstrap mode reason="no admins found"
# 2025-01-05T10:30:00Z WARN invitation created code=abc123xyz expires=7d
# 2025-01-05T10:30:00Z WARN register at url=http://localhost:52000/api/_auth/signin?invite=abc123xyz

Production:

Terminal window
# Same process, bootstrap invitation logged
./orbital serve
# Admin uses CLI from secure workstation
orbital invite ls
orbital admin register <token>
orbital admin login

For high-security environments, monitor logs for bootstrap invitation generation and immediately use it to create the first admin.

Platform admin JWTs include:

{
"sub": "admin-id-xid",
"email": "[email protected]",
"name": "Admin Name",
"type": "platform",
"iat": 1704451200,
"exp": 1704537600
}

The type: "platform" field distinguishes platform JWTs from orb-level JWTs.

For application-level authentication (operators, players), orbs use the access domain primitive:

kind: Orb
id: accounts
uses:
- service: access
handlers: [passkey, invitations, bootstrap]
modules:
- id: operators
schemas:
- id: operator
fields: [email, displayName, roles]

Users access orb signin at /api/accounts/signin - completely separate from platform auth.