npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

spaps

v0.9.1

Published

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

Downloads

728

Readme

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

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:

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:

npm install -g spaps

Add it to a project:

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 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 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:

{
  "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:

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:

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 [email protected] --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

Authenticated follow-up calls such as whoami, logout, and token refresh still need a normal SPAPS app key when the server is not in local mode. The CLI resolves that key from:

  • SPAPS_API_KEY
  • NEXT_PUBLIC_SPAPS_API_KEY
  • VITE_SPAPS_API_KEY
  • repo-local .env.local
  • repo-local .env

Examples:

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_API_KEY=spaps_pub_demo npx spaps whoami --json

Auth diagnostics:

npx spaps doctor --server-url http://localhost:3301 --origin http://localhost:3000 --json
npx spaps auth methods --origin http://localhost:3000 --json
[email protected] 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.

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:

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:

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:

<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:

{
  "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:

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.

const express = require("express");
const { requireAdmin, requirePermission } = require("spaps");

const app = express();
const customAdmins = ["[email protected]"];

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:

npx spaps local --port 3400

I need deterministic portable runtime files

Pin the bundled runtime directory explicitly:

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:

npx spaps status

If needed, restart with a clean boot:

npx spaps local --fresh

If you want the old "db=fresh from prod" behavior through the portable CLI:

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:

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:

npx spaps quickstart --json

If the server requires provisioning, rerun with:

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