Skip to content

feat(server): periodic GitHub release poll + dashboard update banner + macOS amfid fix#33

Open
dvcdsys wants to merge 2 commits intomainfrom
feat/version-check-banner
Open

feat(server): periodic GitHub release poll + dashboard update banner + macOS amfid fix#33
dvcdsys wants to merge 2 commits intomainfrom
feat/version-check-banner

Conversation

@dvcdsys
Copy link
Copy Markdown
Owner

@dvcdsys dvcdsys commented May 7, 2026

Summary

Two related changes that ship together so a dev-box make run works end-to-end on macOS:

  1. Version-check feature — server-side versioncheck service polls GitHub releases on the server/v* tag stream every 6h (configurable), exposes the result via GET /status, dashboard renders a dismissible banner when a newer release is available.
  2. macOS amfid fix (folded in from fix(server): re-sign bundled llama-server on darwin to survive macOS amfid #34) — strips xattrs and re-signs the bundled llama-server + dylibs on every make bundle so 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: killed loop on make run before 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_version in localStorage — dismissing 0.6.0 silences the banner only until 0.6.1 ships.

Config (env)

Var Default Notes
CIX_VERSION_CHECK_ENABLED true Set false to disable polling entirely (no outbound HTTP)
CIX_VERSION_CHECK_INTERVAL 6h Go duration string
CIX_VERSION_CHECK_REPO dvcdsys/code-index Forks can point at their own repo

When disabled, GET /status omits the version-check fields entirely (rather than returning nulls).

Files

Backend (Go):

  • server/internal/versioncheck/check.go — service + Snapshot type (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 poller
  • server/internal/config/config.go — three new env vars + getenvDuration helper
  • server/internal/httpapi/router.goVersionCheck *versioncheck.Service on Deps
  • server/internal/httpapi/server.goGetStatus folds in the version-check fields

Frontend (TS/React):

  • server/dashboard/src/app/UpdateBanner.tsx — banner component
  • server/dashboard/src/app/Shell.tsx — renders banner above main row
  • server/dashboard/src/lib/useServerStatus.ts — type extensions

Contract:

  • doc/openapi.yaml — adds update_available, latest_version, release_url, version_check to /status + new VersionCheckStatus schema
  • server/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.provenance xattr from the previous bundle get SIGKILL'd within milliseconds of execve. 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 with embeddings: llama-server not ready: context deadline exceeded.

Root cause: existing bundle: target copies cp -R $(LLAMA_DIR)/. $(BUNDLE_DIR)/llama/cp creates new files macOS treats as untrusted regardless of source state. So the strip + deep re-sign must run on every bundle, gated behind ifeq ($(OS),darwin) so other platforms are unaffected.

Diagnosis trail

  1. Direct llama-server invocation outside the supervisor → same signal: killed, exit 137, empty stderr
  2. xattr -l showed com.apple.provenance on every dylib (auto-applied by macOS on file creation)
  3. codesign --force --deep --sign - once → first run works, subsequent runs killed (signature on dylibs not propagated by single-binary codesign)
  4. xattr -cr <bundle>/llama/ && codesign --force --deep --sign - <bundle>/llama/llama-server → both consecutive runs alive (SN status, full metal init logs)

Test plan

Version-check

  • cd server && go test ./internal/versioncheck/... cold
  • cd server && make test — full suite green
  • cd server/dashboard && pnpm typecheck && pnpm build
  • cd server && make run → visit http://localhost:21847/status → verify the four version-check fields appear
  • Open dashboard, confirm no banner when no release is newer
  • (Manual) point CIX_VERSION_CHECK_REPO at a repo with a known-newer server/v* tag and confirm banner appears + dismissal persists per-version
  • Set CIX_VERSION_CHECK_ENABLED=false and confirm /status omits all version-check fields

macOS amfid fix

  • make bundle produces a working llama-server
  • Two consecutive direct invocations both survive past ggml_metal_library_init
  • make run boots cleanly without supervisor restart loop
  • Re-run make bundle + make run → still works (the recurrence-on-rebundle case)
  • Linux dev box (or CI Linux runner) → ifeq ($(OS),darwin) block skipped, no behavioural change

🤖 Generated with Claude Code

dvcdsys and others added 2 commits May 7, 2026 15:14
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>
@dvcdsys dvcdsys changed the title feat(server): periodic GitHub release poll + dashboard update banner feat(server): periodic GitHub release poll + dashboard update banner + macOS amfid fix May 7, 2026
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