whity-cli
Migrations, seeding, tenants, plugins, translations and the background workers — the operational surface of a Whity deployment, from one tool.
Two entry points, same commands.
bin/whity-cli is the CLI tool. In a Docker install, most operations are run through the container against public/index.php, which dispatches the same commands — that is the form the quick start uses, because it needs no PHP on your host.
--help works at both levels: on its own for the command list, and after a command for the options that command accepts. An unknown option is rejected rather than ignored.
docker exec whity_frankenphp \
php public/index.php migrate runwhity-cli migrate status
whity-cli --help
whity-cli seed --helpEvery command.
| Command | Arguments | What it does |
|---|---|---|
migrate | status | run | rollback | Manage database migrations. run also creates the bootstrap administrator. |
seed | --with-fixtures · --with-document-demo | Seed the default tenant, roles and notification-template baseline. Idempotent — never rewrites an existing account's password. |
plugin | list | enable | disable | reload | Manage plugins at runtime, without a restart. |
tenant | list | create | update | delete | Manage tenants. |
totp | — | TOTP secret maintenance — re-encrypt legacy secrets. |
queue:work | — | Run the durable async job worker loop. |
events:relay | — | Deliver durable domain events to their listeners. Run alongside queue:work, or event_outbox grows without bound. |
schedule:run | — | Run the cron-tick scheduler, exactly once per minute across workers. |
health:watch | — | Sample service health for the status page, from outside the app. |
i18n:extract | --check | Rebuild the English translation catalogue from the t() calls in the source. --check verifies without writing, which is what CI runs. |
i18n:sync | --language · --all · --dry-run | Seed catalogue keys missing from the translations table. Never overwrites an existing row, in any language. |
i18n:coverage | — | Per-domain translated and missing counts for every committed language. Needs no database. |
permissions:unheld | — | Report registered permissions that no role currently holds. |
payments:secret | — | Payment provider secret maintenance. |
scale:seed | — | Bulk-insert a deterministic, parameterised large-scale multi-tenant dataset for load testing. |
Also dispatched by public/index.php
Operational commands you run inside the container.
generate:openapi | Regenerate public/openapi.json from the routing layer. |
update:check | Check whether a newer core release is available. |
form-uploads:sweep | Remove orphaned form upload files. |
revoked-tokens:cleanup | Prune expired entries from the revoked-token store. |
The three you must keep running.
A deployment that skips these looks healthy and quietly stops doing work in the background.
queue:workThe durable async job worker. Without it, queued jobs are recorded and never run.
events:relayDelivers durable domain events to their listeners. Run it alongside
queue:work— without it, asynchronously dispatched hooks record events that are never delivered andevent_outboxgrows without bound.schedule:runThe cron tick, exactly once per minute across every worker.
CLI FAQ.
How do I run the Whity CLI?
Two entry points reach the same commands. bin/whity-cli is the CLI tool itself — whity-cli <command> [options]. Most operations in a Docker install are run through the container, for example docker exec whity_frankenphp php public/index.php migrate run. Use whity-cli --help for the command list and whity-cli <command> --help for one command's options.
Which background workers does Whity need running?
queue:work for durable async jobs, events:relay to deliver domain events to their listeners, and schedule:run for cron ticks. events:relay in particular must run alongside queue:work — without it, asynchronously dispatched hooks record events that are never delivered and the event_outbox table grows without bound.
Will i18n:sync overwrite existing translations?
No. The sync only ever inserts keys that are missing. It contains no UPDATE and no DELETE statement at all, and a unit test asserts that about the class's own source, so existing rows are never written to in any language or scope.
Every flag, in the docs.
The full reference — every action, option and exit code — is compiled from the repository.