A release touches the broker image, six client SDKs and the CLI, across seven package registries plus GitHub Releases. Some channels are published by CI from a verified tag; others require a maintainer upload. 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.shreadsversionwithjqto tag the broker image it builds or pushes locally..github/workflows/docker-build.ymlreadsversionwithjqfor the same purpose in CI.
Neither reads name: the image name is queen in both, and both take the proxy image’s
version from proxy/Cargo.toml instead.
Everything else carries its own version, or none at all.
| Artifact | Where its version lives |
|---|---|
| Broker image | server/server.json |
queen-engine (the broker crate) |
server/Cargo.toml, which must say the same string as server/server.json: the first is what crates.io serves, the second is what the built binary reports on /health |
| JavaScript client | clients/client-js/package.json |
| Python client | clients/client-py/pyproject.toml and clients/client-py/queen/__init__.py, whose __version__ the package exports: bumping only the first ships a package that misreports itself |
| PHP/Laravel client | nowhere: composer.json has no version field; the split repository receives a bare vX.Y.Z tag from clients/client-php/vX.Y.Z |
| Go client | nowhere: the git tag is the version |
| Rust client | clients/client-rust/Cargo.toml |
queen-protocol |
crates/queen-protocol/Cargo.toml, and the version requirement on EVERY dependency on it: both clients/client-rust/Cargo.toml and server/Cargo.toml pin it by version beside the path |
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
Each published component carries its own release version. Coordinated product releases through
1.3 kept the broker, proxy and SDK minor lines aligned; from 1.4 onward, broker/proxy releases may
advance without republishing SDKs when the stable /api/v1 contract remains compatible. Never
infer the installed SDK version from the broker tag: read the manifest or component tag in the
table above. A patch that rebuilds only one component moves only that component.
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.
Docker Hub is an opt-in mirror of the same build, not a second build: with
vars.DOCKERHUB_NAMESPACE plus the DOCKER_USERNAME and DOCKER_PASSWORD secrets configured,
buildx pushes the one manifest to docker.io under the same three tags, so the images are
bit-identical across the two registries. Unset, the job publishes to GHCR alone, and a pull
request never mirrors because secrets do not reach forked pull requests.
Authentication for GHCR is the workflow’s own GITHUB_TOKEN under
permissions: packages: write, so that registry needs no secrets to rotate; the two Docker
Hub secrets exist only if the mirror does. 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, which drops the requirement that the bare tag exist at the
checkout. In practice it does exist: a release tags the broker and the CLI together, and
GoReleaser publishes its GitHub Release against the bare tag, so the queenctl X.Y.Z release
lands on the vX.Y.Z tag. The broker’s release notes for that tag and the CLI artifacts
therefore share one GitHub Release: edit the release that exists rather than creating a second
one on the same tag.
The PHP client mirror
queen-mq/queen is the only source of truth. The queen-mq/php-client repository is a
read-only clients/client-php subtree, created from master and tagged only by
.github/workflows/release-php-client.yml. A source tag such as
clients/client-php/v1.2.3 is verified on PHP 8.3 and 8.4, exercises the Redis
WithoutOverlapping integration, and checks that the matching native supervisor release exists
before it can create v1.2.3 in the mirror. Packagist reads that bare mirror tag.
Before enabling the mirror, install an organization-owned GitHub App on only
queen-mq/php-client with repository permission Contents: Read and write. Store its App ID in
the PHP_CLIENT_MIRROR_APP_ID Actions variable and its private key in the
PHP_CLIENT_MIRROR_APP_PRIVATE_KEY Actions secret of queen-mq/queen. The workflow asks the App
for a short-lived token scoped to that one repository; it does not use a personal access token or
a deploy key. Protect clients/client-php/v* in the source and v* in the mirror from
updates and deletion. The first Packagist submission is manual; enable the Packagist GitHub hook
on the mirror afterwards.
What a maintainer publishes
The package registries below either need a direct upload or a source tag created by a maintainer. The PHP tag is then verified and mirrored by CI; no one commits to its split repository.
| 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 | queen-mq/php-client |
push clients/client-php/vX.Y.Z; the verified release workflow mirrors it as vX.Y.Z, then the Packagist webhook indexes it |
| crates.io | queen-protocol, then queen-mq and queen-engine |
cargo publish from crates/queen-protocol, then from clients/client-rust and from server/ (the broker package; its library is the embedded engine) |
The C++ client needs nothing published: consumers take clients/client-cpp/queen_client.hpp at
the tag.
Three 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. The PHP
client likewise uses clients/client-php/v1.0.0; the release workflow maps it to the bare
v1.0.0 tag required by Packagist in queen-mq/php-client. Without the Go prefix, go get
reports no matching versions.
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 three crates, protocol before
client and engine; then the Go module tag. Publish the supervisor release before creating the
PHP source tag: the PHP release workflow verifies the pinned supervisor archives before it
mirrors clients/client-php/vX.Y.Z as Packagist vX.Y.Z. Finally create 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 re-applies schema.sql
and the 33 stored-procedure files at every boot (the schema catalogue has
the mechanism), so re-applying is always safe, and rolling the broker image back to a previous
1.x is safe at the database level. Note that the procedures are embedded in the binary, so the
binary and the procedures roll together: you cannot roll one back independently.
The boundary into 1.0 is the exception, and it is a wall rather than a teardown. The boot apply only creates: the teardown and migration files that once dropped the retired engines were themselves deleted from the tree (see Retired objects), so nothing removes, converts or migrates an older database’s objects at boot.
Stale release material
The pre-1.0 release checklist, developer/14-release.md, is stale like the rest of developer/;
for what each release actually changed, read CHANGELOG.md at the repository root.
The CI that was stale the same way is gone: cpp-server-build.yml (C++ make targets that no
longer existed) and cli.yml (an end-to-end suite pinned to a pre-log-engine 0.14.3 broker
image) have both been deleted. The active release workflows are docker-build.yml,
release-cli.yml, release-supervisor.yml and release-php-client.yml; the CLI’s end-to-end
suite runs in the cli cell of the tests matrix, against the broker built from the same tree.
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.