13 · Release Engineering Playbook — Stella Ops
A concise, automation‑first guide describing how source code on main becomes a verifiably signed, air‑gap‑friendly release. It is opinionated for offline use‑cases and supply‑chain security (SLSA ≥ level 2 today, aiming for level 3).
0 Release Philosophy
- Fast but fearless – every commit on
mainmust be releasable; broken builds break the build, not the team. - Reproducible – anyone can rebuild byte‑identical artefacts with a single
make releaseoffline. - Secure by default – every artefact ships with a SBOM, Cosign signature and (future) Rekor log entry.
- Offline‑first – all dependencies are vendored or mirrored into the internal registry; no Internet required at runtime.
- Non-Kubernetes deployment - Stella Ops does not ship or support Kubernetes manifests or Helm charts. Release packaging targets Docker Compose, Offline Kit, and signed release manifests only.
1 Versioning & Branching
| Branch | Purpose | Auto‑publish? |
|---|---|---|
main | Always‑green development trunk | nightly-* images |
release/X.Y | Stabilise a minor line | stella:X.Y-rcN |
| Tags | X.Y.Z = SemVer | stella:X.Y.Z, OUK tarball, release manifest |
- SemVer – MAJOR for breaking API/CLI changes, MINOR for features, PATCH for fixes.
- Release tags are signed (
git tag -s) with the Stella Ops GPG key (0x90C4…).
2 CI/CD Overview (GitLab CI + GitLab Runner)
graph LR A[push / MR] --> Lint Lint --> Unit Unit --> Build Build --> Test-Container Test-Container --> SBOM SBOM --> Sign Sign --> Publish Publish --> E2E Publish --> Notify
Pipeline Stages
| Stage | Key tasks |
|---|---|
| Lint | ESLint, golangci‑lint, hadolint, markdown‑lint. |
| Unit | dotnet test, go test, Jest UI tests. |
| Quota unit‑tests 🏷 | Validate QuotaService logic: reset at UTC, 5 s vs 60 s waits, header correctness. |
| Build | Multi‑arch container build (linux/amd64, linux/arm64) using BuildKit + --provenance 📌. |
| Test‑Container | Spin up compose file, run smoke APIs. |
| SBOM 📌 | Invoke StellaOps.SBOMBuilder to generate SPDX JSON + attach .sbom label to image. |
| Sign | Sign image with Cosign (cosign sign --key cosign.key). |
| Publish | Push to registry.git.stella-ops.org. |
| E2E | Compose/offline-kit smoke tests; verify sub-5 s scan SLA where scanner packs are enabled. |
| Notify | Report to Mattermost & GitLab Slack app. |
| OfflineToken | Call JwtIssuer.Generate(exp=30d) → store client.jwt artefact → attach to OUK build context |
All stages run in parallel where possible; max wall‑time < 15 min.
Implementation note. .gitea/workflows/release.yml executes ops/devops/release/build_release.py to build multi-arch images, attach CycloneDX SBOMs and SLSA provenance with Cosign, and emit out/release/release.yaml for downstream packaging (Compose and Offline Kit). The build-test-deploy workflow also runs python ops/devops/release/test_verify_release.py so release verifier regressions fail fast during every CI pass.
After build/publish produces final container digests, release engineering must materialize stable and air-gap release-manifest authority from a local artifact instead of resolving tags. First produce that artifact from local build outputs:
python devops/release/produce_image_digests.py \
--channel-source stable=out/release/stable/artifacts/metadata \
--channel-source airgap=out/release/airgap/artifacts/metadata \
--manifest stable=devops/releases/2025.09-stable.yaml \
--manifest airgap=devops/releases/2025.09-airgap.yaml \
--infrastructure-source out/release/infrastructure-digests.json \
--output out/release/image-digests.json
Component digests must come from Docker Buildx --metadata-file output written by successful publish jobs. Already digest-pinned PostgreSQL and Valkey entries can be carried from the local release manifests. RustFS and any other tag-only infrastructure image must be supplied by a local infrastructure source captured from its real mirror/build result. The output artifact is JSON with manifests.<channel>.components and manifests.<channel>.infrastructure mappings to final image@sha256:<digest> references. Apply and validate it offline:
python devops/release/apply_image_digests.py \
--artifact out/release/image-digests.json \
--manifest devops/releases/2025.09-stable.yaml \
--manifest devops/releases/2025.09-airgap.yaml
python devops/release/check_release_manifest.py \
--manifest devops/releases/2025.09-stable.yaml \
--manifest devops/releases/2025.09-airgap.yaml
The patcher refuses tag-only values, tag-plus-digest values, missing required components, repository/name mismatches, and partial stable/air-gap updates before writing either manifest.
This tooling is retained as release-manifest authority for non-Kubernetes packaging; it is not Helm support.
Release-guide TODOs.
- TODO(release-digests): update the required component/infrastructure digest list when release payload composition changes.
- TODO(release-schema): update the release-manifest schema notes when bundle metadata or evidence-pack schemas change.
- TODO(model): current setup guidance pins
microsoft/bitnet-b1.58-2B-4Tas the default small ternary advisory model. Update this release guide and model-selection docs before changing the default to a custom/larger Stella Ops model. - TODO(provider): update provider-pack guidance when crypto/evidence provider packs are selected and legally cleared.
- TODO(dora-taxonomies): re-pin DORA taxonomy/schema package versions during every release branch hardening window. Do not carry the previous release’s DORA taxonomy pins forward without a fresh source/version/hash check and offline validation evidence.
For local tests only, developers may generate a simulated digest artifact:
python devops/release/produce_image_digests.py \
--simulate-buildx-fixture-for-tests \
--manifest stable=devops/releases/2025.09-stable.yaml \
--manifest airgap=devops/releases/2025.09-airgap.yaml \
--output out/release/image-digests.test-only.json
That artifact carries testOnly: true and sourceKind: simulated-buildx-fixture. Production apply and validation reject it by default. The patcher accepts it only with --allow-test-only-artifact-write-for-tests, stamps output manifests as test-only, and those manifests still fail check_release_manifest.py and runtime posture validation unless the corresponding --allow-...-for-tests flags are used. Simulated digests are never stable or air-gap release authority.
Compliance Provider And Schema Asset Intake
External compliance assets are release inputs only after approved offline intake. Release engineering must not download provider packs, taxonomies, XSDs, or trust lists during a release build.
Current pinned-but-not-ingested assets:
| Area | Selected pin | Release note |
|---|---|---|
| eIDAS BaselineT/LT/LTA | selected QTSP/QSCD or compliance-owned sealed provider pack | Future provider-pack ingestion must update the crypto runbook, eIDAS bridge contract, release notes, and evidence-pack manifest with pack id, manifest SHA-256, expiry, storage location, and legal/license disposition. |
| DORA major-incident reports | eba-dora-incident-reporting-framework-4.3 | Future taxonomy ingestion must update the DORA incident contract, release notes, and offline schema-validation evidence. |
| DORA register of information | eba-dora-roi-reporting-framework-4.0-taxo-package-4.0-errata5 | Future taxonomy ingestion must update the RoI contract, CLI export evidence, release notes, and offline schema-validation evidence. |
Any ingested external asset must have a recorded source, immutable local path, manifest hash, license/notice review, and offline validation fixture. If the asset cannot be redistributed, the release must point to the internal sealed asset location and describe how customer deployments provide it without network fetches.
DORA pins are release-scoped. During release hardening, release engineering must verify whether the EBA DORA incident-reporting and RoI taxonomy packages have changed, record the selected version and source hash in the release evidence pack, and rerun offline schema-validation fixtures. A release must not publish DORA conformance claims from stale taxonomy pins that were not rechecked for that release.
Packaging support note: Kubernetes and Helm artifacts are not part of the current supported Stella Ops release model. Legacy Helm/Kubernetes references in release documentation are not release authority and must be cleaned up before publishing the next operator-facing release guide.
3 Container Image Strategy
| Image | Registry Tag | Contents |
|---|---|---|
| backend | stella/backend:{ver} | ASP.NET API, plugin loader. |
| ui | stella/ui:{ver} | Pre‑built Angular SPA. |
| runner-trivy | stella/runner-trivy:{ver} | Trivy CLI + SPDX/CycloneDX 🛠. |
| runner-grype | stella/runner-grype:{ver} | Optional plug‑in scanner. |
| 🏷️ StellaOps.Registry 📌 | stella/registry:{ver} | Scratch image embedding Docker Registry v2 + Cosign policy controller. |
| 🏷️ StellaOps.MutePolicies 📌 | stella/policies:{ver} | Sidecar serving policy bundles. |
| 🏷️ StellaOps.Attestor 📌 | stella/attestor:{ver} | SLSA provenance & Rekor signer (future). |
Images are --label org.opencontainers.image.source=git.stella-ops.ruand include SBOMs generated at build time.
4 📌 Offline Update Kit (OUK) Build & Distribution
Purpose – deliver updated CVE feeds & Trivy DB to air‑gapped clusters.
4.1 CLI Tool
Go binary ouk lives in src/Tools/ouk/.
ouk fetch \
--nvd --osv \
--trivy-db --date $(date -I) \
--output ouk-$(date +%Y%m%d).tar.gz \
--sign cosign.key
4.2 Pipeline Hook
- Runs on first Friday each month (cron).
- Generates tarball, signs it, uploads to GitLab Release asset.
- SHA‑256 + signature published alongside.
- Release job must emit
out/release/debug/withdebug-manifest.jsonand.sha256soops/offline-kit/mirror_debug_store.pycan mirror symbols into the Offline Kit (seeDEVOPS-REL-17-004).
4.3 Activation Flow (runtime)
- Admin uploads
.tar.gzvia UI → Settings → Offline Updates (OUK). - Backend verifies Cosign signature & digest.
- Files extracted into
var/lib/stella/db. - Valkey caches invalidated; Dashboard “Feed Age” ticks green.
- Audit event
ouk_updatestored.
4.4 Token Detail
client.jwt placed under /root/ inside the tarball. CI job fails if token expiry < 29 days (guard against stale caches).
5 Artifact Signing & Transparency
| Artefact | Signer | Tool/Notes |
|---|---|---|
| Git tags | GPG (0x90C4…) | git tag -s |
| Containers | Cosign key pair | cosign sign |
| OUK tarballs | Cosign | cosign sign-blob |
| Debug store | — | debug/debug-manifest.json hashed |
Rekor integration is TODO – once the internal Rekor mirror is online (StellaOpsAttestor) a post‑publish job will submit transparency log entries.
6 Release Checklist
- CI pipeline green.
- Bump
VERSIONfile. - Tag
git tag -s X.Y.Z -m "Release X.Y.Z"& push. - GitLab CI auto-publishes images, release manifests, and offline bundles.
- Draft GitLab Release Notes using
src/Tools/release-notes-gen. - Verify SBOM attachment with
stella sbom verify stella/backend:X.Y.Z. - Run the release verifier locally if CI isn’t available (mirrors the workflow step):
python ops/devops/release/test_verify_release.py - Verify reproducibility – rebuild and compare checksums:
export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD) make release sha256sum dist/* | diff - out/release/SHA256SUMS - Generate Release Evidence Pack – trigger evidence pack workflow:
gh workflow run release-evidence-pack.yml \ -f version=X.Y.Z \ -f release_tag=vX.Y.Z - Self-verify evidence pack – extract and run verify.sh:
tar -xzf stella-release-X.Y.Z-evidence-pack.tgz cd stella-release-X.Y.Z-evidence-pack ./verify.sh --verbose - Mirror the release debug store into the Offline Kit staging tree and re-check the manifest:
./ops/offline-kit/mirror_debug_store.py \
--release-dir out/release \
--offline-kit-dir out/offline-kit
jq '.artifacts | length' out/offline-kit/debug/debug-manifest.json
readelf -n /app/... | grep -i 'Build ID'
Validate that the hash from readelf matches the .build-id/<aa>/<rest>.debug path created by the script. 12. Smoke-test OUK tarball in offline lab. 13. Announce in #stella-release Mattermost channel.
7 Hot‑fix Procedure
- Branch from latest tag →
hotfix/X.Y.Z+1-hf1. - Apply minimal patch, add regression test.
- CI pipeline (with reduced stages) must pass.
- Tag
X.Y.Z+1. - Publish only containers and the signed release manifest/offline bundle entries required by the fix; OUK not rebuilt unless feed content changed.
- Cherry‑pick back to
main.
8 Deprecation & End‑of‑Life Policy
| Feature | Deprecation notice | Removal earliest |
|---|---|---|
| Legacy CSV policy import | 2025‑10‑01 | 2026‑04‑01 |
| Docker v1 Registry auth | 2025‑12‑01 | 2026‑06‑01 |
| In‑image Trivy DB | 2025‑12‑15 | 2026‑03‑15 |
At least 6 months notice; removal requires major version bump.
9 📌 Non‑Commercial Usage Rules (English canonical)
- Free for internal security assessments (company or personal).
- SaaS resale / re-hosting prohibited without prior written consent (policy requirement; not a license restriction).
- If you distribute a fork with UI or backend modifications you must:
- Include the LICENSE and NOTICE files.
- Mark modified files with prominent change notices.
- Retain the original Stella Ops attribution in UI footer and CLI
--version.
- All third‑party dependencies remain under their respective licences (MIT, Apache‑2.0, ISC, BSD).
- Deployments in state‑regulated or classified environments must obeyapplicable local regulations governing cryptography and software distribution.
10 Best Practices Snapshot 📌
- SBOM‑per‑image → attach at build time; store as OCI artifact for supply‑chain introspection.
- Provenance flag (
--provenance=true) in BuildKit fulfils SLSA 2 requirement. - Use multi‑arch, reproducible builds (
SOURCE_DATE_EPOCHpins timestamps). - All pipelines enforce Signed‑off‑by (DCO); CI fails if trailer missing.
cosign policyensures only images signed by the project key run in production.
11 Contributing to Release Engineering
- Fork & create MR to
infra/release-*. - All infra changes require green
integration-e2e-offlinejob. - Discuss larger infra migrations in
#sig-releaseMattermost; decisions recorded inADR/folder.
12 Change Log (high‑level)
| Version | Date | Note |
|---|---|---|
| v2.1 | 2025‑07‑15 | Added OUK build/publish pipeline, internal registry image (StellaOps.Registry), non‑commercial usage rules extraction, SBOM stage, BuildKit provenance. |
| v2.0 | 2025‑07‑12 | Initial open‑sourcing of Release Engineering guide. |
| v1.1 | 2025‑07‑09 | Fixed inner fencing; added retention policy |
| v1.0 | 2025‑07‑09 | Initial playbook |
(End of Release Engineering Playbook v1.1)
