Skip to content

Versions and releases

For maintainers: version identity, packaging and publication.

Synoec is a technical alpha. The development version is in the root package.json. A release ships a Mac .dmg and a Windows .exe installer, each with its .sha256; that is all a release publishes. The changelog lists every release, the source-only tags and the asset-only tags (memory-coreai-v1 and the NInfer runtime mirror that Local AI setup downloads). Project status describes tested behavior and known limits. A version bump, source tag, signed package and published release are separate steps, and each needs its own authorization.

To run Synoec today, use Getting started and the desktop build guide.

Bump only when cutting a release, by compatibility and behavior, not changed-line counts (rules in TESTING.md).

Command Use
bun run release:suggest Suggest an increment from commit messages since the last tag
bun run release:version patch|minor|major|X.Y.Z[-beta.N] A fix release, a feature release, a breaking change, or an exact version, with a pre-release suffix below 1.0 if wanted

The command rewrites the root and app manifests, packages/shared/src/product-version.ts and bun.lock. It does not build, commit, tag, push or publish. Between bumps every build carries its build number and commit, so About and synoec --version identify the exact build.

Only when a milestone is authorized: commit the reviewed tree, then annotate and push the exact version together.

Terminal window
git tag -a vX.Y.Z -m "Synoec X.Y.Z source milestone"
git push --atomic origin HEAD refs/tags/vX.Y.Z

A tag does not imply packaging or publication.

Terminal window
bun run release:package

This packages the CLI, builds the portable Electron app, assembles it and packages the Mac app. It requires a macOS host, the pinned Bun, a clean committed tree and an assembled app whose version matches package.json. Outputs go to the ignored release/cli/ and release/desktop/ directories with checksums and manifests. Inspect the actual archive contents and installation on each platform you claim.

The Windows installer is packaged on a Windows host (or any host with NSIS) after the same build and assembly:

Terminal window
bun run desktop:package:windows

It writes release/desktop/synoec-windows-vX.Y.Z-x64.exe with .sha256 and .manifest.json. It needs NSIS 3 makensis on PATH or in MAKENSIS, the pinned Bun, a clean committed tree and an assembled Windows app of the current version. The manual Desktop Windows acceptance and installer workflow does all of this and keeps the installer as a two-day artifact once acceptance passes. The installer is per user (no administrator prompt) and its uninstaller keeps user data; After copying the app it sets up Git for Windows (reusing one already installed, otherwise the pinned official PortableGit), then it shows a found Ollama (with an option to start it with Synoec), offers Bonsai 2 on Synoec’s Local AI service on a supported NVIDIA card, with an unticked Image generation box and its extra download on an 11 GB card, sums up and downloads the runtime and model; the app connects Ollama and starts Bonsai at first launch. The installer’s first page chooses Upgrade (keeps everything), Install fresh (removes the app’s data, keeps the downloaded local AI) or Remove (everything, models included). Silent installs take /S /MODE=install|upgrade|remove /OLLAMA=1 /OLLAMASTART=1 /BONSAI=1 /BONSAIIMAGE=1. The app’s Update control runs /S /NCRC /MODE=upgrade /UPDATE=<process id>, which waits for that app to exit, replaces the files and reopens Synoec, skipping the Git, probe and Bonsai steps. Upgrading by hand does not offer a Bonsai 2 that is already on the computer; it offers only image generation, when that is not there yet.

The Release workflow (.github/workflows/release.yml) builds and publishes a release without switching machines. It runs only when a v* tag is pushed, or when started by hand for a tag that is already pushed; ordinary pushes never run it.

  1. bun run release:version …, add the changelog entry, commit.
  2. git tag -a vX.Y.Z[-beta.N] -m "Synoec X.Y.Z[-beta.N]" and git push --atomic origin HEAD refs/tags/vX.Y.Z[-beta.N].
  3. The workflow checks the tag matches package.json, builds and packages the Windows installer on windows-latest and the Mac .dmg on macos-26, uploads both with their .sha256 to a draft, then publishes it as Latest with notes that state the signing limits.

If the release already exists (a rerun, or a download added later), the workflow replaces this version’s downloads on it and keeps its notes. A manual run can skip the Mac job (mac: false), or build another branch or commit to diagnose a build (source), which is never published.

Both jobs fetch the pinned assets a fresh checkout lacks (context-embedding:fetch, rea:fetch). The Mac job also runs memory-coreai:fetch: the MiniLM archive (memory-coreai-v1) and the prebuilt Core AI helper (memory-coreai-helper-v1), which needs the macOS 27 SDK that hosted runners lack. The build refuses the helper unless its manifest matches the current CoreAIMemory.swift; after changing that file, build the helper (memory-coreai:build), publish it as the next memory-coreai-helper-vN asset-only pre-release and update core-ai-helper-release.json. A test fails until the pin matches the source.

The Mac job signs with the persistent Apple Development identity from two repository secrets, MAC_SIGNING_CERTIFICATE_P12 (the base64 of the exported .p12) and MAC_SIGNING_CERTIFICATE_PASSWORD; without them it stops, since ad-hoc signing is not allowed. Adding them is the owner’s step. A failed run leaves no published release; rerun it by hand for the same tag.

The Mac package is certificate signed with the persistent local identity and not notarized; its manifest records notarized: false and the DMG says so. There is no Developer ID notarization lane yet. The Windows installer is not code signed; its manifest records signing: "unsigned" and SmartScreen warns on first run. Identity selection and the macOS privacy-permission caveats are in desktop signing.

bun run release:publish creates a draft GitHub release for the version tag, which must already be pushed (gh release create --verify-tag), with only this version’s user downloads: the Mac .dmg (required) and the Windows .exe (when present in release/desktop/), each with its .sha256. It refuses a Mac manifest for another bundle identifier and any download whose bytes no longer match its manifest and checksum. It does not require notarization, since no notarization lane exists; it prints a note for the unnotarized Mac package and for each unsigned download, and the release text must say so. The CLI tarball, Mac .zip and manifests are not uploaded. After review, publish the draft as Latest by hand. Publish only when a release is explicitly authorized; a testing distribution must state its real signing and platform limits.

Optional model assets (such as the Core AI memory archive) are versioned and published separately with pinned descriptors. Never replace bytes behind an existing pin.

Use TESTING.md for proportionate checks. A release candidate needs its release gate plus platform and install evidence; a commit or tag does not. The Release workflow runs on a v* tag or a manual dispatch only; the older push-triggered workflows stay disabled under .github/workflows-disabled/.

  • The Windows installer is unsigned. Install and uninstall on a clean machine are checked by hand through the acceptance workflow, not by an automated gate.
  • Mac packages are not notarized, so macOS Gatekeeper may block them when downloaded.
  • The app reads its GitHub releases with a built-in read-only key (apps/desktop/assets/release-access.json, staged as runtime/release-access.json), shortly after it opens and every six hours; Settings → General → Check for Updates does so on demand. It asks for the repository’s latest release, which GitHub never resolves to a draft or prerelease, so the NInfer mirror is skipped. A release whose vX.Y.Z is above the app’s version puts an Update control above the Profile on the left rail. It downloads the release’s installer to the app’s data folder and runs it. On Windows that shows no pages: the app shuts down normally, the files are replaced and Synoec reopens. If the app stays open while quitting, because the person chose to keep editing unsaved drafts, the update does not run. On a Mac the disk image opens. With no published release it finds nothing; trust its reported status, not an endpoint you cannot reach.
  • Licensing — notices that must ship with artifacts
  • Changelog
  • Desktop signing

Decisions behind this page: 0132, 0196, 0240, 0244, 0286 — see the decisions index.