A release touches the broker image, five client SDKs and the CLI, across five package registries plus GitHub Releases. Two of those channels are published by CI on a trigger; the rest are published by a person running a command. Knowing which is which is the point of this page.
Where the version lives
server/server.json is the single source. Three separate things read it:
server/build.rsextractsversionand exports it asQUEEN_VERSION, which is whatGET /health, the boot log andqueenctl pingreport. A version bump therefore forces a rebuild of the broker.build.shusesnameandversionto tag and push the image locally..github/workflows/docker-build.ymlreads both withjqfor the same purpose in CI.
Everything else carries its own version, or none at all.
| Artifact | Where its version lives |
|---|---|
| Broker image | server/server.json |
| JavaScript client | clients/client-js/package.json |
| Python client | clients/client-py/pyproject.toml |
| PHP/Laravel client | nowhere: composer.json has no version field; Packagist reads the git tag |
| Go client | nowhere: the git tag is the version |
queenctl |
nowhere: the tag is the version, injected at build time with -ldflags |
| C++ client | nowhere: header-only, taken at whatever tag you check out |
Dashboard (app/) |
app/package.json, private, never published separately |
The versioning rule
The broker and the SDKs are version-aligned at 1.0.0. One MAJOR.MINOR.PATCH for the
broker image, npm, PyPI, Packagist, the Go module tag and the CLI tag, so a client version tells
you which broker it was released against without a compatibility matrix.
What CI publishes
The container images
.github/workflows/docker-build.yml publishes two images to GHCR, as a matrix over the two
Dockerfiles:
| Image | Dockerfile | Version from |
|---|---|---|
ghcr.io/queen-mq/queen |
Dockerfile |
server/server.json |
ghcr.io/queen-mq/queen-proxy |
proxy/Dockerfile |
proxy/Cargo.toml |
Both build for linux/amd64 and linux/arm64 from the repository root: the proxy crate embeds
../server/webapp/dist, so its context cannot be proxy/. Each gets three tags: its own
version, sha-<short>, and latest. The broker also receives QUEENCTL_VERSION and
QUEENCTL_COMMIT as build arguments so that queenctl version inside the container matches what
the broker reports.
Authentication is the workflow’s own GITHUB_TOKEN under permissions: packages: write, so there
are no registry secrets to rotate. Layer caching is the GitHub Actions cache, scoped per image
(scope=${{ matrix.image }}); a shared scope would make the two matrix legs evict each other’s
layers and cold-build every run.
Triggers are a push to the release branch, a v* tag, or workflow_dispatch. A pull
request against release builds both images without pushing.
The CLI
.github/workflows/release-cli.yml fires on a tag matching clients/client-cli/v* and runs
GoReleaser v2, which cross-compiles queenctl for linux, darwin and windows on amd64 and arm64
(windows/arm64 excluded), produces tar.gz archives (zip on windows) with checksums and
pre-built shell completions, and attaches them to a GitHub Release. Version, commit and build
date go in through -ldflags, so queenctl version reports the tag.
The directory-prefixed tag shape is required by Go’s module proxy for a nested module, and
GoReleaser wants bare semver, so the workflow strips the prefix into GORELEASER_CURRENT_TAG
and runs with --skip=validate. The bare semver tag does not physically exist at the release
commit, only the prefixed one does.
What a human publishes
Four channels have no workflow. Each needs a command run by a maintainer.
| Channel | Package | How |
|---|---|---|
| npm | queen-mq |
npm publish from clients/client-js |
| PyPI | queen-mq |
clients/client-py/publish.sh, which builds the wheel and sdist and uploads with twine |
| Go modules | github.com/smartpricing/queen/clients/client-go |
push a clients/client-go/vX.Y.Z tag; the module proxy picks it up |
| Packagist | smartpricing/queen-mq |
push a vX.Y.Z tag; a webhook makes Packagist pull it |
The C++ client needs nothing published: consumers take clients/client-cpp/queen_client.hpp at
the tag.
Two tag shapes are load-bearing and easy to get wrong. Both nested Go modules need the
directory-prefixed form (clients/client-go/v1.0.0 and clients/client-cli/v1.0.0) because
that is how Go’s module proxy resolves a module that is not at the repository root. Without the
prefix, go get reports no matching versions. The tags already in the repository follow both
shapes, alongside the plain vX.Y.Z broker tags.
Order
The broker tag and image go first, because every SDK is released against a broker; then npm,
because most downstream consumers wait on it; then PyPI; then the Go module tag; then the plain
vX.Y.Z tag that Packagist and the C++ header hang off; then the CLI tag, which triggers its own
workflow.
Rollback, and the one place additivity stops
Schema application is idempotent by construction. server/src/schema.rs applies schema.sql
and all 34 stored-procedure files at every boot, in lexical order, under a session advisory lock,
and every statement is CREATE OR REPLACE, IF NOT EXISTS or ADD COLUMN IF NOT EXISTS. So
re-applying is always safe, and rolling the broker image back to a previous 1.x is safe at the
database level.
There is one hard exception, and it is the boundary into 1.0.
server/sql/procedures/040_log_drop_legacy.sql runs before the log-engine schema and
drops the retired seg_* engine: every queen.seg_* function, discovered dynamically over
pg_proc so no straggler survives, then the legacy tables, then the seg-era columns folded into
queen.partition_consumers. It is written to be a no-op on a database that never had them and a
full teardown on one that did.
What the older release note gets wrong
developer/14-release.md is the pre-1.0 checklist and is still in the tree. Read it for the
publishing mechanics of npm, PyPI and Packagist, which are unchanged. Everything below it says
no longer holds.
| It says | Actually |
|---|---|
the version is 0.14.1.beta.1 in server/server.json |
it is 1.0.0-beta.1, shipping as 1.0.0 |
| the release covers “the C++ broker binary, libqueen, and five client SDKs” | there is no C++ broker and no lib/. The image carries the Rust broker, the dashboard and queenctl |
the pre-release checklist runs cd lib && make test-contention |
that directory does not exist. The gate is test/run.sh. See Testing |
user-facing docs live in docs/*.html, and a version bump has to edit docs/index.html and docs/quickstart.html |
that site is retired. Documentation is webdoc/, and it is built and gated by its own workflow |
| the version lives “in nine places” including those HTML files | the live places are server/server.json, proxy/Cargo.toml, app/package.json, clients/client-js/package.json, clients/client-py/pyproject.toml, README.md, and the image tag shown in these docs |
a rolled-back procedure can be re-applied from lib/schema/procedures/ |
procedures live in server/sql/procedures/ and are embedded in the binary, so the binary and the procedures roll together: you cannot roll one back independently |
helm/, helm_queen/ and proxy/helm/ are release artifacts to update |
those directories are gitignored and untracked. Nothing in CI reads them and they are not shipped |
| the schema is “always additive between versions”, so a rollback is always safe | true within 1.x, false across the 0.16-to-1.0 boundary. See the warning above |
One workflow is dead in the same way. .github/workflows/cpp-server-build.yml triggers on the
release branch and runs make deps and make build-only in server/, targets that were
removed with the C++ broker; it cannot succeed. And .github/workflows/cli.yml pins its
end-to-end broker to smartnessai/queen-mq:0.14.3, so the CLI’s end-to-end suite currently
tests against a pre-log-engine broker.
Post-release
- Smoke-test the published packages from a clean directory rather than a local link: install
queen-mqfrom npm and from PyPI, andgo getthe client module at the new version. - Write release notes covering schema changes, new environment variables and anything removed. A removed route or environment variable is a major-version change.
- Re-check that the published image tag in these docs matches what was pushed. It appears in Start here and Self-hosting.