@contember/cli is the command line tool for developing and operating Contember projects. It manages migrations, validates and prints the schema, transfers data, deploys, and provides tenant-administration commands.

Run it without arguments to see the list of commands:

npm run contember

Every command is also scriptable: stdout carries data, stderr carries diagnostics, --json makes the data machine readable and the exit code tells a caller what went wrong.

Command names

The canonical form is space separated, noun first, verb second:

contember migrations diff add-article
contember tenant person list

The older colon form keeps working everywhere and prints no deprecation warning, so existing scripts and CI pipelines do not need to change:

contember migrations:diff add-article
contember tenant:person:list
Tip

You can abbreviate each part of a command name as long as the result stays unambiguous, for example contember migr:exe runs migrations execute. An ambiguous abbreviation fails and lists the candidates.

Discovering commands

A command group on its own prints the commands it contains, on stderr:

contember tenant
contember tenant person

--help (or -h) works on any command and prints its arguments, its options and the global options:

contember migrations diff --help

For tooling, commands prints the whole catalog — every command with its group, description, aliases, arguments and options:

contember commands --json

Output

The streams have fixed roles:

  • stdout is data. The result of the command, nothing else.
  • stderr is diagnostics. Progress, warnings, confirmations and help never mix with result data.

In --json and --quiet modes, the stable automation contract has three outcome classes:

  • A successful command exits with 0. It writes result data to stdout when it has a payload; otherwise stdout may be empty.
  • A result-bearing domain check or a batch with partial failures may exit nonzero while still writing its result to stdout. It does not write a fatal error envelope.
  • An invocation, transport or internal failure that aborts the command writes the error to stderr and leaves stdout empty. In JSON mode, stderr contains the fatal error envelope described below.

Use the exit code to distinguish a nonzero result from success. In human mode, a command may write contextual data to stdout before a later fatal summary on stderr.

--json

--json is a global option, available on every command. It turns the data on stdout into JSON and switches errors to the envelope described below. Informational messages, warnings, progress and child process output are suppressed. When a command emits result data, it writes one JSON document: the bare value, not a wrapper, so it pipes straight into jq:

contember commands --json | jq -r '.[] | select(.group == "tenant") | .name'
contember tenant person list --json | jq -r '.[].email'

--quiet

--quiet (or -q) drops informational diagnostics and child process output, and reduces stdout to bare scalar values, one per line — useful when you only need an id or a name. A fatal error is still reported on stderr:

contember tenant project list --quiet

--no-color

--no-color disables ANSI colors. Colors are disabled automatically in --json and --quiet mode.

Put --json, --quiet and --no-color after the command name. For example, use contember tenant person list --json, not contember --json tenant person list. Leading --help or -h is recognized separately as application help; put it after a command to see that command's help.

Interactivity

Destructive commands ask for confirmation. Pass --yes to skip the prompt. Without a TTY, and in --json or --quiet mode, a prompt would hang forever, so the command fails immediately with the TTY_UNAVAILABLE error instead. --json does not imply --yes.

Secrets and long inputs are read from stdin or from an environment variable only behind an explicit flag — for example --password-stdin, --password-env, --config-stdin, --content-stdin, --memberships-stdin and --policy-stdin. A normal run never reads stdin implicitly. Literal secret options are documented per command. Prefer stdin or an environment variable wherever the command offers them, because an option value lands in the shell history and in ps.

Exit codes

CodeMeaning
0Success.
1Input error — invalid input, failed validation, unknown command.
2Internal error — unexpected, a bug.
3Not found.
4Transient failure — network, timeout, 5xx. The caller should retry.
5Conflict — the thing already exists.
6Forbidden — authentication or permission denied.

An error that carries no more specific classification exits with 1; an unexpected failure — one the caller cannot fix by changing the input — exits with 2.

Errors

A fatal failure that aborts a command is written to stderr. In --json and --quiet modes, stdout stays empty. In human mode, the command may already have written contextual data to stdout before the one-line fatal summary:

Error [PROJECT_NOT_DEFINED]: Project not defined. Set CONTEMBER_DSN, or CONTEMBER_API_URL, CONTEMBER_API_TOKEN and CONTEMBER_PROJECT_NAME.

With --json the same error becomes an envelope on stderr, while stdout stays empty:

{
  "ok": false,
  "error": {
    "code": "COMMAND_NOT_FOUND",
    "message": "Command \"nope\" not found.",
    "retryable": false,
    "details": null
  }
}

code is a stable machine readable slug — for tenant failures it is the tenant API error code verbatim, for example PERSON_NOT_FOUND or ALREADY_MEMBER. retryable tells a caller whether repeating the same call can succeed; it is true for the transient failures that exit with 4.

A nonzero exit does not by itself mean that stderr contains an error. Domain checks and partial batch results use stdout as described in Output.

Connecting to a project

The CLI reads the connection from environment variables:

VariableMeaning
CONTEMBER_DSNFull DSN, contember://project:token@endpoint. Takes precedence over the variables below.
CONTEMBER_API_URLAPI endpoint. CONTEMBER_INSTANCE is accepted as an alias.
CONTEMBER_API_TOKENAPI token.
CONTEMBER_PROJECT_NAMEProject name.
CONTEMBER_DIRWorkspace directory. Defaults to the current directory.

Some commands can override this per run:

  • deploy [dsn] takes a DSN as its first argument.
  • tenant apply takes --dsn.
  • data export, data import and data transfer take a project name or a full DSN as their source/target argument.
  • actions * take --project, which is a project name or a full DSN.
Note

The tenant API commands other than tenant apply have no --dsn option. They can connect with only CONTEMBER_API_URL and CONTEMBER_API_TOKEN; CONTEMBER_PROJECT_NAME is not required. Their --project option is the target project slug inside the tenant API, not the connection.

Command reference

The tables below list every command with its required arguments and required options. Run contember <command> --help for the optional ones, or contember commands --json for the whole catalog in one machine readable document.

Top level

CommandDescription
commandsLists all commands with their arguments and options.
deploy [dsn]Deploy Contember project.
versionPrints Contember CLI version.

migrations

See the migrations chapter.

CommandDescription
migrations amend [migration]Amends latest migration.
migrations blank <migrationName> [format]Creates new blank migration file.
migrations describe [migration]Describes a migration.
migrations diff <migrationName>Creates schema migration diff.
migrations executeExecutes migrations on Contember server.
migrations init-stateEnables schema state mode by extracting ACL, validation, actions and settings into the state/ directory.
migrations rebase <migration...>Rebase migrations on filesystem and in local instance.
migrations snapshotCreates a schema snapshot (snapshot.json) that bootstraps a fresh database in one step instead of replaying every migration.
migrations statusShows status of executed migrations on an instance & sync status.
migrations verify-snapshotVerifies that snapshot.json still matches a full replay of all migrations.

migrations snapshot asks for confirmation before writing. An unattended, JSON, quiet or non-TTY run must pass --yes explicitly. The snapshot result is written to stdout; progress and warnings are diagnostics on stderr. For example:

contember migrations snapshot --yes --json > snapshot-result.json

project

CommandDescription
project generate-docGenerates HTML documentation from project schema.
project print-schemaPrints project schema.
project validateValidates project schema.

data

CommandDescription
data export [source]Export data from a project.
data import <file> [target]Import data into a project.
data transfer <source> [target]Transfer data between projects.

actions

See managing actions.

CommandDescription
actions failed-eventsShow failed events.
actions get-event <eventIds...>Get one or more events by id.
actions list-variablesShow action variables.
actions retry-event <eventIds...>Retry one or more events.
actions set-variables <variables...>Set action variables.
actions stop-event <eventIds...>Stop one or more events.

tenant

Tenant-administration commands cover projects, persons, members, API keys, auth policies, mail templates and the auth log. They do not expose every tenant API operation, such as end-user sign-in, sign-up verification and identity-provider authentication flows. Most administration commands need a token with SUPER_ADMIN or PROJECT_ADMIN privileges.

Projects and configuration

CommandDescription
tenant whoamiShow the identity, global roles and project access of the current API token.
tenant project listList projects on the tenant.
tenant project show <slug>Show a project by slug, including ACL roles and their discriminated variableDefinitions.
tenant project create <slug>Create a project.
tenant project update <slug>Update a project's name and/or config.
tenant project secret set <slug> <key>Set a project secret from exactly one explicit source: --value, --value-env <name> or --value-stdin.
tenant config showShow the tenant-wide configuration (signup, password policy, login, captcha, rate limits).
tenant idp listList configured identity providers.
tenant apply [config]Apply declarative tenant configuration. See declarative tenant configuration.

tenant project secret set never reads stdin unless --value-stdin is present. Literal and environment-backed values are sent byte-for-byte. The stdin form removes exactly one final LF or CRLF; all other leading, trailing and embedded whitespace is preserved.

Persons and sessions

CommandDescription
tenant person listList persons. A SUPER_ADMIN sees everyone, anyone else only the members of projects they may view members of.
tenant person show <id>Show a single person with their roles, active sessions and identity provider connections.
tenant person create <email>Create a person (tenant signUp). Consumes the per-IP sign-up rate limit and verifies a captcha when one is configured.
tenant person update <id>Change the profile of a person. Only the fields you pass are changed; neither can be cleared.
tenant person disable <id>Disable a person. They can no longer sign in and all of their API keys are invalidated.
tenant person set-password <id>Set the password of a person. The new password is read from stdin or from an environment variable.
tenant person reset-password-request <email>Send a password-reset e-mail to an address.
tenant person reset-mfa <id>Reset every MFA factor of a locked-out person: active and pending TOTP, e-mail OTP, and their backup codes.
tenant person sign-out <id>Force sign-out: revoke every active session and disable all attached API keys, including permanent keys.
tenant session createCreate a one-time session token for exactly one person selected by --email or --person-id. The token is shown once.
tenant identity role add <identityId> <role...>Grant global (project-independent) roles to an identity.
tenant identity role remove <identityId> <role...>Revoke global (project-independent) roles from an identity.

tenant person create and tenant person reset-password-request accept a captcha token from exactly one of --captcha-token, --captcha-token-env <name> or --captcha-token-stdin. The literal form is visible in shell history and process listings. When tenant person create also needs a password from stdin, put either the password or the captcha token in an environment variable because only one input can consume stdin.

tenant person list returns one page. The server uses a page size of 100 by default and caps it at 1000; the command never fetches more pages automatically. Set --limit to choose the page size and --offset to page explicitly. The --email filter is a case-insensitive substring match. % and _ in the supplied value are treated as literal characters, not wildcard syntax.

tenant session create requires exactly one of --email and --person-id. Use --expiration for a positive lifetime in minutes. In quiet mode it prints only the token.

Project members

CommandDescription
tenant member list --project <slug>List the members of a project with their memberships.
tenant member add --project <slug> --identity <id>Add an existing identity to a project.
tenant member update --project <slug> --identity <id>Replace the memberships of an existing project member.
tenant member remove --project <slug> --identity <id>Remove an identity from a project.
tenant member invite --project <slug> --email <email>Invite a person to a project by e-mail.
tenant member invite-unmanaged --project <slug> --email <email>Invite a person to a project without sending any e-mail.

Memberships are given either as repeated --role options, or as a JSON document via --memberships or --memberships-stdin when a role takes variables. An empty membership list on tenant member update removes every membership. It requires an interactive confirmation or --yes; in JSON, quiet or non-TTY use, pass --yes explicitly.

tenant member invite-unmanaged requires exactly one credential source. Choose a plaintext password with one of --password, --password-env <name> or --password-stdin, or choose a caller-generated SHA-256 password reset token hash with one of --reset-token-hash, --reset-token-hash-env <name> or --reset-token-hash-stdin. Password and reset-token-hash sources are mutually exclusive.

API keys

CommandDescription
tenant api-key listList global permanent API keys with their identity and visible global roles, or project-scoped keys with their identity and memberships using --project <slug>.
tenant api-key create --description <text> (--project <slug> | --global)Create a permanent API key, scoped to exactly one project or globally.
tenant api-key disable <id>Disable a permanent API key, revoking its token immediately.

For global keys, globalRoles is nullable. Human output shows (not visible) when the caller cannot inspect an identity's roles.

tenant api-key create requires exactly one target: --project <slug> or --global. A project key also requires at least one membership, supplied by repeated --role, --memberships <json> or --memberships-stdin. For a global key, repeated --role options grant global tenant roles.

Without --token-hash, the server generates the token and the command prints it once; it cannot be retrieved later. With --token-hash <sha256-hex>, the caller provisions a precomputed token hash. The server never receives the plaintext token, so the command returns or prints the API-key id instead of a token.

tenant api-key create and tenant session create accept --trust-forwarded-client-info. Enable it only when the tenant endpoint is behind a trusted proxy that removes client-supplied forwarding headers and sets the real client IP and user agent. Otherwise a client can spoof audit metadata.

Auth policies, mail templates and the auth log

CommandDescription
tenant policy listList the configured auth policies (per-role MFA and session policy).
tenant policy create (--policy <json> | --policy-stdin)Create an auth policy from an AuthPolicyInput JSON document.
tenant policy update <id> (--policy <json> | --policy-stdin)Replace an auth policy with the given AuthPolicyInput JSON document.
tenant policy delete <id>Delete an auth policy.
tenant mail-template listList the configured mail templates, global and project-scoped.
tenant mail-template add <type> --subject <text> (--content <text> | --content-stdin)Add or replace a mail template. Existing templates with the same project, type and variant are overwritten.
tenant mail-template remove <type>Remove the selected global or project-scoped mail template.
tenant auth-logRead the tenant auth log. Requires the system:viewAuthLog permission.

tenant policy create and tenant policy update require exactly one policy source: --policy <json> or --policy-stdin. tenant mail-template add requires exactly one body source: --content <text> or --content-stdin. Both body sources permit intentionally empty content.

Removing a project-scoped mail template restores fallback to the matching global template. If no matching global template exists, the built-in template is used. Removing a global template falls back directly to the built-in template.

tenant auth-log --limit must be a positive GraphQL Int (at most 2147483647); the server defaults to 100 and caps the returned page at 500. --offset must be a non-negative GraphQL Int. JSON output includes hasMore and nextOffset for paging.

workspace

CommandDescription
workspace update api <version>Updates Contember API version and all related packages.