@deploybaseline/cli
v0.14.0
Published
Local drop-in replacement services for CI and no-network sandboxes
Maintainers
Readme
Baseline
Stateful emulators of the third-party APIs your app depends on. Not mocks.
Baseline runs local copies of Stripe, GitHub, Slack, Google, HubSpot, and 23 other APIs. Each emulator keeps real state, runs the vendor's OAuth flows, signs its webhooks, and is tested against the vendor's official SDK. You point your app at Baseline instead of the real service and keep the rest of your code unchanged.
Documentation: docs.deploybaseline.com
- Why Baseline
- Quick start
- Providers
- Configuration
- Saved state and snapshots
- Use it in tests
- Custom emulators
- CLI reference
- Repository layout
- Development
- Project documents
Why Baseline
Tests and sandboxes that talk to real vendor APIs need accounts, network access, and rate limit headroom, and they leave data behind. Hand-written mocks avoid that but drift from the real API and cannot follow a flow across requests. Baseline sits between the two:
- Real state. A payment you create can be fetched, refunded, and listed. Every emulator stores its data, so later requests see earlier ones.
- Official SDKs work unchanged. Every provider is tested with its vendor's SDK. Where the vendor publishes an API spec, responses are also checked against it.
- Full flows. OAuth and OIDC sign-in pages, signed webhooks, hosted checkout and approval pages, and WebSocket gateways run locally.
- Repeatable. Seed data from a config file, reset between tests, save and restore named snapshots, and inject latency or errors to test retries.
How closely each provider follows its real API is tracked in CONFORMANCE.md.
Quick start
Requires Node 24 or later.
npx @deploybaseline/cliWith no config, the eleven default providers start on fixed ports, from GitHub on 4001 to Twilio on 4012 (see the table below). Every listener binds to 127.0.0.1. Start prints each service URL, the API reference at http://localhost:3999/docs, a control token, and an inspector link for state, snapshots, and faults.
Point your SDK at the emulator instead of the vendor:
import { Octokit } from '@octokit/rest'
const octokit = new Octokit({ baseUrl: 'http://localhost:4001', auth: 'test_token_admin' })
const { data } = await octokit.rest.repos.createForAuthenticatedUser({ name: 'hello-world' })Start other providers by name. Named services take consecutive ports from the base port:
npx @deploybaseline/cli start --service stripe,hubspotEach provider's docs page shows the exact client setup for its SDK.
Providers
Grouped as on the docs site. The eleven with a default port start with a bare npx @deploybaseline/cli and keep that port in every release; start the others by name (--service hubspot). Each provider's page covers setup, behavior, seed config, and limits, and a running CLI serves its full API reference at http://localhost:3999/docs.
Identity and auth
| Provider | Service | Default port | What is emulated |
|---|---|---|---|
| Auth0 | auth0 | named only | Universal Login, OAuth and OIDC, Management API, role-based access control |
| Clerk | clerk | 4010 | Frontend API, Backend API, OAuth and OIDC, organizations, sessions, JWT templates |
| Okta | okta | 4006 | OAuth 2.0 and OIDC, authorization servers, users, groups, apps |
| Apple | apple | 4004 | Sign in with Apple: authorization, client secrets, ID tokens, server-to-server notifications |
Workspace and productivity
| Provider | Service | Default port | What is emulated |
|---|---|---|---|
| Google | google | 4002 | OAuth 2.0 and OIDC, Gmail, Calendar, Drive, Sheets, Docs |
| Microsoft | microsoft | 4005 | Entra ID OAuth 2.0 and OIDC, Microsoft Graph: Outlook mail, calendar, contacts, To Do, users and groups; SharePoint Online and OneDrive over Graph and the SharePoint REST API |
| Notion | notion | named only | Pages, databases and data sources, queries, search, blocks, comments, OAuth, webhooks |
Messaging and email
| Provider | Service | Default port | What is emulated |
|---|---|---|---|
| Slack | slack | 4003 | Web API, OAuth v2, incoming webhooks, interactivity, events, Socket Mode |
| Discord | discord | named only | Bot REST API, Gateway over WebSocket, interactions, OAuth2, rate limits |
| Twilio | twilio | 4012 | Programmable Messaging, Messaging Services, Verify, Voice, Conversations |
| WhatsApp | whatsapp | named only | Cloud API messages, templates, media, phone numbers and accounts, webhooks |
| Resend | resend | 4008 | Emails, idempotency, domains, API keys, audiences and contacts, a local inbox |
| SendGrid | sendgrid | named only | Mail Send, scheduling, templates, API keys, Marketing, Event Webhook, a local inbox |
Payments and commerce
| Provider | Service | Default port | What is emulated |
|---|---|---|---|
| Stripe | stripe | 4009 | Customers, Payment and Setup Intents, charges, refunds, subscriptions, invoices, Checkout, Connect, webhooks |
| PayPal | paypal | named only | Orders v2, a hosted approval page, Payments v2, webhooks |
| Square | square | named only | Payments, refunds, orders, customers, catalog, locations, OAuth, webhooks |
| Plaid | plaid | named only | Link, items and accounts, transactions, institutions, sandbox helpers, webhooks |
| Shopify | shopify | named only | Admin GraphQL API, OAuth, webhooks |
Developer platforms
| Provider | Service | Default port | What is emulated |
|---|---|---|---|
| GitHub | github | 4001 | REST and GraphQL: repositories, issues, pull requests, organizations, webhooks, Actions, Checks, OAuth Apps and GitHub Apps |
| Linear | linear | 4011 | GraphQL API, OAuth, webhooks |
| Jira | jira | named only | REST API, JQL search, Agile boards and sprints, OAuth 2.0 (3LO), webhooks |
CRM and support
| Provider | Service | Default port | What is emulated |
|---|---|---|---|
| Salesforce | salesforce | named only | Records, SOQL, describe, sObject collections, composite requests, OAuth |
| HubSpot | hubspot | named only | CRM objects, search, associations, properties, pipelines, owners, OAuth, webhooks |
| Zendesk | zendesk | named only | Tickets, comments, users, organizations and groups, search, views, triggers, OAuth, webhooks |
| ServiceNow | servicenow | named only | Table API, Attachment API, Aggregate API |
Finance and ERP
| Provider | Service | Default port | What is emulated |
|---|---|---|---|
| NetSuite | netsuite | named only | Records, transforms, SuiteQL, custom fields and records, metadata, OAuth 2.0 |
| QuickBooks | quickbooks | named only | Accounting entities, queries, batch, change data capture, OAuth 2.0, webhooks |
| Xero | xero | named only | Accounting API, OAuth 2.0 and OpenID Connect, webhooks |
Discord's Gateway and Slack's Socket Mode work out of the box: the WebSocket kit ships inside those providers. See WebSockets.
Firebase, Azure Blob Storage, and Google Cloud Pub/Sub ship official local emulators, so Baseline does not reimplement them. Official emulators shows how to run them next to Baseline.
Every running Baseline serves an API reference for the services it started at http://localhost:3999/docs: every route each emulator serves, generated from the code, with a playground that sends requests to your local emulators. /docs/<service>/openapi.json is the OpenAPI document. See API reference.
Configuration
Configuration is optional. The CLI looks for baseline.config.json (or .ts, .mts, .js, .mjs, .yaml, .yml) in the current folder. --config <file> and --seed <file> point at another file, and npx @deploybaseline/cli init writes a starter config with a $schema line, so editors complete keys and flag mistakes as you type. npx @deploybaseline/cli validate checks a config and shows each problem with its line, the setting, and the fix (for example github.users[0].nmae: unknown property "nmae". Did you mean "name"?); --json prints the same for scripts and agents. npx @deploybaseline/cli convert baseline.config.yaml moves a YAML config to JSON.
{
"tokens": {
"my_token": {
"login": "octocat",
"scopes": ["repo", "user"]
}
},
"identities": [
{
"name": "Ada Lovelace",
"email": "[email protected]"
}
],
"github": {
"users": [
{
"login": "octocat",
"name": "The Octocat"
}
],
"repos": [
{
"owner": "octocat",
"name": "hello-world",
"auto_init": true
}
]
},
"stripe": {
"port": 4009,
"version": "^0.14",
"faults": {
"profile": "degraded"
}
},
"state": ".baseline/state.json"
}identities: one person, seeded into every provider that has usersport: keep this port whatever else runsversion: provider version range; start pins the exact version in baseline.lockstate: keep data, tokens, and webhooks across restarts
The CLI ships no providers: the first start downloads the ones it runs into a cache, and later starts work offline. Each built-in service runs a provider package from the project's node_modules, the provider cache, or a pull from npm, and start pins the version it ran in baseline.lock (commit it). Provider versions covers the order, pull for CI and Docker builds, and --offline.
A service can also run a provider package that is not built in: provider: npm:@acme/baseline-billing@^1.2.0 with a port:. It runs with your permissions, so start asks before its first run and pins it in baseline.lock; in CI, run npx @deploybaseline/cli trust <service> once and commit the lockfile. Provider packages covers running and writing them.
Each provider's seed keys are on its docs page. Configuration covers the shared settings: tokens, identities, ports, one process per service, advertised URLs and local HTTPS, fault injection, the control API, and OIDC signing keys. baseline.config.example.json has an entry for every provider.
Saved state and snapshots
By default, state lives in memory and resets when the process stops. --state keeps it across restarts in a JSON file, a SQLite database, or Turso (libSQL):
npx @deploybaseline/cli start --state .baseline/state.json
npx @deploybaseline/cli start --state sqlite:.baseline/state.dbNamed snapshots capture every running service at once, so a test can return to a known point:
npx @deploybaseline/cli snapshot save before-checkout
npx @deploybaseline/cli snapshot restore before-checkout
npx @deploybaseline/cli snapshot export --redact -o share.jsonState files hold access tokens and private signing keys. Keep .baseline/ out of version control. See Saved state for file formats, locking, and security.
Use it in tests
createEmulator starts one service in process, with the same seeds, state, and faults as the CLI:
import { createEmulator, type Emulator } from '@deploybaseline/cli'
let github: Emulator
beforeAll(async () => {
github = await createEmulator({ service: 'github', port: 4001 })
process.env.GITHUB_EMULATOR_URL = github.url
})
afterEach(() => github.reset()) // returns a Promise, which the test runner awaits
afterAll(() => github.close())Instances also expose snapshot(), restore(), and named snapshots. See Programmatic API.
Custom emulators
Emulate any other HTTP API in TypeScript with defineEmulator. A custom emulator runs next to the built-in ones and gets the same seeds, resets, snapshots, persistence, and inspector:
npx @deploybaseline/cli init --custom inventory # scaffold an emulator, config entry, and test
npx @deploybaseline/cli start --watch # reload on every changeSee Custom emulators, the GraphQL kit, and WebSockets.
CLI reference
| Command | What it does |
|---|---|
| start | Start the emulators. The default command, so npx @deploybaseline/cli alone runs it |
| init | Write a starter config, or scaffold a custom emulator with --custom <name> |
| validate [file] | Check a config: each problem with its line, the setting, and the fix (--json for scripts) |
| convert [file] | Write a YAML config as baseline.config.json and check it |
| list | List every provider with its endpoints |
| pull | Put every configured provider in the cache and pin it in baseline.lock, for CI and Docker builds |
| update [services...] | Move providers to the newest version in their range and rewrite baseline.lock |
| providers, prune | List cached providers, or remove the ones no known baseline.lock uses |
| trust [services...] | Trust provider: services that are not built in, and pin them in baseline.lock |
| snapshot save, restore, list, delete | Manage named snapshots on a running server |
| snapshot export, import | Write the current state to a file, or replace it from one |
Common start flags:
| Flag | Default | What it does |
|---|---|---|
| --service <names> | default set | Comma-separated providers to start |
| --port <port> | 4000 (or BASELINE_PORT) | Base port |
| --host <host> | 127.0.0.1 | Listening address. Use 0.0.0.0 for containers |
| --single-port | off | Not supported: each service listens on its own port |
| --console-port | base port - 1 | Port for the API reference at /docs and the control API |
| --no-console | off | Do not open the console port |
| --base-url <url> | none | Advertised URL, with a {service} placeholder, for proxies and local HTTPS |
| --config <file>, --seed <file> | discovered | Config file to load |
| --state <target> | in memory | Keep state in a JSON file, sqlite:<path>, or libsql://<host> |
| --reset-state | off | Ignore saved state, reseed, and overwrite it |
| --watch | off | Reload on config, import, and fixture changes |
| --offline | off (or BASELINE_OFFLINE=1) | Never touch the network: run providers from the project, the cache, or the CLI |
Run npx @deploybaseline/cli --help for every flag. The docs cover authentication, local HTTPS, and the control API.
Repository layout
apps/
home/ # the deploybaseline.com landing page (Astro)
packages/
cli/ # the baseline CLI and createEmulator
@deploybaseline/
core/ # HTTP server, store, plugin interface, middleware, shared UI
testing/ # provider test harness: startService, restartService, webhook sink
graphql/ # GraphQL endpoint kit (Linear, Shopify, GitHub)
ws/ # WebSocket kit (Slack Socket Mode, Discord Gateway)
state-sqlite/ # --state sqlite:<path>
state-libsql/ # --state libsql://<host>
<provider>/ # one package per emulated vendor
docs/ # Mintlify site, published at docs.deploybaseline.com
skills/ # agent skills, one per provider plus baseline and custom-apis
tests/contracts/ # behavior contracts every runtime runs
conformance/ # dates of the last passing dual mode run per provider
scripts/ # scaffolding, generators, conformance, release helpersArchitecture covers core, the plugin system, state, and middleware.
Development
git clone https://github.com/deploybaseline/baseline-cli && cd baseline-cli
make setup # check Node 24 and pnpm 11, install, build
make dev SERVICE=stripe # run from source, restarting on every change
make test-provider P=stripe # test one provider
make test-changed # test what your branch touches
make check # everything the CI build job runsRun make to list every task. To add a provider, run make new-provider NAME=<name>, which scaffolds the package, docs page, skill, and the row in the table above. The contributor guides cover local development, adding a provider, and testing. See also CONTRIBUTING.md and AGENTS.md.
Project documents
| Document | Contents | |---|---| | CONFORMANCE.md | How closely each provider follows the real API, and how that is verified | | PLATFORMS.md | Every emulated platform and what it covers | | ROADMAP.md | Platforms not built yet, and the order to build them in | | PROGRESS.md | Build status of the current plan | | CHANGELOG.md | Release notes | | SECURITY.md | Reporting vulnerabilities |
Acknowledgments
Baseline is a derivative of the Apache-2.0 project emulate; see NOTICE. Coming from emulate lists every renamed package, route, and environment variable.
The internal HTTP layer builds on Hono's API and design. Thank you to Yusuke Wada and the Hono contributors. Hono's copyright and MIT license notice are in THIRD_PARTY_NOTICES.md.
