UNPKG

spaps

Version:

Sweet Potato Authentication & Payment Service CLI - Docker Compose orchestrator for local Python/FastAPI SPAPS server with built-in admin middleware

653 lines (488 loc) 29.3 kB
# spaps Test SPAPS auth in a new app before you build auth backend infrastructure. `spaps` is the shortest path from "I have a new frontend" to "this app can hit authenticated SPAPS routes locally." It wraps the local SPAPS server, tells you what auth mode the server is actually in, scaffolds starter code, and provisions a real local application when the server requires one. Run `spaps` with no arguments to get the current operator session, app context, runtime mode, and the next recommended command. ## TL;DR ### The problem You want to wire auth into a new app, but you do not want to design and ship your own auth backend first. ### The shortcut Run SPAPS locally, inspect the current auth mode, then either use local mode directly or provision a real local app with one command. The CLI tells you which mode the server is actually in, so you do not have to guess. ### Why Use `spaps`? | Need | `spaps` gives you | | --- | --- | | "Can I test auth in this app today?" | `spaps local`, `status`, and `quickstart` | | "Do I need a real app key or not?" | Runtime truth from `/health/local-mode` | | "Can you scaffold the wiring for me?" | `spaps create <name> --template ...` | | "I want a working local app, not a fake contract" | Self-service provisioning when `SELF_SERVICE_PASSWORD` is available | | "If provisioning fails, tell me exactly why" | Explicit `scaffold_only` fallback and warnings | | "I want persistent local personas, roles, and entitlements in this repo" | `spaps fixtures ...` and a repo-local `.spaps/` kernel | | "Can an agent tell me why this action is blocked?" | `spaps access check`, `journey run`, `graph`, `explain`, and `contract` | ## No Backend Yet? Start Here ```bash npx spaps npx spaps local npx spaps quickstart --json | jq '.auth' SELF_SERVICE_PASSWORD=your-password npx spaps create demo-app --template react cd demo-app npm install ``` What happens next depends on the server mode: | Server state | What `spaps` does | What you do | | --- | --- | --- | | `local_mode_active: true` | Exposes local-mode hints and test personas | Use the starter against `localhost`; no app key provisioning required | | `local_mode_active: false` and `SELF_SERVICE_PASSWORD` is set | Provisions a real local SPAPS app and writes the working key into `.env.local` | Start wiring auth flows in your app immediately | | Server unreachable or no self-service password | Scaffolds files only and tells you why provisioning was skipped | Bring the server up or rerun with `SELF_SERVICE_PASSWORD` later | This package targets `Node.js >=22`. ## Local Runtime Modes `spaps local` now has two runtime paths: | Runtime path | When it is used | What it does | | --- | --- | --- | | `repo` | You are running from the `sweet-potato` checkout | Uses the repo's Docker Compose stack and source-mounted assets | | `bundle` | You installed `spaps` from npm elsewhere | Uses bundled Docker assets plus the published `spaps-server-quickstart` package | The default is `auto`: use repo assets when they exist, otherwise fall back to the bundled runtime. The bundled runtime defaults to `SPAPS_LOCAL_MODE=true`, so RBAC fixtures and test personas work out of the box. `SPAPS_LOCAL_MODE` is the SPAPS app's canonical local auth/persona switch. `DEVELOPMENT_ENVIRONMENT=local` is only for base quickstart services that wire `LocalAuthMiddleware`. The CLI reads runtime truth from `/health/local-mode`; servers also expose the resolved value in `/health/ready` and the `X-SPAPS-Mode` response header. ## Local Data Sources `spaps local` separates runtime selection from base data selection: | Data source | What it does | | --- | --- | | `empty` | Boot an empty local DB and let migrations create schema | | `prod-cache` | Restore the cached SPAPS production dump before the API starts, then reuse it until the cached dump changes | | `prod-fresh` | Force a fresh production dump fetch, restore it, then boot the API | | `--from-backup <path>` | Restore a specific `.sql.gz` dump file before boot | Restore order is always: restore base dump first, then let the API container run migrations to head on top of it. ## Install Run it without installing: ```bash npx --yes spaps ``` For CI, SSH automation, or the first run on a fresh box, prefer `npx --yes spaps ...` so npm does not block on its install confirmation prompt. Install globally: ```bash npm install -g spaps ``` Add it to a project: ```bash npm install spaps ``` ## Quick Start | Step | Command | Why | | --- | --- | | 1 | `npx spaps` | Inspect operator session, detected app context, runtime mode, and the next command | | 2 | `npx spaps local` | Start the local SPAPS stack when the runtime is not up yet | | 2b | `npx spaps local --data-source prod-cache` | Start the local stack with a cached prod-backed base DB | | 3 | `npx spaps connect` | Authenticate the CLI with the active SPAPS server | | 4 | `npx spaps verify --json` | Confirm the runtime and auth path end to end | | 5 | `npx spaps create my-app --template react` | Scaffold a starter and, when possible, provision a real local app | | 6 | `npx spaps tools --json` | Export the current AI tool contract for agents or tests | | 7 | `npx spaps fixtures apply` | Materialize repo-local personas into Playwright/browser artifacts | ## CLI Surface | Command | Purpose | Common flags | | --- | --- | --- | | `spaps` / `spaps home` | Show operator session, app context, runtime mode, and the next recommended action | `--port`, `--server-url`, `--json` | | `spaps local [stop]` | Start or stop the local server workflow | `--port`, `--runtime-dir`, `--runtime-source`, `--data-source`, `--detach`, `--fresh`, `--from-backup`, `--open`, `--json` | | `spaps status` | Check whether the local server is running | `--port`, `--json` | | `spaps verify` | Run a compact runtime and auth verification pass | `--port`, `--server-url`, `--json` | | `spaps quickstart` | Print quick-start instructions | `--port`, `--json` | | `spaps init` | Create a starter `.env.local` | `--json` | | `spaps create <name>` | Scaffold a SPAPS starter project directory and try local provisioning | `--template`, `--dir`, `--port`, `--force`, `--json` | | `spaps fixtures <subcommand>` | Manage repo-local `.spaps` auth fixtures | `--dir`, `--port`, `--base-url`, `--persona`, `--seed`, `--sync-server`, `--format`, `--force`, `--json` | | `spaps docs` | Browse or search bundled docs | `--interactive`, `--search`, `--json` | | `spaps tools` | Emit the AI tool spec (covers auth-method discovery, auth, stripe, dayrate, email, webhooks, policies, billing, issue-reporting) | `--port`, `--format`, `--json` | | `spaps doctor` | Diagnose local environment, domain mounts, and auth-provider configuration | `--port`, `--server-url`, `--origin`, `--stripe`, `--json` | | `spaps token` | Print the stored access token (silent near-expiry refresh; `--refresh` forces public-client refresh) | `--port`, `--server-url`, `--refresh`, `--json` | | `spaps auth methods` | Print the active app's auth-method matrix from `/api/auth/methods` | `--server-url`, `--port`, `--origin`, `--json` | | `spaps auth mfa-test` | Exercise local TOTP MFA enroll, activate, login challenge, verify, and cleanup | `--email`, `--password`, `--server-url`, `--origin`, `--allow-remote`, `--json` | | `spaps auth passkeys configure` | Create or update the application's canonical passkey RP/origin profile | `--application-id`, `--rp-id`, `--display-name`, repeatable `--allowed-origin`, `--environment`, `--policy-version`, `--confirm-disruptive`, `--json` | | `spaps auth passkeys show` | Read the active passkey profile from the server | `--application-id`, `--rp-id`, `--json` | | `spaps auth passkeys validate` | Validate an RP/origin topology without saving it | Configure options, `--json` | | `spaps auth passkeys disable` | Disable a profile, with confirmation when credentials are affected | `--application-id`, `--rp-id`, `--confirm-disruptive`, `--json` | | `spaps auth passkeys doctor` | Fail closed on passkey discovery, browser-key scope, RP/origin, HTTPS, CORS, SDK, and scaffold mismatches | `--application-id`, `--rp-id`, `--origin`, `--environment`, `--publishable-scope`, `--cors-origin`, `--sdk-version`, `--json` | | `spaps auth passkeys test` | Describe or launch the isolated local Chromium virtual-authenticator ceremony harness without retaining credential bodies | `--rp-id`, `--origin`, `--launch`, `--allow-remote`, `--json` | | `spaps auth sms-test` | Request or verify a local console SMS OTP challenge | `--phone-number`, `--challenge-id`, `--code`, `--server-url`, `--origin`, `--allow-remote`, `--json` | | `spaps dayrate config` | Fetch the active dayrate admin config (requires admin JWT) | `--server-url`, `--port`, `--json` | | `spaps billing status\|attach\|verify` | Inspect, bind, and verify the current app's billing-account resolution (admin/operator) | `--billing-account-id`, `--server-url`, `--port`, `--json` | | `spaps email <verb>` | Thin email control-plane commands over the existing SPAPS email API | `--server-url`, `--port`, `--json`; run `spaps email --help` for verb-specific flags | | `spaps policy list\|create\|delete` | List, create, or delete authorization policies (admin) | `--name`, `--effect`, `--conditions`, `--id`, `--is-active`, `--limit`, `--json` | | `spaps webhook list\|register` | List registered webhooks or register a new outbound webhook | `--url`, `--events`, `--json` | | `spaps issue-reports list-mine` | List issue reports created by the authenticated caller | `--status`, `--limit`, `--offset`, `--json` | | `spaps access check` | Compose an access decision and return reasons, facts, next actions, and sources | `--actor-ref`, `--action`, `--resource-type`, `--resource-ref`, `--entitlement-key`, `--policy-name`, `--usage-feature-key`, `--json` | | `spaps journey run` | Prepare the next safe action for an agent and request command templates when the server recognizes trusted operator context | access flags plus `--include-command-templates`, `--operator-gated`, `--operator-labels`, `--environment`, `--json` | | `spaps graph nodes\|paths\|impact\|refresh` | Inspect or refresh the materialized capability graph for the current app | `--application-id`, `--node-type`, `--query`, `--from`, `--to`, `--node-key`, `--max-depth`, `--include-stale`, `--correlation-id`, `--json` | | `spaps explain <decision-id>` | Fetch a persisted decision trace with graph references | `--server-url`, `--port`, `--json` | | `spaps contract` | Fetch the capability graph client contract | `--server-url`, `--port`, `--json` | `spaps contract --json` includes graph vocabulary fields for agents: `graph_node_types`, `graph_edge_types`, `graph_source_domains`, and `source_domain_notes`. ### Agent Decision Commands The capability commands use a stable JSON envelope when `--json` is present: ```json { "schema_version": "spaps.cli.capability.v1", "command": "access.check", "success": true, "status": 200, "data": {}, "diagnostics": [], "remediations": [], "sources": [] } ``` `access check` exits `0` for a valid denied decision. Agents should inspect `data.allowed`, `data.outcome`, and `remediations` instead of treating denial as a shell failure. Failed API responses preserve server diagnostics: `diagnostics[].code`, `diagnostics[].status`, `diagnostics[].details`, top-level `request_id`, and top-level `remediations` are carried through when the server returns them. `--operator-gated` and `--operator-labels` are compatibility/descriptive inputs. They do not grant operator authority. Mutation command templates require a server-recognized non-publishable key plus an authenticated admin/operator user context; publishable callers cannot self-attest the gate. Exit codes used by the capability commands: | Code | Meaning | | --- | --- | | `0` | Command completed; the decision may still be `allowed: false` | | `2` | Local input or setup error | | `10` | Authenticated API request failed | | `20` | `spaps contract` failed | | `30` | `spaps journey run` failed | ### Email Commands Use nested help for the exact flag surface: ```bash spaps email --help spaps email send --help spaps email preview --help ``` | Verb | Auth | Purpose | | --- | --- | --- | | `send` | API key | Send a transactional email by template key | | `get-template` | API key + JWT | Fetch one template definition | | `preview` | API key + JWT | Render a preview with sample or custom context | | `logs` | API key + JWT | List email logs for the caller's scope | | `list-templates` | API key + JWT + admin | List all templates for the application | | `create-template` | API key + JWT + admin | Create a template | | `update-template` | API key + JWT + admin | Update a template | | `get-override` | API key + JWT | Read the current template override | | `set-override` | API key + JWT + admin | Create or update an override | | `clear-override` | API key + JWT + admin | Delete an override | Example command usage: ```bash spaps --json spaps local --port 3400 --detach spaps local --runtime-source bundle --runtime-dir ./.skillbox/spaps-local spaps local --runtime-source repo --data-source prod-cache spaps local --runtime-source repo --fresh --data-source prod-fresh spaps local --from-backup ~/.cache/spaps/db/prod.sql.gz spaps status --json spaps verify --json spaps create my-app --template react spaps connect --json spaps fixtures apply --base-url http://localhost:5173 spaps fixtures apply --sync-server --base-url http://localhost:5173 spaps fixtures apply --seed --persona dayrate-entitled --base-url http://localhost:5173 spaps fixtures storage-state --persona admin spaps docs --search secure-messages spaps doctor --stripe mock spaps local stop spaps dayrate config --json spaps billing status --json spaps billing attach --billing-account-id 11111111-1111-4111-8111-111111111111 --json spaps billing verify --json spaps email --help spaps email send --help spaps email send --template-key welcome --to user@example.com --context '{"user_name":"Jane"}' --json spaps email preview --template-key welcome spaps email logs --owner-id owner-123 --limit 25 --json spaps email set-override --template-key welcome --subject-override "New subject" --json spaps policy list --json spaps policy create --name allow_admin --effect allow --conditions '{"role":"admin"}' --json spaps webhook register --url https://example.com/hook --events user.created,user.deleted --json spaps issue-reports list-mine --status open --json spaps access check --actor-ref user_123 --action checkout.create --resource-type product --resource-ref dayrate --entitlement-key bookme_paid --json spaps journey run --action admin.delete_user --resource-type user --resource-ref user_123 --include-command-templates --json spaps graph refresh --correlation-id local-refresh --json spaps graph nodes --node-type x402_resource --query dayrate --json spaps graph paths --from actor:user_123 --to x402_resource:dayrate --json spaps explain 11111111-1111-4111-8111-111111111111 --json spaps contract --json ``` ## CLI Auth `spaps connect` is an alias for `spaps login`, and both use the active FastAPI device-flow contract: - device authorization at `/api/cli/device/*` - authenticated follow-up calls at `/api/auth/*` - standard SPAPS response envelopes For users with activated TOTP MFA, `spaps connect` keeps polling the device token endpoint while the browser approval page completes the second factor. The approval page handles `mfa_required` from `/api/cli/device/verify` by retrying that endpoint with `challenge_id`, `challenge`, and either `totp_code` or `recovery_code`; the CLI does not need an MFA flag. The CLI no longer assumes a built-in `spaps-cli` application slug. Resolve the client id in this order: - `--client-id <app-slug>` - `SPAPS_CLI_CLIENT_ID` - repo-local `spaps.app.json` - repo-local `.spaps/app.json` - `/health/local-mode` test application metadata Public-client device-flow sessions store `client_id` and refresh keylessly with `{ refresh_token, client_id }` (no `X-API-Key`). Bearer-only endpoints such as `whoami`, `logout`, and `sessions` work without a repo-local key for those sessions. Confidential credentials (no `client_id`) still need an app key for refresh; the CLI resolves it from: - `SPAPS_API_KEY` - `NEXT_PUBLIC_SPAPS_PUBLISHABLE_KEY` (preferred browser name) - `VITE_SPAPS_PUBLISHABLE_KEY` (preferred browser name) - `NEXT_PUBLIC_SPAPS_API_KEY` (legacy compatibility) - `VITE_SPAPS_API_KEY` (legacy compatibility) - repo-local `.env.local` - repo-local `.env` ### Credential storage Interactive `spaps login` stores the credential pair in the native OS secret store when one is available: macOS Keychain through `security`, or Linux Secret Service through `secret-tool`. It also writes an AES-256-GCM encrypted fallback to `~/.config/spaps/credentials.json`; the random fallback key is kept at `~/.config/spaps/credentials.json.key`. Both files use mode `0600`, their parent directory uses mode `0700`, and credential updates remain atomic. Commands such as `token`, `whoami`, refresh, status, and logout first use an already-unlocked keyring. They never request an unlock: when the OS store is locked or unavailable, they use the encrypted fallback without displaying a desktop or terminal prompt. A legacy version-1 plaintext credentials file remains readable and is replaced with the encrypted format on the next write. Use `SPAPS_CREDENTIALS_PATH` to relocate the encrypted store and `SPAPS_CREDENTIALS_KEY_PATH` to relocate its key. Keep both files private; the separate key mainly prevents credentials from appearing in casual file reads, logs, and backups, and does not protect against an attacker who can read both files as your user. ### Headless token export and forced refresh ```bash # Print the current access token; refreshes silently when near expiry npx spaps token --server-url https://api.sweetpotato.dev # Force a public-client refresh even when the stored access token is still valid npx spaps token --refresh --server-url https://api.sweetpotato.dev ``` Use these against production SPAPS for operator keepwarm and scripted token export. Public-client sessions do not need an app API key on refresh. ### Deprecated: Buildooor proxy keepwarm refresh The Buildooor proxy keepwarm refresh path (session keep-alive through the buildooor.com / `api.buildooor.com` proxy that existed so refresh could run with a server-side app API key) is **deprecated**. Production `https://api.sweetpotato.dev` accepts public-client refresh with `{ refresh_token, client_id }` and no `X-API-Key` when `public_client_config.enabled` is set for the app. Prefer `spaps token` / `spaps token --refresh` (or an equivalent direct public-client refresh client). The proxy keepwarm path is removable. ### Machine and CI auth Automation should not default to operator device-flow sessions: | Pattern | When to use | | --- | --- | | `SPAPS_ACCESS_TOKEN` | Short-lived CI access. Highest precedence; skips `credentials.json` and refresh. | | Confidential secret key (`spaps_sec_...`) + refresh token (no `client_id`) | Refreshable machine/workload automation; refresh sends `X-API-Key`. | Avoid as machine-auth defaults: `spaps login` public-client refresh, publishable keys alone, or public offline access. Public-client refresh is for human/operator CLI sessions; pre-DPoP refresh-token theft is not cryptographically prevented. Examples: ```bash npx spaps connect --client-id my-app npx spaps login --client-id my-app SPAPS_CLI_CLIENT_ID=my-app npx spaps login VITE_SPAPS_PUBLISHABLE_KEY=spaps_pub_demo npx spaps whoami --json ``` Auth diagnostics: ```bash npx spaps doctor --server-url http://localhost:3301 --origin http://localhost:3000 --json npx spaps auth methods --origin http://localhost:3000 --json npx spaps auth passkeys doctor --application-id app_123 --rp-id localhost --origin http://localhost:3000 --environment local --json npx spaps auth passkeys test --rp-id localhost --origin http://localhost:3000 --json npx spaps auth passkeys test --rp-id localhost --origin http://localhost:3000 --launch --json SPAPS_TEST_EMAIL=user@example.com SPAPS_TEST_PASSWORD=correct-horse npx spaps auth mfa-test --json npx spaps auth sms-test --phone-number +15555550100 --json npx spaps auth sms-test --phone-number +15555550100 --challenge-id <id> --code <code> --json ``` `mfa-test` and `sms-test` refuse non-local server URLs unless `--allow-remote` is set. The SMS command cannot read OTP digits from the server because SPAPS does not return them in API responses; with the console provider, read the code from local server logs and rerun with `--challenge-id` and `--code`. `passkeys doctor` emits deterministic, redacted JSON and reports `ready` only when every browser boundary agrees with server discovery and the accepted passkey profile. `passkeys test` never emulates WebAuthn in the terminal: its `--launch` mode delegates to the SDK's isolated local Chromium harness and returns only a bounded receipt. Remote mutation is refused unless the operator passes `--allow-remote`; browser secrets and credential request/response bodies must never be supplied to either command. Still reserved and not finished: - `spaps types` ## Create A Starter Project `spaps create` ships a local-first starter kit rather than a full framework generator. It creates a new directory with: - `spaps.app.json` for the machine-readable SPAPS app contract - `.env.local` pointing at local SPAPS and, when available, a working template-appropriate key - `package.json` with `spaps-sdk` - a small template-specific integration starter When the local server is reachable and `SELF_SERVICE_PASSWORD` is set, `create` also provisions a real local SPAPS application. When that is not possible, it falls back to scaffold-only mode and tells you why. Supported templates: - `nextjs` - `react` - `node` - `vanilla` Examples: ```bash npx spaps create my-app --template react npx spaps create my-api --template node --dir ./services/my-api SELF_SERVICE_PASSWORD=your-password npx spaps create my-app --template react ``` The generated starter is template-aware: - browser templates write a publishable key when provisioning succeeds - server templates write a server key when provisioning succeeds - `spaps.app.json` records provisioning status and application id, but never stores raw keys ## Repo-Local Fixtures Use `.spaps/` when you want persistent local personas for clicking around, Playwright, or app-level auth tests without inventing ad hoc storage keys in each repo. `spaps fixtures init` creates: - `.spaps/app.json` for runtime and browser target settings - `.spaps/users.json` for personas and profile data - `.spaps/roles.json` for RBAC grants - `.spaps/entitlements.json` for entitlement grants - `.spaps/browser/` for generated storage-state and header artifacts Then run: ```bash npx spaps fixtures apply --base-url http://localhost:5173 npx spaps fixtures apply --sync-server --base-url http://localhost:5173 npx spaps fixtures apply --seed --persona issue-reporter-blocked --base-url http://localhost:5173 npx spaps fixtures storage-state --persona admin ``` Use `--sync-server` when a local app needs real SPAPS DB state. It calls the local-only `/api/dev/fixtures/apply` endpoint to upsert fixture users, memberships, and entitlements, then writes ownership metadata to `.spaps/fixtures.lock.json`. It refuses non-local targets and only runs when `/health/local-mode` reports local mode active. The default fixture pack includes shared personas (`user`, `admin`, `premium`), CFO-style `cfo-billing-demo` company entitlements, and HTMA-style `htma-local-auth` invite/template seed state. Browser artifacts keep entitlements as plain keys, while `--sync-server` can send structured grants with resource scope metadata to SPAPS. Use `--seed` when a persona declares backend seed steps in `.spaps/users.json`. Seeding is opt-in, only runs against a reachable local-mode SPAPS server, and reuses the persona selectors already defined in the fixture kernel instead of adding fixture-only backend routes. What `apply` emits: - `browser/<persona>.storage-state.json` for Playwright `storageState` - `browser/<persona>.headers.json` for `extraHTTPHeaders` - `browser/<persona>.context.json` with merged persona/runtime metadata - `public/spaps-dev-auth.js` for frontend-only auth/RBAC dev mode This does not try to import browser password-manager state. It writes deterministic, repo-local test artifacts instead. If you want a human to launch the frontend and click around without a mock backend, include the generated bridge before your app boots: ```html <script src="/spaps-dev-auth.js"></script> ``` That bridge does three things: - seeds SDK-compatible localStorage keys like `sweet_potato_user` and `sweet_potato_access_token` - intercepts `/api/auth/user`, `/api/auth/login`, `/api/auth/logout`, `/api/entitlements`, and `/api/entitlements/check` - renders a tiny persona switcher so you can flip between `user`, `admin`, and `premium` Personas can also carry app-owned demo state in `.spaps/users.json`: ```json { "code": "pds-initiator", "display_name": "PDS Initiator", "profile": { "user_id": "00000000-0000-0000-0000-000000000001" }, "scenario": { "pds": { "dashboard": "work", "role": "initiator" } } } ``` `scenario` is opaque to SPAPS. It is copied into `browser/<persona>.context.json`, persisted as `spaps.fixture.scenario`, and exposed by the bridge: ```js const pdsScenario = window.__SPAPS_DEV_AUTH__.getScenario("pds"); const unsubscribe = window.__SPAPS_DEV_AUTH__.onPersonaChange((event) => { console.log(event.persona.code, event.scenario.pds); }); window.addEventListener("spaps:persona-change", (event) => { console.log(event.detail.persona.code); }); ``` Use this for persona-specific dashboard stories, queues, and UI fixture hints. Keep product-specific data in the consuming app; SPAPS only owns the persona/auth/role/entitlement envelope. ## Middleware Example The main module exports admin and permission helpers for Express-style apps. ```js const express = require("express"); const { requireAdmin, requirePermission } = require("spaps"); const app = express(); const customAdmins = ["admin@example.com"]; app.get( "/admin", requireAdmin({ customAdmins }), (_req, res) => { res.json({ ok: true }); }, ); app.post( "/admin/products", requirePermission("manage_products", { customAdmins }), (_req, res) => { res.json({ created: true }); }, ); ``` ## What `spaps init` Writes `spaps init` creates a `.env.local` file with a local API URL starter and leaves an existing file alone if one is already present. ## Troubleshooting ### Port already in use Start on another port: ```bash npx spaps local --port 3400 ``` ### I need deterministic portable runtime files Pin the bundled runtime directory explicitly: ```bash npx spaps local --runtime-source bundle --runtime-dir ./.skillbox/spaps-local ``` This is the recommended path for managed environments such as skillbox. ### I need machine-readable output Use `--json` on commands that support it, including `local`, `status`, `quickstart`, `init`, `fixtures`, `docs`, `tools`, and `doctor`. ### The local server is not responding Check current status first: ```bash npx spaps status ``` If needed, restart with a clean boot: ```bash npx spaps local --fresh ``` If you want the old "db=fresh from prod" behavior through the portable CLI: ```bash npx spaps local --fresh --data-source prod-fresh ``` If you want to keep a cached prod-backed base DB around for day-to-day work: ```bash npx spaps local --data-source prod-cache ``` ### `spaps create` only scaffolded files That means one of two things: - the local server was unreachable - the server is not in local mode and `SELF_SERVICE_PASSWORD` was missing or invalid Check the current mode first: ```bash npx spaps quickstart --json ``` If the server requires provisioning, rerun with: ```bash SELF_SERVICE_PASSWORD=your-password npx spaps create my-app --template react --force ``` ## Limitations - The CLI is aimed at local workflows. It is not a full deployment or hosting tool. - `create` does not run `create-next-app`, Vite, or Express generators for you; it scaffolds the SPAPS integration layer. - `types` is still a placeholder today. - The middleware ships a legacy default admin configuration in code; public-facing apps should pass explicit `customAdmins` instead of relying on that default. ## FAQ ### What is this package actually for? Use it when you want to prove auth wiring in a new app before you build backend auth infrastructure yourself. ### Does this package include the TypeScript SDK? No. The SDK is published separately as `spaps-sdk`. ### Can I use the CLI without a global install? Yes. Use `npx spaps ...`. ### Does `spaps init` overwrite an existing env file? No. It skips `.env.local` if the file already exists. ### What does `spaps tools` output? An OpenAI-style tool spec for the local SPAPS surface. The auth section is derived from `/health/local-mode` when the local server is reachable. ### Is the middleware available from a subpath? Yes. You can import from the main module or `spaps/middleware`. ## License MIT