Docs

Deployment

Sixb runs the same project in two shapes. In development one process co-hosts everything on in-memory providers. In production you split the work into focused role processes that all load the same config and point at the same durable providers.

This page is the operator's mental model: dev vs production, the role commands, and the execution model that moves every async job. What gets scheduled and dispatched is documented under schedules, data, rules, and workflows.

Dev vs production

sixb dev boots one process that hosts the API, the Atlas admin UI, the custom app (if present), and every background runtime — orchestrator, scheduler, functions, rules, and all queue workers. It runs in NODE_ENV=development against in-memory providers, so a single process owns all state:

TS
// sixb.config.ts — local development
import { LocalBlobStorage } from "@sixb/blob-local"
import { createSixb, InMemoryBroker, InMemoryQueues } from "@sixb/core"
import { LocalLakeStorage } from "@sixb/lake-local"
import { SqliteStorage } from "@sixb/sqlite"

export const sixb = await createSixb({
  id: "acme-corp",
  broker: new InMemoryBroker(),
  storage: new SqliteStorage({ path: ".sixb" }),
  lakeStorage: new LocalLakeStorage({ path: ".sixb/lake" }),
  blobStorage: new LocalBlobStorage({ basePath: ".sixb" }),
  queues: new InMemoryQueues(),
})

Production splits those responsibilities across separate processes ("roles"). Every role loads the same config, but each starts only part of the runtime. Because the roles are now separate processes, in-memory providers no longer work — there is no shared memory between them. Each role must point at durable, shared providers: a real storage, lakeStorage, blobStorage, broker, and a queue provider that can be shared across processes.

TS
// sixb.config.ts — production
import { S3BlobStorage } from "@sixb/blob-s3"
import { NatsBroker } from "@sixb/broker-nats"
import { createSixb } from "@sixb/core"
import { DuckLakeStorage } from "@sixb/ducklake"
import { PostgresStorage } from "@sixb/pg"
import { BullMqQueues } from "@sixb/queues-bullmq"

export const sixb = await createSixb({
  id: "acme-corp",
  broker: new NatsBroker({ connection: { servers: process.env.NATS_URL } }),
  storage: new PostgresStorage({ connectionString: process.env.DATABASE_URL }),
  lakeStorage: new DuckLakeStorage({ /* ... */ }),
  blobStorage: new S3BlobStorage({ bucket: process.env.BLOB_BUCKET }),
  queues: new BullMqQueues({ connection: process.env.REDIS_URL }),
})

sixb worker and sixb worker-group refuse to start when queues is InMemoryQueues:

TXT
[SixbWorker] `sixb worker` requires a queue provider that can be shared across
processes. `InMemoryQueues` is for `sixb dev` only.
sixb devProduction roles
Processesonemany, one per role
NODE_ENVdevelopmentproduction
Providersin-memorydurable + shared across processes
QueuesInMemoryQueuesshared queue provider
Use forlocal iteration, testsreal workloads, scaling, isolation

Role commands

Each role is a sixb subcommand that runs in NODE_ENV=production. All accept --entry <path> to load a config other than sixb.config.ts.

CommandRole
sixb apiHTTP/WebSocket API server
sixb atlasBuilt-in admin UI server
sixb appCustom app server
sixb orchestratorEvent-to-queue dispatcher
sixb schedulerSchedule producer (emits schedule.triggered)
sixb functionsRuns registered functions
sixb rulesEvaluates rules
sixb worker <type>Runs one queue worker
sixb worker-group [types...]Runs several queue workers in one process

The worker <type> is one of sync, action, pipeline, projection, or workflow:

BASH
sixb worker sync
sixb worker projection

sixb worker-group runs several in one process. With no types it starts every worker type that has registered work in the config:

BASH
# explicit
sixb worker-group sync pipeline projection

# auto: every worker type with registered definitions
sixb worker-group

A role process is idle, not an error, when it has nothing to do — an orchestrator with no routes, a rules process with no rules, or a worker group with no registered worker types prints a warning and stays running.

Execution model

This is the core production data flow. Everything asynchronous in Sixb moves through it:

TXT
event  ->  orchestrator  ->  queue  ->  worker  ->  event
  1. Something produces a domain event — a sync finishes, a dataset version is committed, or a schedule triggers.
  2. The orchestrator subscribes to events, matches each against compiled routes, and enqueues a job onto the right queue.
  3. A worker claims the job, runs it, and writes a run record.
  4. The worker emits a finished event, which can drive the next step (for example sync.run.finished -> a projection job).

The orchestrator subscribes only to the event types its routes need, and fan-out (one event -> several jobs) is best-effort: a failure enqueuing one job never drops its siblings.

Two paths skip the orchestrator. Requesting an action enqueues onto queues.actions directly (the action.requested event is an observation, not a route) and posting a message to an agent thread enqueues onto queues.agents, while the API can enqueue a sync, pipeline, or workflow run on demand. All still flow through the queue/worker half of the model.

Queues and workers

There is one queue per worker type, and each worker claims from exactly one queue.

WorkerQueueEnqueued by
syncqueues.syncRunsorchestrator (sync triggers), or API run-request
pipelinequeues.pipelinesorchestrator (pipeline triggers), or API run-request
projectionqueues.projectionsorchestrator, on dataset.version.committed
workflowqueues.workflowsorchestrator (scheduled), or API run-request
actionqueues.actionsa requested action, enqueued directly
agentqueues.agentsa posted agent-thread message, enqueued directly

Run records

Every queued execution writes a durable run record to storage, so progress survives restarts and is visible in Atlas. A run moves through the same lifecycle across worker types:

StatusMeaning
runningclaimed and executing
succeededcompleted and committed
failederrored; recorded with the failure name/message
cancelledaborted (by shutdown or an explicit request)

Run records carry the inputs, outputs, timing (startedAt / finishedAt), and any error, so a run stays auditable after the fact.

Durability under failure

Workers claim jobs with a lease (default 15 minutes). On each outcome:

  • success — the job is completed and removed from the queue.
  • execution error — the job is failed (the default) or retried with an optional delay.
  • abort (shutdown mid-job) — the job is released by default so another process can reclaim it.

Because jobs and run records live in durable, shared providers, a crashed worker loses no work: the unfinished job's lease expires and another worker reclaims it.

Startup order

Roles start consumers before producers and shut down in reverse. This guarantees that by the time anything emits an event or enqueues a job, the role that handles it is already listening. The co-hosted dev runtime applies this automatically; when bringing up separate processes, follow the same order:

  1. Consumers first — rules, functions, then the action, agent, projection, pipeline, workflow, and sync workers.
  2. Producers last — the orchestrator (subscribes and enqueues), then the scheduler (emits triggers).

On shutdown the order reverses: the scheduler stops producing first, the orchestrator drains pending dispatches, then workers, functions, and rules drain in turn.

Atlas admin UI

Atlas is the built-in browser admin UI. It serves the UI shell and static assets and injects the API origin and auth audience at runtime; the browser then authenticates against the API server. Atlas does not serve API routes — /api, /auth, /ws, and /docs belong to the API server.

In sixb dev, Atlas is co-hosted automatically. In production, run it as its own role pointed at the API origin:

BASH
sixb atlas --api-public-origin https://api.acme.example.com

A minimal production topology

A typical deployment runs each role as a separate process, all loading the same config against shared durable providers:

BASH
sixb api            # HTTP/WS API
sixb atlas          # admin UI
sixb orchestrator   # event -> queue dispatch
sixb scheduler      # cron/interval triggers
sixb rules          # rule evaluation
sixb functions      # registered functions
sixb worker-group   # all registered queue workers

Scale by running more copies of any role. Extra sixb worker processes share the queue and increase throughput, since each job is claimed by exactly one worker.

  • Runtime — how createSixb() discovers and wires a project
  • Infrastructure — provider choices for storage, queues, and the broker
  • Events — the domain events that drive the execution model
  • Schedules — cron and interval triggers
  • Data — syncs, pipelines, and projections
  • Workflows — workflow runs and interventions

Search docs

Search the documentation