Release and packaging
How JACO releases are built, signed, and published. The pipeline runs
in .github/workflows/release.yml;
the packaging recipe is nfpm.yaml; the local
preview path is make package / make release.
Artifact set per release
For each tag vX.Y.Z, the pipeline produces, per linux/<arch> in
{amd64, arm64}:
jaco_<X.Y.Z>_<arch>.debjaco-<X.Y.Z>-1.<arch>.rpmjaco_<X.Y.Z>_<arch>.apkjaco-v<X.Y.Z>-linux-<arch>.tar.gz
Plus a single SHA256SUMS listing every artifact, and (once signing
is wired) SHA256SUMS.minisig.
The current pipeline runs unsigned; the verification surface in
jaco self-upgrade already requires the minisig, so signing is the
gating step before self-upgrade is operationally complete. See
"Verification key rotation" below.
What's inside each artifact
.deb / .rpm / .apk
nfpm-built from nfpm.yaml. Lays down:
/usr/local/bin/jacoand/usr/local/bin/jacod(mode 0755)./lib/systemd/system/jaco.service./etc/jaco/jacod.yaml(type: config|noreplace— operator edits survive upgrade).
Postinstall, preremove, and postremove hooks under
build/packaging/ handle daemon-reload and clean removal. Per-format
dependencies:
.deb→docker.io | docker-ce | docker-engine.rpm→/usr/bin/docker.apk→docker
Alpine uses OpenRC; the systemd unit ships in the package but the
postinstall reload is gated on systemctl presence so the install
succeeds on musl systems even though the unit sits unused.
Generic tarball
Built by the release workflow (linux/<arch>) and by the local
build/release.sh (linux + darwin, both archs). Contains:
jaco,jacod(static,CGO_ENABLED=0,-trimpath -ldflags "-s -w -X main.version=<tag>").jaco.service,jacod.yaml,LICENSE,README.md,INSTALL.md.
Local preview
# build .deb/.rpm/.apk locally — defaults to amd64, version from
# `git describe`. Override:
make package
make package PACKAGE_ARCH=arm64 PACKAGE_VERSION=0.1.0
# cross-build linux + darwin × amd64 + arm64 tarballs into dist/.
# Set MINISIGN_KEY=... to also sign dist/SHA256SUMS.
make releasemake package requires nfpm on PATH (install with
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.46.3).
Cutting a release
- Bump version where needed in code (none today — version is baked
in via
-ldflags "-X main.version=<tag>"). - Update
CHANGELOG.md(if/when one exists). - Confirm
make ci-test vet lintis clean. - Tag and push:
git tag v0.2.0 && git push --tags. - The
releaseworkflow triggers on tags matchingv*. It builds binaries, packages, and the tarball per arch; assemblesSHA256SUMS; uploads everything to a draft GitHub release for the tag. - Once signing is wired, the workflow also produces
SHA256SUMS.minisig. - Publish the draft.
Signing (planned)
jaco self-upgrade verifies releases by:
- minisign signature over
SHA256SUMSagainst the embedded public key (internal/packaging/release-pubkey.txt). - SHA-256 of the tarball against the corresponding line in
SHA256SUMS.
Both verifications must pass before either binary is touched on disk.
Any mismatch surfaces as Error{code: upgrade_verification_failed}
and the upgrade aborts.
The release workflow currently produces artifacts unsigned; a
GPG-signing job is included commented-out, awaiting the project's
minisign signing key being uploaded to the MINISIGN_SIGNING_KEY
secret. Until signing is live, jaco self-upgrade will refuse to
proceed against the unsigned releases — provision new clusters from
the packages or generic tarball, not via self-upgrade.
Key rotation
If the signing key needs to rotate (compromise, schedule):
- Generate a new minisign keypair offline.
- Publish a new JACO release whose
internal/packaging/release-pubkey.txtis the new key, signed with the old key (operators are running old code that verifies against the old key). - Operators upgrade to that release; their daemons now verify future releases with the new key.
- The next release after that is signed with the new key.
What goes where between versions
- gRPC field additions are backward-compatible. Operators may run an N+1 CLI against an N daemon within the same major version.
- Raft FSM apply must remain compatible across adjacent versions. New
command variants land additively under the
Command{}proto'soneof. jacod.yamlschema is closed — adding a key requires a loader update plus a defaulting rule so existing configs continue to parse.- Audit-event types are append-only on
AuditEventType. Renames break backward compatibility for audit consumers.