Repository navigation
Brainstem 1.3.0
First release on the way to 2.0: one database for all state (SQLite or Postgres) and a durable decide/deliver pipeline. Includes the unreleased 2.0 Phase 0 and Phase 1 work. See "Behaviour changes" below before upgrading.
Brainstem 2.0 Phase 0 (safety net), see docs/roadmap-2.0.md:
- CI workflow running the syntax check, unit/integration tests, and acceptance tests on every PR and push to
main. - Tests for the runtime polling loop, shutdown and abort handling, poll-retry re-delivery (1.x behaviour), per-ID ordering and decision caching in the core, config saves, and the SSE stream.
- Multi-core acceptance harness (
npm run test:acceptance) that runs several real core processes against shared state. The 2.0 cluster guarantees are tracked astodotests until Phase 3. - Architecture decision records for the 2.0 design in
docs/adr/.
Brainstem 2.0 Phase 1 (storage abstraction):
- One database for all state, via Kysely: SQLite (default,
sqlite:data/brainstem.sqlite) or Postgres (BRAINSTEM_DATABASE_URL=postgres://...). The runtime and the embedded API share one connection. - Records, checkpoints, telemetry, stats, and auth stores are async and database-backed. Checkpoints are read from the database on every poll, with no per-process cache.
- New
ctx.stateplugin API for durable plugin state. The Matrix destination keeps its suppression state there, claiming a notification before sending so two processes can't both send it. - Record writes check the expected revision (groundwork for multi-core).
- Safe first-admin creation under concurrent setup requests; login rate limiting stored in the database and keyed by client IP behind trusted proxies (
BRAINSTEM_TRUSTED_PROXIES); retention pruning moved to a periodic maintenance job. brainstem migrate,migrate status,migrate import-v1, andmigrate copy/sqlite-to-pgcommands.- 1.x state (
web-stats.sqlite,checkpoints.json,matrix-notifications.json) is imported automatically on first start; legacy files are never modified. - CI runs every storage test against both SQLite and Postgres 17.
- Behaviour change: with no
records/checkpointsconfig, state now persists to the database (previously records were in memory and checkpoints in JSON). Explicitmemory,json, andsqlitetypes still work.
Brainstem 2.0 Phase 2 (durable pipeline), see docs/delivery-pipeline.md:
- The runtime is split into
poll,decideanddeliverstages connected by jobs in the newjobstable. Each stage commits its outputs and the next stage's jobs in one transaction. Checkpoints advance in the same transaction that stores the polled observations. - One deliver job per (decision, destination). A failing destination is retried on its own with exponential backoff and jitter (
runtime.queue.deliver.retry, orretryper destination). It no longer re-triggers other destinations, the decision or the poll, and a slow destination no longer stalls polling. - Jobs that run out of attempts become dead jobs. The new Deliveries page in the Web UI shows queue depth, the delivery log and dead jobs, with redrive and discard. API:
GET /api/pipeline,GET /api/jobs,GET /api/deliveries,POST /api/jobs/redrive,POST /api/jobs/discard. - Delivery log (
deliveriestable) keyed by an idempotency key, which is checked before sending. Delivered rows are pruned afterruntime.deliveries.pruneAfterDays(14). - Decide jobs are serialized per observation ID through the queue (
queue_key), and record writes are compare-and-set on the revision. - Jobs hold renewable leases. Jobs from crashed processes are reclaimed, and jobs interrupted by shutdown are handed back without using up an attempt.
- Destination plugin SDK v2 (
brainstem.destination/v2):handle(ctx, output)receives the serializablebrainstem.outputenvelope, plusctx.idempotencyKeyandctx.attempt. v1 destinations keep working through an adapter, which logs a deprecation warning, until 3.0. - Built-in destinations are on v2:
http-jsonsends anIdempotency-Keyheader.- Matrix uses the key as
txnId, replacing the random id per attempt. - opencode-runner resumes the session of a retried delivery instead of starting a new one.
- log-decisions is migrated.
- Migration
0002_pipelineadds thejobsanddeliveriestables.migrate copyincludes them. - New telemetry:
brainstem.core.failed.brainstem.destination.failednow carriesattempt,deadandretryInMs. New metrics:decisions.failed,decisions.deadanddestinations.dead. - Behaviour changes:
runtime.destinations.blockInputOnFailureis ignored, with a warning.- A failing decision no longer holds back the input checkpoint. It stays queued and is retried.
--onceruns every due job after polling, and exits non-zero if any poll or job failed.- Config callbacks of the built-in destinations (
notificationKey,titleForEvent,promptTemplate) receiveoutput.payload. - Give destinations an
id: it is part of the idempotency key.
Docker image: ghcr.io/jurrebuunk/brainstem:v1.3.0. Upgrade notes: docs/delivery-pipeline.md#upgrading-from-1x.