CLI reference
The calmapi package installs a calm command (also available as calmapi). It is a dev dependency of every project, so use it with npx calm <command> or through the npm scripts. It has no dependencies of its own.
calm create [dir] new project (npm create calmapi@latest)
calm dev watch mode + devtools
calm make resource <name> [fields...] scaffold a resource
calm make block <name> start your own block
calm add <block...> copy blocks into your project (calm add --list, gh:user/repo/name)
calm block list|check|pack your blocks: what changed, a safety review, a file to share
calm seed [resource...] sample records that look real
calm routes every route with its policy and source file
calm openapi write openapi.json
calm doctor [--prod] [--audit] [--ci] security and correctness checks
calm audit [--ci] test every route as every role on a throwaway database
calm admin <email> [--revoke] grant (or remove) the admin role
calm keygen [--write] encryption key for .encrypted() fields (rotates)
calm upgrade merge framework updates into calm/
calm mcp MCP server for coding agentscalm <command> --help prints the options of a command, calm --version the version. Aliases: new and init for create; generate, g and m for make; blocks for block. Running calmapi with no arguments outside a project starts the create wizard. Commands other than create must run inside a project (a folder with calm.lock, or below one). Set CALM_DEBUG=1 to see stack traces.
calm create#
calm create [directory] also: npm create calmapi@latest [directory]| Option | Meaning |
|---|---|
--template <api|minimal> | api: users, auth and an example resource (default). minimal: an empty app/ to start from scratch |
--db <uri> | MongoDB connection string (default: local server) |
--pm <npm|pnpm|yarn|bun> | Package manager to install with |
--no-install | Skip installing dependencies |
--no-git | Skip git init |
-y, --yes | Accept every default, no prompts |
With npm create, put the options after --: npm create calmapi@latest my-api -- --template minimal --yes. The target folder must be empty or not exist.
calm dev#
calm devStarts the app in development mode: NODE_ENV=development, file watching (node --watch on app/, calm/, calm.config.js, server.js and .env, so new resource folders restart the server too) and devtools at http://localhost:<port>/_calm. Extra arguments are passed to node, for example calm dev --inspect.
It also records every file change (with a line diff, marked as made by an AI agent or by you), restart and crash in .calm/activity.jsonl. The contents of .env are never recorded. Devtools shows this log live in the Activity panel; see Watching an agent work.
calm make resource#
calm make resource <name> [fields...]Creates app/resources/<name>/<name>.resource.js and, when the project has users, test/<names>.test.js.
| Option | Meaning |
|---|---|
--service | Also create <name>.service.js |
--routes | Also create <name>.routes.js (with an example GET /stats) |
--no-owner | Do not add an owner field (by default owner: f.ref('user').owner() is added when the project has users) |
--no-test | Do not create the test file |
--force | Overwrite existing files |
Field syntax: name:type[!][=default][+modifier...]
| Spec | Generated |
|---|---|
title:string! | f.string().required() |
price:number=0 | f.number().default(0).sortable().filterable() |
status:enum:draft,live=draft | f.enum(['draft', 'live']).default('draft').filterable() |
category:ref:category | f.ref('category').filterable() |
author:ref | f.ref('author').filterable() (the field name is the resource) |
tags:string[] | f.array(f.string()).default([]) |
name:string!+searchable+sortable | f.string().required().searchable().sortable() |
publishedAt:date=now | f.date().default(() => new Date()).sortable().filterable() |
Types: string text email url password number integer boolean date enum ref id json (also str, num, float, int, bool, datetime, object). Modifiers: searchable sortable filterable unique index hidden writeOnly readOnly immutable populate owner. Enums, booleans and refs are made filterable, numbers and dates filterable and sortable.
A field spec owner:ref:user becomes the owner field (f.ref('user').filterable().owner()), and no second owner field is added.
npx calm make resource invoice number:string!+unique amount:number! status:enum:draft,sent,paid=draft customer:ref:user notes:text --serviceWithout a user resource (the minimal template), the generated policies are public with a comment reminding you to add auth before deploying anything writable.
calm make block#
calm make block <name>Starts your own block: app/blocks/<name>/index.js (a working route, with every extension point listed in comments), block.json (what sharing it needs), README.md, and test/blocks/<name>.test.js. The block is live in your app right away. The name is turned into kebab case and must start with a letter.
| Option | Meaning |
|---|---|
--no-test | Do not create the test file |
--force | Overwrite existing files |
calm add#
calm add <block...>Copies a block's files into your project, adds its dependencies to package.json, appends its environment variables to .env.example and .env, installs, and records it in calm.lock. The code is yours from then on. See Blocks.
npx calm add jobs api-docs # official blocks (same as @calmapi/jobs)
npx calm add @acme/billing # a registry from package.json "calm.registries"
npx calm add gh:acme/blocks/billing # r/billing.json in a GitHub repository (@ref to pin)
npx calm add https://x.dev/r/y.json # any URL
npx calm add ./blocks/my-block.json # a local file
npx calm add --list # list official blocks| Option | Meaning |
|---|---|
--dry-run | Show what would change, write nothing |
--overwrite | Replace files that already exist with different content |
--no-install | Do not run the package manager |
--remote | Fetch official blocks from https://docs.calmapi.dev/r instead of the copy bundled with the CLI |
-y, --yes | Do not ask for confirmation |
--trust | With --yes: also add third-party blocks whose review found risky code |
--list | List official blocks |
Existing files with different content are kept and reported as conflicts unless you pass --overwrite. Blocks that provide the same resource (storage-local and storage-s3) refuse to be installed together. A block whose minVersion is newer than the CalmAPI version in calm.lock is refused until you run calm upgrade.
Blocks that are not official are reviewed before anything is written: the plan lists what their code does, in plain language (network requests, file writes, child processes, eval, computed imports, native or minified code, request hooks, the environment variables it reads and the hosts it mentions). Risky findings are marked ! and must be confirmed; with --yes, they need --trust too. --dry-run shows the review without writing anything. See What people see before installing.
calm block#
calm block list
calm block check [name|address]
calm block pack [name...] [--out r]Tools for the blocks in your project and the ones you share. See Building your own block.
| Command | Does |
|---|---|
list (or ls) | Every block in the project: version, where it came from (from calm.lock, or "written here"), and the files you changed since adding it |
check | Checks your blocks before you share them, or someone else's before you add it: name, versions, file targets (never calm/, .env or package.json), dependency ranges, env var declarations, committed secrets, imports of calm/ internals, and the plain-language review of what the code does. Without a name it checks every block with a block.json. With an address (gh:..., a URL, @scope/name or a .json file) it fetches the block first. Exits with code 1 on errors. |
pack | Writes a registry item for each block to r/<name>.json and an r/index.json. Without names, every block with a block.json. Blocks with errors are not packed. --out <dir> writes elsewhere. |
$ npx calm block pack
✔ Packed slack-notify into r/
Share it
Commit r/ and push to GitHub, then anyone can run:
npx calm add gh:<you>/<repo>/slack-notify
Or host r/ anywhere: npx calm add https://<host>/r/<name>.jsoncalm seed#
calm seed [resource...] [--count 10] [--users 3] [--seed 42]Fills the database (MONGODB_URI from .env) with sample records that look real, built from your field definitions: names, emails, prices, ratings, phone numbers, cities, values from enums, dates in the past or the future depending on the field name, unique values kept unique, references to records that exist, and owners. Records are validated like API input and created through your services, so hooks run. Resources that the seeded ones reference get a few records first (up to 5), when they are empty.
npx calm seed # every resource, 10 records each
npx calm seed product --count 50 # one resource
npx calm seed --seed 42 # the same choices on every run| Option | Meaning |
|---|---|
--count <n> | Records per resource (default 10, at most 10,000) |
--users <n> | Demo users to create when there are none yet (default 3) |
--seed <n> | A fixed random seed, so names, numbers and choices repeat. Emails and unique values still differ between runs, so they never collide. |
When the database has no users, demo users are created to own the records, and their emails and password are printed so you can sign in as them:
✔ Seeded 3 user, 10 product
Demo users (sign in with these)
demo.grace.mgnx8k2a0@example.test calm-demo-password-1
demo.alan.mgnx8k2b1@example.test calm-demo-password-1
demo.radia.mgnx8k2b2@example.test calm-demo-password-1It refuses to run with NODE_ENV=production. In devtools, Sample data in the Data panel does the same for one resource.
calm routes#
calm routes [--json]Every route the app serves with its method, path, policy and the file it comes from. Defaulted policies are marked (default). --json prints machine readable output.
calm openapi#
calm openapi [--out openapi.json] [--stdout]Writes the OpenAPI 3.1 document generated from your resources and routes (default openapi.json), or prints it with --stdout. The running app also serves it at /api/openapi.json.
calm doctor#
calm doctor [--prod] [--audit] [--ci] [--strict] [--json]Security and correctness checks: config, policies, private fields, personal data exposure, hardcoded secrets, .env in git, risky code patterns, framework drift and published advisories. Also works inside CalmAPI 1.x projects (npx calmapi@next doctor), where it reports the advisories published for 1.x. See Security for every check.
| Option | Meaning |
|---|---|
--prod | Evaluate with production rules (NODE_ENV=production) |
--audit | Also run npm audit on production dependencies |
--ci | Exit with code 1 when there are errors |
--strict | With --ci: also fail on warnings |
--json | Machine readable output: { findings, errors, warnings } |
CalmAPI doctor · my-api · production rules
! warn No mail transport in production
Password reset emails cannot be delivered. Run `calm add @calmapi/mail-resend` or register a transport.
• info TRUST_PROXY is not set
Behind a load balancer every client shares one IP for rate limiting. Set TRUST_PROXY (e.g. 1).
0 errors · 1 warning · 1 infoDoctor boots your app in a child process to inspect it, without connecting to MongoDB.
calm audit#
calm audit [--json] [--ci]Authorization testing for your API. Boots the app on a throwaway database (MONGODB_TEST_URI, default mongodb://127.0.0.1:27017), creates a user per persona (anonymous, owner, another user, admin, custom roles), seeds records and calls every CRUD route as every persona. It checks status codes against your policies, owner-only lists, field leaks, mass assignment, query injection, the page size cap, error hygiene and token handling. Your real database is never touched. See Security audit.
| Option | Meaning |
|---|---|
--ci | Exit with code 1 when a check fails |
--json | Machine readable report |
note /notes
action Anonymous Signed-in user Owner Admin
list ✔ deny ✖ own (200) ✖ own (200) ✖ own (200)
read ✔ deny ✔ deny ✔ allow ✔ deny
create ✔ deny ✔ allow ✔ allow ✔ allow
update ✔ deny ✔ deny ✔ allow ✔ deny
delete ✔ deny ✔ deny ✔ allow ✔ deny
✖ list as Signed-in user: returned 2 record(s) owned by someone else
✖ list as Owner: returned 1 record(s) owned by someone else
✖ list as Admin: returned 5 record(s) owned by someone elseThat is one resource of a failing run: its service ignores the owner scope on list.
calm admin#
calm admin <email> [--revoke]Gives an existing user the admin role, or removes it with --revoke. It connects to MONGODB_URI from .env, and the change applies to the user's next request. To make your first admin, sign up through POST /api/auth/register, then:
npx calm admin ada@example.com✔ Ada Lovelace <ada@example.com> roles: user, admincalm keygen#
calm keygen [--write]Generates a 32-byte key for .encrypted() fields and prints it as a CALM_ENCRYPTION_KEYS=k1:<key> line. With --write, it adds the line to .env. If CALM_ENCRYPTION_KEYS already exists, the new key goes first (it encrypts from now on) and the old keys stay (they still decrypt existing data): that is key rotation.
$ npx calm keygen --write
✔ Added CALM_ENCRYPTION_KEYS to .env (key k1).
$ npx calm keygen --write
✔ Rotated: k2 encrypts new data; k1 still decrypts existing data.Put the same keys in your production secrets. Never commit them.
calm upgrade#
calm upgrade [--dry-run] [--base <dir>] [-y]Brings framework fixes into calm/ without losing your edits. See Upgrading.
| Option | Meaning |
|---|---|
--dry-run | Show the plan, write nothing |
--base <dir> | Use this calmapi checkout as the original files instead of downloading the version in calm.lock |
-y, --yes | Do not ask for confirmation |
calm mcp#
calm mcpStarts a Model Context Protocol server on stdio for AI coding agents. Register it once:
claude mcp add calm -- npx calm mcpor add { "command": "npx", "args": ["calm", "mcp"] } to your agent's MCP configuration. It offers 18 tools: inspect the project, scaffold, call the dev API as any user (calm_http), run tests, doctor and the audit, and read requests, logs, emails, security events and project activity. Every tool call is logged to .calm/activity.jsonl with the client's name, so you can follow it in devtools. See AI agents for the list.