dispatcher: create macOS instances through the Compute API with a Namespace application #3
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "claude/macos-application-request"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
KSE-0186 D8 (key.store PR #125): macOS runners cannot put
/nixon a cache volume, so key.store hydrates it at VM start from a Namespace macOS Application (nsc macos package→ nscr.io). Thensc createCLI (0.0.557) has no applications flag, so the macOS create gains a Compute API backend.Default behaviour unchanged: with no
macos_application_imagethe backend resolves tonscand the create path is byte-for-byte today'snsc create(verified by the existing tests). Everything after create (Compute SSH bootstrap bound to the TTL, drain,nsc destroy, volume mapping/retry) stays shared.internal/nsc/macos_compute.go(new):CreateInstanceRequest{Shape (vcpu/mem from machine_type, os=macos, arch, support-disk selectors), Deadline=now+TTL, Labels, Volumes (NSC_CACHE_VOLUMES → CACHE), Applications=[{Name, ImageRef, Command, Args}], Experimental.MacosBaseImageId}; bearer fromNSC_TOKEN_FILE; ResourceExhausted retry with backoff; pollsDescribeInstanceuntil RUNNING and thesshservice is READY (when listed), logging phase changes + application status (containers / services / shutdown reasons); destroys on ERROR/DESTROYED/timeout with the shutdown reasons in the error.namespace.*, mirrored innsc.Options, logged at startup):macos_create_backend=auto(default: compute iff an application is configured) |nsc|compute;macos_application_image(nscr.io only; startup Warn unless@sha256:-pinned — whoever can push that repo runs code at VM start on every macOS runner),macos_application_name(default: image's last path component),macos_application_command(defaultentrypoint),macos_application_args,macos_application_ready_file.KEYSTORE_NSC_MACOS_APPLICATION_{NAME,IMAGE,READY_FILE}; when a ready file is named, waits for it (KEYSTORE_NSC_MACOS_APPLICATION_READY_TIMEOUT, default 900 s; optionalKEYSTORE_NSC_MACOS_APPLICATION_FAILED_FILEends the wait early) before the Nix installer decision, and puts/nix/var/nix/profiles/default/binon PATH first so an application-installed Nix is seen instead of running the Determinate installer a second time; exportsKEYSTORE_NSC_MACOS_APPLICATION_READY=1|0.ApplicationRequestis a JOB workload — the instance terminates when the command exits — so the entrypoint must keep running after its ready marker (key.store's entrypoint holds until SIGTERM /<state>/release).writeFakeExecutableretries ETXTBSY (pre-existing flake at main).go vet+go test ./...green (linux/amd64 go1.26.3, darwin/arm64 go1.25.5); go.mod/go.sum unchanged.Unverified against the live API (no instance created): the v1beta macOS
CreateInstancefor tenant osibqsl33bahe (the 2026-02 CLI switch cited support-disk selector rejection) — key.store stays on thenscpath until a live probe withmacos_create_backend: computesucceeds.Workflow-orchestrated (implementer + adversarial reviewer); reviewer fixes folded in.
🤖 Generated with Claude Code
Founder direction (2026-08-17): macOS runners cannot put /nix on a cache volume, so /nix is hydrated at VM start from a Namespace macOS Application (`nsc macos package` -> nscr.io image + zstd snapshot of the aarch64-darwin closures, refreshed by the same pipeline as the custom base image). Only the Compute API can attach an application to an instance: `nsc create` (0.0.557) has no flag for it, and until now every macOS launch went through the CLI. - internal/nsc/macos_compute.go: Compute-API create backend. Builds CreateInstanceRequest{Shape (vcpu/mem from machine_type, os=macos, arch, the same support-disk selectors as the CLI path), Deadline=now+TTL, labels, Volumes from NSC_CACHE_VOLUMES, Applications=[{Name, ImageRef, Command, Args}], Experimental.MacosBaseImageId for macos_custom_image_id; no Features = --bare}, authenticated with the NSC_TOKEN_FILE bearer token the CLI uses, retries CreateInstance with backoff on ResourceExhausted like the CLI, then polls DescribeInstance until RUNNING (MacosCreateWaitTimeout inside MacosCreateTimeout), logging the phase and what describe exposes about the application (container name/id, service readiness, shutdown reasons with container_failed_reason; there is no dedicated application status field in compute v1beta). ERROR/DESTROYED/timeout destroy the instance and fail the launch with the shutdown reasons. - internal/nsc/macos_nsc.go: launchMacOSRunner (was launchMacOSRunnerViaNSC) picks the create backend and keeps everything else shared: private ExtraEnv copy, age identity, TTL drain env, cache-volume mapping, volume-less retry when Namespace rejects the volume request, tracked state, Compute SSH bootstrap bound to the TTL, `nsc destroy` teardown. The CLI create moved to createMacOSInstanceViaNSC unchanged. - Options/config: macos_create_backend (nsc | compute | auto; default auto = compute when macos_application_image is set, nsc otherwise; explicit nsc + application is rejected, compute needs compute_base_url), macos_application_image (nscr.io only), macos_application_name (default: last path component of the image), macos_application_command (default "entrypoint"; image ENTRYPOINT/CMD are ignored by Namespace), macos_application_args, macos_application_ready_file. main.go wires them and logs the resolved backend at startup. - Bootstrap: the wrapper exports KEYSTORE_NSC_MACOS_APPLICATION_NAME/_IMAGE/ _READY_FILE; when a ready file is named, macosBootstrapScript waits for it (KEYSTORE_NSC_MACOS_APPLICATION_READY_TIMEOUT, default 900s) before the Nix installer runs, so a snapshot import and the installer never race; on timeout it logs, exports KEYSTORE_NSC_MACOS_APPLICATION_READY=0 and installs Nix itself as before. - internal/nsc/macos.go: the dead 2026-02 Compute path (launchMacOSRunner running the bootstrap itself as an ApplicationRequest, plus its stop monitor) is removed; the new backend supersedes it. Default behaviour is unchanged: without macos_application_image the dispatcher still runs `nsc create --bare ...` with the same arguments. Tests: go test ./... on darwin/arm64 (go1.25.5) and 10/10 on the forge (linux/amd64, go1.26.3, CGO_ENABLED=0). New tests drive an in-process fake Compute API (CreateInstance/DescribeInstance/GetSSHConfig) plus the fake sshd and fake nsc: the request carries the application/shape/volumes/deadline, PENDING->CREATING->RUNNING is waited for, volume rejections retry without volumes, ResourceExhausted retries until the create timeout, ERROR and never-RUNNING instances are destroyed, the wrapper env advertises the application and ready file, config parsing/validation, and the bootstrap ready-file wait. Not exercised against the live Compute API (no macOS instance was created for this change). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>