lumo dev
Spawn throwaway organizations — Neon-branch style. Each sandbox is a fully-isolated organization with its own slug, settings, users, OAuth clients, audit log, and webhooks. The server's scheduled cleanup job (app:sandbox:cleanup) deletes any sandbox past its TTL (time to live: how long it exists before it is removed).
lumo dev start --name pr-1234
lumo dev list
lumo dev stop sandbox-pr-1234-7a3b21
Spawning sandboxes is the Developer sandboxes add-on: $199 / month, available on the Business plan only, with up to 10 live sandboxes per owner. An organization admin adds it on Settings → Billing. Without it lumo dev start answers 403 feature_not_in_plan and the CLI prints the billing page to go to; lumo dev list and lumo dev stop keep working so existing sandboxes can always be torn down. See Billing → Add-ons.
When to use this
| Workflow | Why a sandbox |
|---|---|
| PR previews | Spin up sandbox-pr-1234 per PR, point the preview deploy at it, tear down on merge. |
| Test users | Create dummy users without polluting your real organization. |
| OAuth client experiments | Rotate redirect URIs, try sender-constrained tokens (DPoP), pushed authorization requests (PAR) or the FAPI 2.0 high-security profile without affecting prod clients. |
| Webhook receiver development | Generate events freely; nothing leaks to real subscribers. |
| Demo / talk | Hand a unique sandbox to each audience member. |
Commands
lumo dev start
Spawn a new sandbox organization.
lumo dev start [--name <suffix>] [--ttl-hours <hours>]
| Flag | Default | Notes |
|---|---|---|
--name <suffix> | (server picks dev) | Human-readable hint that becomes part of the slug. |
--ttl-hours <hours> | 24 | Sandbox lifespan. Min 1, max 168 (one week). |
Output (printed to stderr; with -o json, or when piped, the server's {"data": {...}} response is printed to stdout instead):
✓ Sandbox spawned
Slug: sandbox-pr-1234-7a3b21
Base URL: https://app.lumoauth.dev
Expires at: 2026-05-09T17:00:00Z
Use it like any other org:
lumo --org sandbox-pr-1234-7a3b21 users list
curl https://app.lumoauth.dev/orgs/sandbox-pr-1234-7a3b21/api/v1/me -H "Authorization: Bearer ..."
Tear down with: lumo dev stop sandbox-pr-1234-7a3b21
The slug is generated as sandbox-<slugified-name>-<6 hex chars> and is unique. The user who runs start is the sandbox's owner; only the owner can stop it.
lumo dev list
List your active sandboxes (yours only — other users' sandboxes are not shown).
lumo dev list
Outputs a table with slug, name, created-at, expires-at. Use -o json for machine-readable output: {"data": [{"slug", "name", "created_at", "expires_at", "owner_email", "spawned_from"}, …]}.
lumo dev stop
Tear down a sandbox.
lumo dev stop sandbox-pr-1234-7a3b21
The server-side handler refuses to delete:
- Organizations that aren't tagged as sandboxes (i.e. real prod organizations).
- Sandboxes owned by someone else.
All three cases (unknown slug, not a sandbox, someone else's sandbox) answer the same 404 Sandbox not found, so the endpoint cannot be used to probe which organization slugs exist.
How sandboxes are stored
A sandbox is just a normal Tenant row whose settings JSON column carries a sandbox envelope:
{
"sandbox": {
"is_sandbox": true,
"owner_user_id": "01HF...",
"owner_email": "jane@acme.com",
"expires_at": "2026-05-09T17:00:00Z",
"spawned_from": "acme-corp"
}
}
A sandbox past expires_at is deleted automatically by the platform.
What gets seeded
Nothing. The sandbox is a blank organization. You set up users, OAuth clients, agents, webhooks via the normal CLI / API / portal surface.
Combining with other commands
# Spawn the sandbox and capture its slug (`-o json` prints the server's
# {"data": {...}} response on stdout)
SLUG=$(lumo dev start --name pr-1234 -o json | jq -r '.data.slug')
# Scaffold a Next.js app pre-wired to the sandbox
lumo init --framework next --dir ./web --org "$SLUG"
# Create a sample user and an agent
lumo --org "$SLUG" users create --email demo@example.com --name "Demo"
lumo --org "$SLUG" agents create --name "PR Bot"
# Deploy preview, run tests, ...
# Tear down
lumo dev stop "$SLUG"