Skip to content

fix(mac): a cached ETag is not an answer to "what server should I install" - #250

Merged
dvcdsys merged 1 commit into
mainfrom
fix/mac-runtime-etag-304
Aug 14, 2026
Merged

fix(mac): a cached ETag is not an answer to "what server should I install"#250
dvcdsys merged 1 commit into
mainfrom
fix/mac-runtime-etag-304

Conversation

@dvcdsys

@dvcdsys dvcdsys commented Aug 14, 2026

Copy link
Copy Markdown
Owner

The bug

A fresh install of cix.app could reach a state where setup fails permanently:

Setup failed — cix could not set itself up again.
could not install the cix server: could not find a cix server to install: not modified

ensureRuntime resolved which server to download through the same
release.Client the background update check uses. That client replays GitHub's
ETag as If-None-Match, so GitHub answers 304 Not Modified and
ensureRuntime reported it as a fatal error.

The ETag describes GitHub's release listing — identical for every Mac in the
world — and says nothing about whether this machine has a runtime on disk.

Why it was permanent, not transient

  1. First launch: onReady runs one background update check, which persists the
    ETag to ~/.cix/launcher.json.
  2. Setup does not complete (cancelled, or the server refuses to start against an
    empty database) — no runtime installed, panel shows STOPPED.
  3. Every later install attempt sends the cached ETag → 304 → "not modified".

~/.cix/ survives reinstalling the app, so the machine stayed unsetuppable
until a new server/v* release changed the listing.

The fix

updater.latestRuntime separates the two questions. On a 304 it uses the
listing this session already saw; when the ETag came from an earlier run (so the
body it matched is gone) it re-asks without it — on a copy of the client, so
the cached ETag survives for the cheap checks it exists for.

Update checks are unchanged: there a 304 still correctly means "no news".

Tests

Two new tests in cli/launcher/runtime_darwin_test.go, against a fake releases
API that honours If-None-Match:

  • TestLatestRuntimeIgnoresAStaleETag — an ETag seeded into prefs (the released
    0.1.0 failure) still resolves a release, and neither the in-memory nor the
    persisted ETag is spent.
  • TestLatestRuntimeReusesTheListingThisSessionSaw — a 304 after a check in the
    same process is answered from memory, with no extra request.

go build ./... and go test ./launcher/ ./internal/release/ pass.

Workaround for anyone already stuck on 0.1.0

rm ~/.cix/launcher.json && killall cix

🤖 Generated with Claude Code

…tall"

ensureRuntime resolved the runtime to install through the same release
client the background update check uses, which replays GitHub's ETag. A
304 therefore came back as an error and surfaced as

    could not install the cix server: could not find a cix server to
    install: not modified

with setup dead in the water. The ETag describes GitHub's release
listing — the same listing for every Mac in the world — and says nothing
about whether this machine has a runtime on disk, so the two questions
must not share a cache.

It bricked the install permanently rather than transiently: the first
launch runs one background update check, which persists the ETag to
~/.cix/launcher.json, and that file outlives both the process and a
reinstall of the app. Any launch after a first run that did not complete
setup answered every install attempt with 304, and kept doing so until
somebody published a new server release.

latestRuntime now separates them: on a 304 it takes the listing this
session already saw, and when the ETag came from an earlier run — so the
body it matched is gone — it re-asks without it, on a copy of the client
so the cached ETag survives for the cheap checks it exists for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dvcdsys
dvcdsys merged commit 306a1bf into main Aug 14, 2026
9 checks passed
@dvcdsys
dvcdsys deleted the fix/mac-runtime-etag-304 branch August 14, 2026 18:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant