# Compatibility

> Node.js 22+, PostgreSQL 15+, no extensions, and an exact-match schema — what is tested and what that means.

You need to know two things before adopting a queue: will it run on your stack, and what does
"supported" actually prove. Workhorse requires Node.js 22 or newer and PostgreSQL 15 or newer, and
a version counts as supported only when continuous integration runs the full suite against it on
every change. Workhorse may run elsewhere, but untested versions do not carry the same correctness
evidence.

## The tested matrix

CI exercises every combination of Node.js 22 and 24 with PostgreSQL 15, 16, 17, and 18. The
runtime exports the same boundary to tooling: `MINIMUM_NODE_MAJOR` is 22, `MINIMUM_POSTGRES_MAJOR`
is 15, and `SUPPORTED_NODE_MAJORS` and `SUPPORTED_POSTGRES_MAJORS` list the CI majors. A test in
the repository fails whenever the CI matrix, the package `engines` fields, and these constants
drift apart, so the published numbers are always the exercised numbers. The
[compatibility matrix](https://github.com/stablemates/workhorse/blob/main/docs/compatibility.md)
tracks them as they change.

You can classify a connected server at runtime. `readPostgresSupport` reports whether it is tested,
merely newer than tested, or below the minimum; `assertSupportedPostgres` refuses to proceed below
the minimum before schema installation can fail confusingly.

```ts
import { readPostgresSupport } from "@stablemates/workhorse";

const support = await readPostgresSupport(pool);
console.log(support); // { major: 17, version: "17.2 …", supported: true, tested: true }
```

A server newer than the tested majors is reported as supported but untested rather than rejected —
refusing to run on a release that did not exist at publish time would strand you on every
PostgreSQL upgrade.

## Plain PostgreSQL, no extensions

Workhorse requires only stock PostgreSQL with its default procedural language. It installs no
extension, so it runs on managed services — RDS, Cloud SQL, Neon, Supabase — without an allowlist
conversation.

Published packages are ESM with TypeScript declarations. Plain JavaScript uses the same runtime;
CommonJS loading is outside the supported package format.

## Match the schema exactly

The PostgreSQL schema is the durable protocol. The application runtime and the installed schema
must agree exactly, because mismatched workers could interpret lifecycle state differently.

Call `assertSchemaCompatible` during startup. A mismatch should fail the process before it claims
or changes work.

Use `installSchema` for a fresh database and `migrateSchema` for an installed schema. The migration
entrypoint applies immutable, forward-only steps from the supported baseline and refuses missing,
older-than-baseline, or newer schemas before changing them. Each step commits one version in its
own transaction behind an advisory lock and rolls back atomically on failure. Development schemas
at versions retired before the baseline still require a reset.

The dashboard transport has its own `dashboard/v1` contract. TypeScript, Python, and Go backends
use its shared procedure schemas, HTML placeholders, request order, and HTTP fixtures. Embed a
shipped backend for operator access; use `Queue` for application operations and `Admin` for
operator integrations.

Clients in other languages use the versioned SQL functions and JSON fixtures under `protocol/v1/`
as their compatibility boundary. The fixtures require clients to reject an incompatible schema or
protocol before mutation, then verify canonical requests, results, transitions, and structured
errors. See [Language clients](/docs/language-clients) for the ownership split.

The Python `stablemates-workhorse` package supports Python 3.10 through 3.14. It uses Psycopg synchronously
or asynchronously and asyncpg asynchronously. Every worker runtime delegates cron evaluation to
the versioned PostgreSQL functions and each definition's stored IANA timezone. Its release test
installs the built wheel and source distribution bare, with the compatibility `psycopg` extra, and
with the `asyncpg` extra. It runs the public examples and checks the active Python and PostgreSQL
versions against the declared matrix. The package version floats
independently from the TypeScript packages because schema and SQL protocol versions define their
compatibility.

## Bun and Deno

Bun and Deno hold a smoke-tested tier, not support. On pull requests, pushes to `main`, and nightly
runs, CI executes an enqueue, claim, and complete round-trip through the built package against
PostgreSQL under the latest release of each; `SMOKE_TESTED_JS_RUNTIMES` exports the tier to tooling. A green lane proves the driver
connects, the schema installs, and one job completes there — the full test suites run under
Node.js only, so no stronger claim is made. Known runtime-specific findings live in the
[compatibility matrix](https://github.com/stablemates/workhorse/blob/main/docs/compatibility.md).

Serverless platforms have a separate lifetime and connectivity matrix. Read
[Serverless and edge](/docs/serverless) before choosing where producers and worker processes run.

## Release each package line from its own changelog

Core and its integration packages release together. Keep their versions aligned so adapters,
dashboard contracts, and exported types describe the same source revision. The
[changelog](https://github.com/stablemates/workhorse/blob/main/CHANGELOG.md) records breaking
changes and the schema version each release requires.

The first public beta is `0.1.0-beta.1` for npm and Go, and `0.1.0b1` for Python. Any 0.x minor
release may break compatibility, including the schema. There is no upgrade path between 0.x
releases; ordered migrations begin at 1.0.0.

The dashboard and core may use different patch releases within the same minor line. The dashboard
server reads core-owned versioned views and functions, so a core patch remains compatible when its
migration preserves those contracts.

`stablemates-workhorse` releases from its own `python/vX.Y.Z` tag after its format, lint, type, test, packed,
and site lanes pass. Its tag must match `python/pyproject.toml` and `python/CHANGELOG.md`. The release
builds a source distribution and universal wheel, then PyPI trusted publishing uploads both.

The Go module records independent versions in `go/CHANGELOG.md`. The repository release script
runs the full gate before it creates and pushes the matching `go/vX.Y.Z` tag.

GitHub Actions remain disabled while the repository is private, so these release paths are defined
but must not run until that restriction is lifted. Once public, pull requests run support-boundary
pairs and `main` plus nightly runs execute the full matrix. Branch protection requires the stable
`CI / required` check, and protected npm and PyPI environments require a second person to approve
publication.

One honest caveat: support proves correctness under the tested matrix. It does not promise a
throughput level for your database, payloads, handlers, or deployment topology.

## Next

- [Installation](/docs/installation) — install and check the schema deliberately
- [Language clients](/docs/language-clients) — implement the versioned SQL protocol
- [Limitations](/docs/limitations) — compare support with your application requirements
- [Deployment and operations](/docs/operations) — enforce compatibility before workers start

---

Exact database requirements and schema compatibility boundary:
[architecture reference](https://github.com/stablemates/workhorse/blob/main/docs/architecture.md#operational-limits).
