Skip to content

CLI reference

z4j ships a CLI named z4j. Runs inside the container or from a local pip install.

Terminal window
z4j --help
Terminal window
z4j serve [--host 0.0.0.0] [--port 7700] [--workers 1]

Starts the FastAPI application via Uvicorn. This is the container entrypoint.

Terminal window
z4j check

Validates that the configuration loads and the database answers, then prints whatever migration revision it finds. Non-destructive.

It does not confirm Alembic is at head, despite an earlier version of this page saying so. It never compares the stored revision against the head your build expects, and a database with no alembic_version table at all still exits 0. Use z4j migrate current --check-heads, which exits non-zero unless every head is applied. z4j doctor is not a substitute either: it runs check and returns its exit code.

Terminal window
z4j doctor

Runs check first and returns its exit code, then adds configuration warnings that check cannot raise because they are not failures. It takes no options.

What it actually warns about: Z4J_ENVIRONMENT=dev with a non-loopback Z4J_BIND_HOST, Z4J_DEBUG_HOST_ERRORS being on, secrets having been auto-minted into ~/.z4j/secret.env, Z4J_METRICS_PUBLIC being on, and an install with no users, no projects or no agents yet.

It inherits check's blind spot and adds nothing to it: no Alembic head comparison, no registry probe, no scheduler reachability check, no mTLS validation, and no --json output. An earlier version of this page claimed all five, and z4j doctor --json exits 2 with "unrecognized arguments". For the schema head, use z4j migrate current --check-heads.

Terminal window
z4j status

Prints a summary of current brain state: version, Alembic head, environment, database URL, and row counts (users, projects, agents, tasks, active sessions, audit rows).

Terminal window
z4j version

Prints the installed z4j version.

Terminal window
z4j init

Scaffolds $Z4J_HOME/config.env as a mode 0644, tunables-only starter file. It intentionally excludes credentials, tokens, secrets, and other bootstrap values that belong in $Z4J_HOME/secret.env or the process environment. It is not a complete runnable configuration. The command refuses to overwrite an existing file unless --force is supplied.

Terminal window
printf '%s\n' "$ADMIN_PASSWORD" | \
z4j bootstrap-admin --email [email protected] --display-name "You" \
--password-stdin

Creates the initial admin user and the default project. First-boot only. A password is required; prefer --password-stdin with a value obtained from a secret manager so it is not exposed in the process list or shell history.

Terminal window
z4j createsuperuser --email [email protected] --display-name "You" --password-stdin

Django-friendly alias for bootstrap-admin. Use --password-stdin to pipe the password in from a secret manager.

Terminal window
z4j changepassword [email protected] --password-stdin

Changes a user's password. Intended for admin recovery / CLI-only ops when the user cannot reach the dashboard reset flow.

Terminal window
z4j reset-mfa [email protected]
z4j reset-mfa [email protected] --confirm # skip the interactive prompt

Operator escape hatch for the lost-phone-AND-lost-recovery-codes case. Clears the user's MFA secret, recovery codes, and trusted devices in one transaction; writes a user.mfa_reset_by_admin audit row attributed to the OS user running the CLI. Shell-only by design -- there is no REST surface so an attacker who has only the dashboard cannot trigger it. See Multi-factor authentication for context.

Terminal window
z4j config show

Prints the resolved effective settings (post-env-var, post-defaults, post-~/.z4j/config.env). Fields typed as SecretStr are masked unless --reveal-secrets is passed. Plain URL fields such as database_url and scheduler_trigger_url are printed as configured and can contain credentials, so treat the entire output as sensitive. The admin settings endpoint exposes the same effective settings but applies additional secret-name masking.

Terminal window
z4j config validate

Safely captures the candidate tunables file, rejects unknown Z4J_* keys, and validates the values through the runtime settings decoder without booting the app. Because config.env is a tunables-only layer, the command supplies inert bootstrap placeholders for required secrets, hosts, public URL, and, when absent, the database URL. A zero exit therefore means the candidate tunables are valid. It does not validate the process environment or secret.env, prove that the effective runtime configuration will load, or test database connectivity.

Terminal window
z4j backup --output /var/backups/z4j-$(date +%Y-%m-%d).db

Snapshots the brain database to a single file. On SQLite, a VACUUM INTO clone; on PostgreSQL, a pg_dump custom-format archive.

--output / -o is the only option, and it is required. An existing destination is always refused, and there is no flag to override that - move or delete the old file, or pick a different path. The output is created private to its owner, so no umask is needed. (An earlier version of this page described the opposite behaviour and offered a --no-clobber flag; that flag does not exist and passing it aborts the command with exit 2.)

Terminal window
z4j restore /var/backups/z4j-pre-upgrade.db --force

Restores from a backup file produced by z4j backup. --force is required, and it records your assertion that the brain is stopped rather than verifying it: there is no liveness detection, so stopping the brain first is your responsibility.

The target must already be a provisioned z4j database at the current migration head, with an audit chain that verifies under the local keyring. An empty database is refused. Restore is also forward-only, so an archive from a previous release is migrated up rather than taking you back to it. See backup and restore for the full ceremony.

Terminal window
z4j migrate upgrade head
z4j migrate current
z4j migrate history
z4j migrate downgrade <revision>

Runs an Alembic command against the brain database.

Terminal window
z4j misfires --project my-app
z4j misfires --project my-app --limit 200
z4j misfires --project my-app --json

Lists a project's detected schedule misfires across ALL of its schedules, newest first. A misfire is a system-detected "this enabled interval or cron schedule missed its expected slot past the grace window" event, recorded by the brain's misfire detector (see misfire detection). The default output is an aligned text table (schedule id, detected-at, name, engine, kind, lateness, grace); --json emits {"project": <slug>, "misfires": [...]} for scripting. --limit defaults to 50 and is capped at 1000. This is the shell-side twin of the VIEWER-facing GET /api/v1/projects/{slug}/schedules/misfires endpoint, so an operator can triage missed slots without the dashboard. Exit 0 on success (including an empty history); exit 2 on a bad slug, unknown project, or unreachable database.

Terminal window
z4j upgrade
z4j upgrade --apply

Without flags, checks PyPI for newer releases of the packages in z4j's bundled release catalogue and changes nothing. --apply runs pip install -U z4j, which lets the umbrella package pull compatible adapters forward. There is no --check option because check-only is the default. Mirrors POST /api/v1/admin/system/versions/check on the API side.

Terminal window
z4j allowed-hosts list
z4j allowed-hosts add z4j.example.com
z4j allowed-hosts remove old-host.example.com
z4j allowed-hosts path

Manages the persistent Host header allow-list (Z4J_ALLOWED_HOSTS). path prints the file the brain reads from so operators can edit it directly. See allowed hosts.

Terminal window
z4j audit verify

Walks the full HMAC-chained audit log. Exit 0 on success, 1 on broken chain.

Pass --known-head with an envelope recorded earlier to detect a log that was rolled back to an older authentic state, which the chain alone cannot show. The result is one of CURRENT_MATCH, PRUNE_MATCH, CURRENT_PRUNE_MATCH, VERIFIED_ANCESTOR, INVALID or UNPROVABLE, and anything other than a match exits nonzero.

Terminal window
z4j audit export-head > /secure/last-known-head
z4j audit export-head --verify > /secure/last-known-head

Prints the authenticated current chain head as the JSON envelope audit verify --known-head accepts. The state row is proved against the configured keys first, and a head that cannot be authenticated is refused rather than exported.

The envelope goes to stdout and every other message to stderr, so the redirect above yields a complete file. --verify walks the active generation first and refuses to export a head from a chain that did not verify clean, which is the right choice for an unattended job: anchoring a compromised chain would record it as the trusted state.

Anchor that file somewhere the database role cannot rewrite. A head kept only in the same database proves nothing, because a writer can restore an older copy of it. Write the file exactly as emitted: the parser's key set is closed, so appending anything makes it unreadable.

The audit group also carries the chain-key lifecycle commands (rotate-chain-key, retire-chain-key) and the chain-state operations (activate-chain-state, reseal-watermark, export-and-delete-frozen, fork-cleanup). Each takes --help. See HMAC audit chain for the rotation ceremony.

Terminal window
z4j metrics-token # print the active token
z4j metrics-token rotate # update the managed store; restart is required

Manages the bearer that gates /metrics. Resolution order is an explicit Z4J_METRICS_AUTH_TOKEN, then $Z4J_HOME/secret.env, then first-boot auto-minting. rotate works only when the effective source is the managed secret.env; it refuses to rewrite a lower-precedence file when an explicit environment value wins. The command writes and prints the new token, but a running brain keeps the old token in memory. Update the Prometheus scrape credential, then restart every brain replica. The old token stops working only after those restarts.

Terminal window
z4j mint-scheduler-cert \
--name scheduler-prod-1 \
--ca-cert /etc/z4j/pki/ca.crt \
--ca-key /etc/z4j/pki/ca.key \
--out-dir /etc/z4j/pki/scheduler-prod-1/

Mints scheduler-prod-1.crt and scheduler-prod-1.key for a z4j-scheduler instance to authenticate to the brain. The command does not modify the brain configuration. Add scheduler-prod-1 to the JSON Z4J_SCHEDULER_GRPC_ALLOWED_CNS value and restart every brain replica before deploying the client certificate. See production hardening.

Terminal window
z4j reset-setup --force

Deletes pending first-boot tokens, preserves existing signed setup audit rows, and appends a signed setup.tokens_reset row. It refuses if any user exists. Without --force, it exits nonzero without changing the database. Use reset --force for a full wipe.

Terminal window
z4j reset # refuses without --force
z4j reset --force

Wipes every runtime table (users, sessions, projects, agents, tasks, events, schedules, audit, ...). Schema stays; Alembic does not re-run. Leaves z4j in a pre-first-boot state.

Flag Description
-h, --help Show help for the command

Per-command flags surface with z4j <command> --help.

When you run the standalone scheduler service, the z4j-scheduler CLI ships these commands. See z4j-scheduler for deployment context.

Terminal window
z4j-scheduler serve # run the scheduler process
z4j-scheduler version # print installed version
z4j-scheduler check --brain-grpc-url ... # one-line pass/fail health
z4j-scheduler status # local introspection: version + configured brain URLs + mode (no network)
z4j-scheduler info [--json] # live runtime snapshot from the running scheduler's GET /info
z4j-scheduler doctor # comprehensive diagnostics
z4j-scheduler restart # informational stub (delegates to systemd / k8s)
z4j-scheduler import --from <tool> ... # migrate from celery-beat / rq / apscheduler / cron
z4j-scheduler export --to <tool> ... # reverse-export
z4j-scheduler schedules add / list / trigger / disable / enable / edit / history ...

All commands accept --help. Most read configuration from Z4J_SCHEDULER_* env vars; explicit flags win where provided.

Each framework adapter ships its own doctor plus a few helpers under python -m z4j_<framework>:

Terminal window
python -m z4j_bare doctor # canonical implementation
python -m z4j_flask doctor # same checks; reads Flask app.config or env
python -m z4j_fastapi doctor # same checks; reads env
python -m z4j_django doctor # equivalent Django management command: ./manage.py z4j_doctor

All four implement doctor with the same probes (buffer dir writable, brain DNS / TCP / TLS, WebSocket upgrade) and accept the same flags (--no-websocket, --json). See frameworks/bare for the canonical reference.