feat(server): periodic GitHub release poll + dashboard update banner + macOS amfid fix#33
Open
feat(server): periodic GitHub release poll + dashboard update banner + macOS amfid fix#33
Conversation
Adds a server-side `versioncheck` service that polls GitHub releases
on the `server/v*` tag stream every 6h (configurable), exposes the
result via `GET /status`, and renders a dismissible banner in the
dashboard when a newer release is available.
Single poll per server regardless of open clients — the per-instance
cache avoids hammering the GitHub API and keeps the browser from
forging "latest version" claims. ETag-based revalidation keeps
unauthenticated rate-limit usage at ~4 req/day per server (vs. the
60/h GitHub limit).
Config:
CIX_VERSION_CHECK_ENABLED default true
CIX_VERSION_CHECK_INTERVAL default 6h, Go duration string
CIX_VERSION_CHECK_REPO default "dvcdsys/code-index"
Server-side:
- server/internal/versioncheck/check.go — service + Snapshot type
- server/internal/versioncheck/check_test.go — coverage including
ETag revalidation, prerelease/draft skipping, error caching
- GetStatus folds in update_available / latest_version /
release_url / version_check (omitted entirely when disabled)
- main.go wires the background poller with 60s initial delay so
the boot path doesn't depend on GitHub reachability
Dashboard:
- UpdateBanner component, dismissals namespaced by latest_version
in localStorage so a fresh release re-shows the banner
- useServerStatus types extended with the new optional fields
OpenAPI:
- update_available, latest_version, release_url, version_check
on the existing /status response
- VersionCheckStatus schema for the version_check object
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…amfid macOS Sequoia (26+) tightened the amfid (AppleMobileFileIntegrity) policy: ad-hoc-signed binaries whose linked dylibs carry stale signatures or a com.apple.provenance xattr from the previous bundle get SIGKILL'd within milliseconds of execve. Symptom in the supervisor: `signal: killed` with empty stderr (kill happens before the child can write a single byte), followed by exhausted restart budget and the boot path failing with "embeddings: llama-server not ready: context deadline exceeded". Root cause is that `cp -R $(LLAMA_DIR)/. $(BUNDLE_DIR)/llama/` in the existing `bundle:` target creates new files macOS treats as untrusted; whatever blessing the previous bundle had does not carry over. So the fix has to run on every bundle, not just first install. Wraps the strip + deep re-sign in `ifeq ($(OS),darwin)` so other platforms are unaffected. Tested with two consecutive `make bundle` + direct llama-server invocation cycles — both stay alive past the first metal_library_init log line where the previous behaviour died. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related changes that ship together so a dev-box
make runworks end-to-end on macOS:versioncheckservice polls GitHub releases on theserver/v*tag stream every 6h (configurable), exposes the result viaGET /status, dashboard renders a dismissible banner when a newer release is available.llama-server+ dylibs on everymake bundleso macOS Sequoia (26+) doesn't SIGKILL the supervisor's child process.The amfid fix is bundled here because without it, anyone testing this PR on a macOS dev box hits a
signal: killedloop onmake runbefore they can even see the version-check banner.(1) Version-check feature
One poll per server regardless of open clients — per-instance cache avoids hammering GitHub and keeps the browser from forging "latest version" claims. ETag-based revalidation keeps unauthenticated GitHub rate-limit usage at ~4 req/day per server (vs. the 60/h limit). 60s initial delay so the server boot path doesn't depend on GitHub reachability. Banner dismissals are namespaced by
latest_versionin localStorage — dismissing 0.6.0 silences the banner only until 0.6.1 ships.Config (env)
CIX_VERSION_CHECK_ENABLEDtruefalseto disable polling entirely (no outbound HTTP)CIX_VERSION_CHECK_INTERVAL6hCIX_VERSION_CHECK_REPOdvcdsys/code-indexWhen disabled,
GET /statusomits the version-check fields entirely (rather than returning nulls).Files
Backend (Go):
server/internal/versioncheck/check.go— service +Snapshottype (337 lines)server/internal/versioncheck/check_test.go— covers ETag revalidation, prerelease/draft skipping, error caching (266 lines)server/cmd/cix-server/main.go— wires the background pollerserver/internal/config/config.go— three new env vars +getenvDurationhelperserver/internal/httpapi/router.go—VersionCheck *versioncheck.ServiceonDepsserver/internal/httpapi/server.go—GetStatusfolds in the version-check fieldsFrontend (TS/React):
server/dashboard/src/app/UpdateBanner.tsx— banner componentserver/dashboard/src/app/Shell.tsx— renders banner above main rowserver/dashboard/src/lib/useServerStatus.ts— type extensionsContract:
doc/openapi.yaml— addsupdate_available,latest_version,release_url,version_checkto/status+ newVersionCheckStatusschemaserver/internal/httpapi/openapi/openapi.gen.go— regenerated(2) macOS amfid fix (Makefile)
macOS Sequoia (26+) tightened amfid: ad-hoc-signed binaries whose linked dylibs carry stale signatures or a
com.apple.provenancexattr from the previous bundle get SIGKILL'd within milliseconds ofexecve. Symptom in the supervisor:{"level":"WARN","msg":"llama-server exited unexpectedly","err":"signal: killed"}with empty stderr (kill happens before the child can write a single byte), restart loop exhausts budget after 4 attempts, boot path fails withembeddings: llama-server not ready: context deadline exceeded.Root cause: existing
bundle:target copiescp -R $(LLAMA_DIR)/. $(BUNDLE_DIR)/llama/—cpcreates new files macOS treats as untrusted regardless of source state. So the strip + deep re-sign must run on every bundle, gated behindifeq ($(OS),darwin)so other platforms are unaffected.Diagnosis trail
llama-serverinvocation outside the supervisor → samesignal: killed, exit 137, empty stderrxattr -lshowedcom.apple.provenanceon every dylib (auto-applied by macOS on file creation)codesign --force --deep --sign -once → first run works, subsequent runs killed (signature on dylibs not propagated by single-binarycodesign)xattr -cr <bundle>/llama/ && codesign --force --deep --sign - <bundle>/llama/llama-server→ both consecutive runs alive (SNstatus, full metal init logs)Test plan
Version-check
cd server && go test ./internal/versioncheck/...coldcd server && make test— full suite greencd server/dashboard && pnpm typecheck && pnpm buildcd server && make run→ visithttp://localhost:21847/status→ verify the four version-check fields appearCIX_VERSION_CHECK_REPOat a repo with a known-newerserver/v*tag and confirm banner appears + dismissal persists per-versionCIX_VERSION_CHECK_ENABLED=falseand confirm/statusomits all version-check fieldsmacOS amfid fix
make bundleproduces a workingllama-serverggml_metal_library_initmake runboots cleanly without supervisor restart loopmake bundle+make run→ still works (the recurrence-on-rebundle case)ifeq ($(OS),darwin)block skipped, no behavioural change🤖 Generated with Claude Code