CLI FEATURE
Workers & Scheduling
Semitexa is not only request-response code. The CLI also owns the long-running workers and operator interventions that keep the platform moving.
Feature Guide
A quick orientation block that answers the essential questions: what this feature does, how it works, why it matters, and the key concepts behind it.
What this does
Semitexa CLI also owns the long-running side of the platform: queues, schedules, webhook delivery, mail workers, and tenant-context execution.
How it works
Dedicated commands expose each stage separately: inspect schedules, plan due runs, start workers, inspect webhook inbox/outbox state, replay deliveries, and run commands in tenant scope.
Why it matters
This makes background systems operable. Operators can inspect, intervene, and replay explicitly instead of treating async infrastructure as invisible magic behind the web server.
Key concepts
- queue:work
- Runs the async events worker for queued handlers.
- scheduler:plan
- Materializes due schedule occurrences into concrete run rows before workers execute them.
- tenant:run
- Executes any command inside a specific tenant context.
Operator Runtime
Long-running platform work is part of the same command surface
Async queues, scheduler pools, outbound webhooks, mail delivery, and tenant-scoped execution all surface through explicit commands. That makes the runtime operable without custom one-off scripts.
Dedicated workers. queue:work, webhook:work, and mail:work turn background processing into explicit operator processes rather than hidden side-effects.
Planner plus executor. The scheduler surface is separated into list, plan, run-now, and work so teams can inspect and intervene before blindly starting daemons.
Context-aware execution. tenant:run lets operators execute commands inside a concrete tenant context instead of manually injecting environment assumptions.
| Command | Purpose | Why it matters |
|---|---|---|
bin/semitexa queue:work nats async |
Run the async events worker against a chosen transport and queue. | Keeps event-driven background work explicit and separately operable. |
bin/semitexa scheduler:list && bin/semitexa scheduler:plan |
Inspect configured schedules, then materialize due runs. | Good operational sequence before starting or debugging the scheduler worker. |
bin/semitexa webhook:show outbox --status=pending && bin/semitexa webhook:work |
Inspect webhook backlog, then run delivery worker. | Makes outbound integration behavior reviewable rather than opaque. |
bin/semitexa tenant:run acme cache:clear --twig |
Run a command inside a tenant context. | Critical when the platform behavior depends on tenant-aware configuration or data isolation. |
Bring up the scheduler loop deliberately
bin/semitexa scheduler:list
bin/semitexa scheduler:plan
bin/semitexa scheduler:work default
Inspect and replay webhook traffic
bin/semitexa webhook:show inbox --limit=10
bin/semitexa webhook:replay:inbound <delivery-uuid>
Operate background work in one tenant
bin/semitexa tenant:run acme queue:work
bin/semitexa tenant:run acme cache:clear --twig
Verified against Semitexa Ultimate 2026.09.19.1020
Workers & Scheduling
Semitexa is not only request-response code. The CLI also owns the long-running workers and operator interventions that keep the platform moving.
How it works
queue:work, webhook:work, and mail:work are separate explicit processes rather than hidden side-effects of the web runtime. The scheduler surface is split into scheduler:list (inspect), scheduler:plan (materialize), and scheduler:work (run the loop) so teams can inspect state before pushing harder. tenant:run executes any command inside a concrete tenant context, including queue and cache operations.
In a project started with bin/semitexa server:start, these workers are already running: a worker service, in a container of its own so it can be scaled apart from the web server, runs every line bin/semitexa worker:processes prints. Each package names what it needs with #[AsWorkerProcessSource]: core one queue:work per queue its listeners and handlers use, mail mail:work, media media:work, the scheduler scheduler:plan (once a minute) and one scheduler:work for each pool its jobs name (scheduler:work reads one pool; a pool chosen only when a job is dispatched is named by the package that dispatches it, as workflow does for its timeout pools), webhooks webhook:work. It runs whatever EVENTS_ASYNC says: with EVENTS_ASYNC=0 and no EVENTS_TRANSPORT the queue consumers name nothing (the transport is in-memory, one per process), and the scheduler and webhooks still run; EVENTS_TRANSPORT=nats or database gives them their queue:work all the same. WORKER_REPLICAS in .env sets how many worker containers run.
Why this matters
Operability matters as much as functionality. Separate inspect, plan, execute, and replay actions mean the platform can be observed and intervened on deliberately instead of forcing operators to restart processes and hope for the best.
How it works
Dedicated commands expose each stage separately: inspect schedules, plan due runs, start workers, inspect webhook inbox/outbox state, replay deliveries, and run commands in tenant scope.
Why it matters
This makes background systems operable. Operators can inspect, intervene, and replay explicitly instead of treating async infrastructure as invisible magic behind the web server.
Key concepts
- queue:work
- Runs the async events worker for queued handlers.
- scheduler:plan
- Materializes due schedule occurrences into concrete run rows before workers execute them.
- tenant:run
- Executes any command inside a specific tenant context.