Releasing
Esta página aún no está disponible en tu idioma.
A release ships the local client (chronos + chronosd) and the desktop app. The
pipeline lives in .github/workflows/release.yml; this page is the operator’s checklist.
Scope of v1: macOS only (arm64 + x86_64). The Windows build compiles in CI, but the
sandbox has no Windows isolation yet, so shipping a Windows client would sell isolation
that isn’t there. The enterprise panel (cloud/) deploys separately (Railway) and is not
part of this artifact.
Cutting a release
Section titled “Cutting a release”-
Make sure
mainis green (just ci) and the workspace crate version is the one you want to ship (crates/chronos-cli/Cargo.toml) — the workflow refuses a tag that does not match it. -
Update the changelog (see below): draft the section, curate it, and commit the change so it ships in the tarball’s user guide.
-
Tag and push:
Terminal window git tag v0.1.0git push origin v0.1.0 -
The
releaseworkflow builds both macOS targets and packages, per target:chronos-<version>-<target>.tar.gz— CLI + daemon binaries + the built user guide (staged atshare/chronos/docs, served by the dashboard at/docs);Chronos-<version>-<target>.dmg— the desktop app, withchronosdbundled inside as a sidecar (drag to Applications; unsigned builds need right-click → Open the first time).
It collates
SHA256SUMSand creates a draft GitHub release with generated notes. -
Review the draft (assets, checksums, notes) and publish it manually. This is the human-facing release — release notes and the
.dmgfor a first install. The in-app update rollout is separate (see below): it goes through the panel’s channel router, not GitHub.
A workflow_dispatch run of the same workflow is a dry run: it builds and uploads the
artifacts to the run (with a <version>-dev.<sha> version), but creates no release.
Changelog
Section titled “Changelog”The customer-facing changelog is curated, not auto-generated. To draft the next version’s section:
just changelog # since the last tagjust changelog v0.1.0 # an explicit base refThat prints a Markdown block, bucketed into Novedades / Correcciones / Seguridad /
Rendimiento from the range’s Conventional Commits. It is a draft: de-jargon each line,
drop what customers don’t need, remove the (#NN) / (CHR-NN) references, and paste the
result into apps/docs/src/content/docs/changelog.md. The script never writes the page and
never touches git — the propose→curate→publish gate is a person, like everywhere else in
the product. The committed changelog reaches clients two ways: the public docs site at
docs.getchronos.dev/changelog, and the copy
bundled into the release tarball for offline use inside the app.
Publishing the docs site (docs.getchronos.dev)
Section titled “Publishing the docs site (docs.getchronos.dev)”The site lives at docs.getchronos.dev, served from the
Railway service chronos-docs (project chronos-panel). It is the same Astro Starlight
source as the offline build; only the base path differs, set by env at build time:
- Public —
DOCS_BASE=/DOCS_SITE=https://docs.getchronos.dev(theapps/docs/Dockerfilebakes these in). Author-written links are root-absolute (/guide/merge/); a small rehype plugin inastro.config.mjsre-adds the base so the same content also works under/docs. - Offline — the default
DOCS_BASE=/docs, whichjust docs-buildproduces for the dashboard and the release tarball.
The service is wired to GitHub (branch main, root apps/docs, watch apps/docs/**), so
merging a docs change to main auto-deploys. Manual out-of-band redeploy, from apps/docs:
railway up # linked to the chronos-docs serviceTLS terminates at Cloudflare (orange-cloud proxy on the getchronos.dev zone); the
docs record is a proxied CNAME onto the service’s Railway domain, with the custom domain
registered on the service so Railway routes the host. Full runbook: docs/docs-site.md.
Shipping an in-app update (channels)
Section titled “Shipping an in-app update (channels)”Installed desktop clients update themselves from the enterprise panel’s channel router; the signed bundles live in a private Railway bucket. Releasing a version and rolling it out are two steps: registering makes a build downloadable, promoting a channel is what actually offers it to clients.
Channels are stable (default), internal and alpha (plus beta, promotable but
not yet a Settings option). They are independent pointers — alpha can run ahead of
stable off the same signed bundles.
1. Tag the version
Section titled “1. Tag the version”just version 0.2.0git commit -am "chore: 0.2.0"git tag v0.2.0 # stable: registered, NOT auto-promotedgit push origin v0.2.0To roll out to a non-stable channel automatically, tag a prerelease whose identifier is the channel name:
git tag v0.2.0-internal.1 # → auto-promoted to the `internal` channelgit tag v0.2.0-alpha.3 # → auto-promoted to `alpha`git push origin --tagsCI builds from the tagged commit, so the version’s code (including the pinned updater
public key) must be on main first — merge before tagging.
2. What CI does on the tag
Section titled “2. What CI does on the tag”release.yml:
- builds and minisign-signs the
.app.tar.gzper target, - uploads each to the release bucket under
releases/<version>/…(path-style), - registers the version with the panel (
POST /v1/admin/releases), - for a prerelease, auto-promotes it to its channel (
-internal.N→internal, …).
It also drafts the GitHub release with the .dmg for humans / first installs.
3. Roll it out (stable, or to override)
Section titled “3. Roll it out (stable, or to override)”A plain (stable) tag is registered but not promoted — rolling stable out is deliberate. In the panel → Releases tab, promote the version to a channel; that click is the rollout gate. To pause a bad release, promote the channel back to the previous version (clients only ever move forward, so there is no forced downgrade).
4. What a tester sees
Section titled “4. What a tester sees”A client on the target channel is offered the update a few seconds after launch (and every
6 h): a card bottom-right with Update and restart. To put a tester on a non-stable
channel: install the app once (hand them the .dmg), then Settings → Version → Update
channel → Internal / Alpha. Their first install must be a build that carries the pinned
public key (i.e. from this code onward), or its updater is fail-closed.
The channel is a client-local choice today; governing it per org/team from the panel is tracked in CHR-49.
Signing and notarization (optional)
Section titled “Signing and notarization (optional)”There are two independent signatures, don’t conflate them: the minisign signature (above) is what the in-app updater verifies — required for updates, already configured. The Apple Developer ID signing + notarization here is what clears Gatekeeper on first install — optional, and tracked in CHR-48.
The build signs and notarizes only when the repo secrets are present; otherwise the
tarball carries an UNSIGNED.txt marker and the installer prints the Gatekeeper
workaround. Recommended before external users. Required secrets:
| Secret | Content |
|---|---|
MACOS_CERTIFICATE | base64 of the Developer ID Application .p12 |
MACOS_CERTIFICATE_PWD | password of that .p12 |
MACOS_SIGNING_IDENTITY | e.g. Developer ID Application: ACME (TEAMID) |
APPLE_API_KEY | App Store Connect API key id (e.g. ABCD1234) |
APPLE_API_ISSUER | the API key’s issuer id (a UUID) |
APPLE_API_KEY_P8 | base64 of the .p8 private key (downloaded once) |
Notarization uses an App Store Connect API key rather than an Apple ID + app-specific password: the key is owned by the organization, carries a scoped role, and is revocable on its own — so releases don’t break when a person’s Apple account changes. Create it in App Store Connect → Users and Access → Integrations → App Store Connect API.
When present:
- The CLI and daemon binaries (for the
tar.gz) are codesigned (hardened runtime + timestamp) and submitted tonotarytoolwith the API key. - The desktop app is fully hardened: the bundled
chronosdsidecar is signed first, then Tauri signs the.appwith the hardened runtime +Entitlements.plist, notarizes it and staples the ticket (so Gatekeeper clears it offline, no right-click needed), and packages the stapled app into the.dmg. This is driven by handingcargo tauri buildtheAPPLE_SIGNING_IDENTITY+APPLE_API_KEY/APPLE_API_ISSUER/APPLE_API_KEY_PATHcredentials.
Without the secrets the .app/.dmg are unsigned and the first launch needs the
Gatekeeper workaround (right-click → Open, or xattr -dr com.apple.quarantine).
Local dry run
Section titled “Local dry run”just package reproduces the CI packaging for the host target: it builds the release
binaries and the user guide, stages the same tarball layout under target/dist/, and
writes the tarball plus SHA256SUMS. Always unsigned — signing/notarization only happens
in CI with secrets. If the docs toolchain is not installed, it packages without the user
guide and says so.
Installing
Section titled “Installing”End users (or pilot admins) run:
curl -fsSL https://raw.githubusercontent.com/renaiss-ai/aluxion-chronos/main/scripts/install.sh | shWhile the repo is private the script needs an authenticated channel: it prefers a
logged-in gh CLI and falls back to GITHUB_TOKEN. It verifies the SHA-256 checksum,
installs to ~/.local/bin, and places the user guide in the daemon’s data dir so the
dashboard serves it at /docs.