Release Editions and the Declared Labels
An edition is a technology or support target the platform ships to: the standard Linux
edition (AKS and any Kubernetes host), the Mac edition (Apple-silicon hosts such as a Mac
Studio or a Mac mini running Colima k3s) and the enterprise edition (installations that
follow official releases only). A channel label is the moving image tag that names the build
an edition currently offers, for example 3-latest, 3-mac or 3-enterprise.
Release Channels says how proven a build is (latest, stable). An edition
says what the build targets. Both are spelled as moving image labels, and both are declared in the
same file.
A channel label is a tag, not a version. Versions stay plain SemVer and never carry a channel word
(policy platform-semver-versioning). A deployment seeds from a label and then runs a resolved
version: a pointer is a seed, never a running pin
(ImageLine).
Declared in one place
Every moving label lives in
.github/release-channels.json
(policy declared-release-channels). Its only reader is .github/scripts/release-channels.py:
| Command | Who calls it |
|---|---|
release-channels.py protected-patterns |
a pruner: the JSON list of label patterns it must exclude by name |
release-channels.py is-protected <tag> |
a shell step: exit 0 means protected, 1 means not a declared label, and 2 means the lookup failed. A caller must abort on 2 and never read it as unprotected |
release-channels.py tags --version <v> --writer <workflow> [--job <job>] [--repository <r>] |
a writer: the written labels that binding moves for that set |
release-channels.py check . |
every pull request (CI's own shell) |
release-channels.py --self-test |
every pull request: shows that each check can fail |
The labels
| Label | Edition | Meaning | Writer | Status |
|---|---|---|---|---|
{major}-latest |
standard | newest armed set of the major, multi-arch | main-cd.yml arm Phase D, control-first |
written |
{major}.{minor}-latest |
standard | newest armed set of one minor line | same | written |
{major}.{minor}.{patch}-latest |
standard | legacy notation's patch line (pre-SemVer sets only) | same | written |
{major}.{minor}-stable |
standard | newest set of a minor line that earned trust (Release Channels) | the stable promoter (owed) | declared |
{major}-mac |
mac | newest armed set whose linux/arm64 variant passed Mac acceptance on Apple silicon |
main-cd.yml mac-accept (owed) |
declared |
{major}-enterprise |
enterprise | newest official release of the major (clean x.y.z) |
release.yml (owed) |
declared |
main |
continuous | newest promoted set, before arming (also hosting-operator:main) |
main-cd.yml promote, hosting-operator.yml |
written |
latest |
continuous | the tester's moving tag, GHCR's latest release, and memex-portal-ai-base:latest |
main-cd.yml promote, release.yml, base-image-acr.yml |
written |
edge |
edge | newest unverified edge build; never a fleet seed | edge-images.yml |
written |
gliner-multi-2.1, 1.7.4-flix-large-v3-q8 |
service | the s1-extract and whisper model-service line tags, re-pushed by each main build | s1-extract-image.yml, whisper-image.yml |
written |
Each label lists its writer bindings. A binding names a workflow, a job, and the repositories
and registries that job writes the label to. For example, promote writes latest only to
mw-plugin-test, while release.yml writes it to GHCR for every released repository. A pruner
protects a label in the union of its bindings' repositories.
declared means the label is reserved and already protected, but no workflow writes it yet. A
label is therefore protected before its first write, which is the order
Released Artifact Retention requires: protect a set before
advertising it.
Mac Studio and Mac mini are host profiles of the one Mac edition, not separate labels. Both run
the same linux/arm64 image inside Colima's VM. What differs is the hardware: the VM size and the
local language model the host's native Ollama can hold in unified memory. One image under two tags
would be one set of bytes protected and rolled twice. If the two ever need different bytes, add a
second entry, for example {major}-mac-studio.
How retention reads it
- Excluded by name, never locked. A label moves, so locking the digest it names tonight
would pin bytes the label leaves tomorrow.
lock-pinned-digests.pyclassifies every declared label as floating, through the reader. A missing or malformed file raises an error instead of silently classifying fewer labels. A pruner skips each matching tag and the manifest that tag names at the moment of the sweep. - Every status is protected.
written,declaredandretiredlabels are all excluded. Removing an entry is the only way to release a label, and that takes a reviewed pull request. - Never shaped like a version on a self-update repository. The version-shape rule applies to
the repositories a self-updater lists (
memex-portal-ai,memex-migration,memex-control). A model-service line tag such as whisper's is never a roll candidate. The self-updater (VersionSelect) selects every tag matching^\d+\.\d+\.\d+([-+].*)?$except those ending in-latest. A label such as3.1.0-macwould be rolled to as if it were a build.checkrefuses that shape, so a new label is{major}-<word>,{major}.{minor}-<word>, or ends in-latest. This is why only{major}.{minor}-stableis declared. The legacy-shaped3.0.0-stablethat Release Channels uses as an example matchesPlatformVersionTagand notMovingPointer. Before anything writes that shape,VersionSelectmust exclude-stableas it excludes-latest. - Held to its publishers. For a
writtenliteral label (main,latest,edge),checkrequires each binding's workflow to have that job, to spell the label, and to name every bound repository. A binding that drifts from what its workflow writes fails the check. - Held to the writers.
checkrenders sample sets in both notations and requiresplatform-version.py line-pointers(the speller main-cd uses today) to equal thewrittenmain-cd.yml#armlabels. If CD moves a pointer the file does not declare, the check fails.
Not labels, and not in this file: per-version markers such as arm-complete-<version> and
staging-<sha>-<run>. They belong to the publication records that
Released Artifact Retention inventories.
Activating a declared label
{major}.{minor}-stable: the stable promoter described in Release Channels writes it, then flips the entry.{major}-enterprise: wirerelease.ymlto move the label forward only, after the release marker, throughrelease-channels.py tags --writer release.yml. Then flip the entry'sstatustowrittenin the same pull request. Until thentagshands the writer nothing.{major}-mac: needs the Mac acceptance lane below, then the same flip.
The Mac deployment gap
What exists:
- Multi-arch platform images (
linux/amd64andlinux/arm64), which run natively in Colima's arm64 VM. - The
memex-localHomebrew CLI: Colima k3s, the same Helm chart as AKS, ingress-nginx with mkcert TLS, Postgres on a PVC, native Ollama, registry mode, anautorolllaunchd watcher and a port-forward LaunchAgent. See LocalColimaMac and MacLocalStack. homebrew.yml, which installs and tests the formula on a hosted macOS runner. It tests the CLI under bash 3.2. It does not run a cluster.
What a Mac host still lacks. Everything above is built for a developer's laptop. An always-on Mac Studio or Mac mini that serves an instance needs the following:
| # | Gap | What exists | What is needed |
|---|---|---|---|
| G1 | Channel | memex-local and autoroll pull :main, the promoted set before arming and before the ladder verdict |
follow a declared label (3-mac, or 3-latest until it exists), resolve it to the version the image reports, and deploy that version |
| G2 | Pull credential | az acr login, which needs a person's Entra session with AcrPull |
a scoped, read-only pull token for the fleet registry, stored in the host's keychain and rotated |
| G3 | Boot persistence | Colima starts only on memex-local up. The LaunchAgents run only after that user logs in to the GUI |
Colima started at boot, the watchers as boot-time jobs (or auto-login on a dedicated account), sleep disabled and restart after power loss (pmset) |
| G4 | Hardware profiles | one VM size (8 CPU / 16 GiB) and one model choice | mac-studio and mac-mini profiles: VM CPU and memory, and the Ollama model the host's unified memory can hold |
| G5 | Fleet record and roll lane | DeploymentContent assumes AKS (cluster, Key Vault), and the control lane rolls through aks-ops.yml |
a record shape for a Mac host, rolled host-side by its own watcher. The control plane observes the host but cannot reach its cluster |
| G6 | Retention visibility | AXIS 3 asks every declared installation's /api/version |
the host reachable through a tunnel, or declared in .github/acr-retention/instances.json. Otherwise the digest it runs is unprotected |
| G7 | Acceptance | nothing proves an arm64 set boots on Apple silicon. Hosted arm64 macOS runners cannot run Colima's VM (no nested virtualization) | a self-hosted Apple-silicon runner (a Mac mini) running memex-local up and verify against the candidate set. On green it writes 3-mac (the mac-accept job) |
| G8 | Ingress and identity | memex.localhost with mkcert, reachable from the host only |
a real hostname and certificate (ACME, or a tunnel that terminates TLS), and the Entra redirect URIs for it |
| G10 | Architecture-safe bundles | the framework identity (FrameworkBuildIdentity.FrameworkVersion, c<major>e<epoch>) does not name an architecture, and publish-bake-bundles.sh refuses a second architecture under an existing publication |
arm64 bundles published and addressed under a distinct, architecture-qualified key beside the amd64 ones. This is a prerequisite for G7. The host then consumes its control's store read-only (below) |
| G9 | Backups and telemetry | Postgres lives on the Colima VM disk. Logs stay on the host | database dumps to the estate's backup store, and logs and metrics shipped to the estate's observability stack |
Mac hosts and the shared artifact store
The estate's compiled modules and bundles are produced once by the build server and the control instance. They are kept in a control-owned shared artifact store. Portals mount that store read-only, and a client environment's control syncs it one way from the upstream control. A Mac host fits this model as an ordinary portal:
- It consumes, never produces. A Mac host mounts its control's store read-only when the two share a network. Otherwise it syncs one way from that control, the same way a client control syncs from upstream. It never writes into a shared store.
- Its builds need their own key, and that does not exist yet. The Mac edition runs
linux/arm64. Today's framework identity is the major/epoch compatibility key, which names no architecture, and the bake refuses to publish a second architecture under a key that already holds one: "One framework identity cannot hold two architectures". So arm64 and amd64 bundles cannot both be published today. The outstanding prerequisite (G10) is architecture-safe publication and addressing: an architecture-qualified key for bundles, published beside the amd64 ones and resolved by architecture. The store's sweep then counts each architecture as a represented identity. 3-macwaits for that. Without arm64 bundles, a Mac host refuses adoption and compiles from live source. That works, but slowly. G7 (mac-accept) therefore activates3-maconly once G10 has landed and requires the set's arm64 bundles to be present.- The prune job reads the same declaration. It skips every label in
.github/release-channels.jsonacross all of a label's repositories, so the image a Mac host seeds from survives while the label names it.
Order of delivery:
- Declare the labels and make retention read them. This page and its pull request do that.
- G1 and G4 in
memex-local. Add--channel <label>to follow a declared label and pin the resolved version, and add--profile mac-studio|mac-mini. These are repository changes with bash 3.2 tests. - G2 and G3. A host-install step: the keychain-held pull token, Colima at boot, and the power settings. These need a maintainer-issued registry token.
- G10, then G7. Make bundle publication and addressing architecture-safe. Then register a
self-hosted Apple-silicon runner, add
mac-accepttomain-cd.yml, and flip{major}-mactowritten. - G5, G6, G8 and G9. Write the Mac host's deployment record, tunnel, backups and telemetry when the first host is commissioned.
Steps 3 to 5 change live systems: a registry credential, a runner registration, a live host. A maintainer runs them. The repository changes prepare each one.
See also: Release Channels · Release Strategy · Platform Versioning · Pinned Image Retention · Release Support Policy.