Skip to content

Latest commit

 

History

History
244 lines (209 loc) · 12.6 KB

File metadata and controls

244 lines (209 loc) · 12.6 KB

Install, update, and uninstall the provider

Last updated: 2026-10-05

How to put the darkbloom CLI on an Apple Silicon Mac with scripts/install.sh, what the script verifies before it touches an existing install, how the binary is updated afterwards, and how to remove everything. For operators; at the end darkbloom doctor runs and reports the machine's state.

Prerequisites

  • An Apple Silicon Mac. The binary targets macOS 14 (provider-swift/Package.swift, .macOS(.v14)); the installer checks only uname = Darwin and uname -m = arm64 and prints the macOS version without gating on it. Sizing (RAM, disk, which models fit) is in hardware requirements.
  • New network providers require macOS 27 or later and current qualified App Attest authorization, not just a successful install or OS upgrade. Only the frozen legacy identities may temporarily use legacy verification; an existing local MDM profile does not establish eligibility.
  • Outbound HTTPS to the coordinator (https://api.darkbloom.dev).
  • No sudo. The script writes to ~/.darkbloom, appends one PATH line to ~/.zshrc (or ~/.bashrc), and tries — best effort, no prompt — to link /usr/local/bin/darkbloom.

Steps

1. Run the installer

[backend] enabled_models = [] idle_timeout_mins = 60 # free when idle; 0 = always ready (see darkbloom idle) max_model_slots = 3

[gemma_optimizations] prefill_layer18 = true weighted_r1 = true

[coordinator] url = "wss://api.darkbloom.dev/ws/provider" private_only = false


The coordinator serves `scripts/install.sh` at `/install.sh` with its own base
URL substituted for the `__DARKBLOOM_COORD_URL__` placeholder; the embedded copy
`coordinator/api/install.sh` is regenerated byte-for-byte by
`scripts/sync-install-embed.sh`. To run the script from a checkout, set the
placeholder yourself: `COORD_URL=https://api.darkbloom.dev bash scripts/install.sh`.

The script performs these actions in order (`scripts/install.sh`; failures exit
1 and are listed in [troubleshooting](./troubleshooting.md#installer-exits)):

1. **Preflight.** Aborts unless `uname` is `Darwin` and `uname -m` is `arm64`.
   Prints chip (`sysctl machdep.cpu.brand_string`), RAM (`hw.memsize`) and
   macOS version (`sw_vers`).
2. **Step 1/5 — release metadata.** `GET $COORD_URL/v1/releases/latest`, then
   extracts `url`, `bundle_hash`, `binary_hash`, `metallib_hash`, `version`,
   `backend` with `sed`. Missing `url`, `bundle_hash` or `version` aborts.
3. **Step 2/5 — download and verify.** Creates `~/.darkbloom` and
   `~/.darkbloom/bin`, downloads the tarball to `/tmp/darkbloom-bundle.tar.gz`
   and requires `shasum -a 256` to equal `bundle_hash` (mismatch deletes the
   file and aborts). `install_bundle_atomically` then:
   - extracts into `~/.darkbloom/.install-staging-<pid>-<random>`;
   - requires `bin/darkbloom`, `bin/darkbloom-enclave`, `bin/mlx.metallib`;
   - hashes `bin/darkbloom` against `binary_hash` and `bin/mlx.metallib`
     against `metallib_hash` (`verify_file_hash`; a hash the release record
     omits is skipped);
   - when the tarball contains `Darkbloom.app`: `verify_staged_app_payload`
     (both hashes are then mandatory and `Contents/MacOS/darkbloom` and
     `Contents/MacOS/mlx.metallib` are re-hashed) and `verify_staged_app`:
     `codesign --verify --deep --strict -R="$DARKBLOOM_DESIGNATED_REQUIREMENT"`
     where the requirement is
     `anchor apple generic and identifier "io.darkbloom.provider" and certificate leaf[subject.OU] = "SLDQ2GJ6TL"`;
     the fan-helper triple (`darkbloom-fan-helper-v1` string in the binary ⇔
     marker `Contents/Resources/darkbloom-runtime-capabilities/fan-helper-v1`
     = `1` ⇔ `Contents/Helpers/darkbloom-fan-helper` present, a regular file,
     mode `0755`, signed to
     `anchor apple generic and identifier "io.darkbloom.fan-helper" and certificate leaf[subject.OU] = "SLDQ2GJ6TL"`);
     the paged-kernel pair (`engine_v2_kv_backend` string in the binary ⇔
     marker `paged-kernel-v1` = `1`, plus exactly one non-empty
     `Contents/Resources/mlx-swift-lm_MLXLMCommon.bundle/pagedattention.metal`);
     and a runtime smoke test:
     `DARKBLOOM_NO_UPDATE_CHECK=1 DARKBLOOM_GEMMA4_PREFILL_CHUNK_EVAL=18 MLX_GEMMA4_FUSED_WEIGHTED_UNSORT=1 MLX_GATHER_QMM_EXPERT_SLICES=1 darkbloom runtime-smoke`
     (`provider-swift/Sources/darkbloom/RuntimeSmokeCommand.swift`,
     `RuntimeSmoke`);
   - `commit_staged_app` moves any existing `~/.darkbloom/Darkbloom.app` to
     `~/.darkbloom/.install-backup-<pid>-<random>`, moves the staged app in,
     writes the symlinks `~/.darkbloom/bin/darkbloom`, `darkbloom-enclave`,
     `mlx.metallib` → `../Darkbloom.app/Contents/MacOS/*`, and `chmod +x`. Any
     failure moves the backup back;
   - a tarball without `Darkbloom.app` (the retired flat layout) is refused;
   - the staging directory is removed; on any failure the script prints
     `Existing installation was left unchanged.` and exits 1.
4. **PATH.** `ln -sf ~/.darkbloom/bin/darkbloom /usr/local/bin/darkbloom`
   (errors ignored). The rc file is `~/.zshrc`, or `~/.bashrc` only when
   `~/.zshrc` does not exist. If the rc does not already mention
   `.darkbloom/bin`, lines referencing `.dginf/bin`, `.eigeninference/bin`,
   `alias eigeninf`, `alias dginf`, `# EigenInference` and `# Darkbloom` are
   deleted and `# Darkbloom` + `export PATH="$HOME/.darkbloom/bin:$PATH"` is
   appended; the rc is then sourced.
5. **Config location and coordinator.** The CLI reads only
   `~/.config/darkbloom/provider.toml` or an explicit `--config` path
   (`provider-swift/Sources/ProviderCore/Config/ProviderConfig.swift`,
   `defaultConfigPath`); retired `~/.dginf` / `~/.eigeninference` installs are
   not migrated. `bind_provider_coordinator` binds the provider to the
   coordinator that served the installer and keeps every other line of the
   file:
   - when `$COORD_URL` is `https://api.darkbloom.dev`, it removes any `url`
     line under `[coordinator]`, so the provider uses its built-in production
     default. It does not create the file when it is missing;
   - for any other coordinator, it writes `url = "wss://<host>/ws/provider"`
     under `[coordinator]` and creates the file when it is missing (`ws://`
     only for `http://localhost` or `http://127.0.0.1`; any other scheme, a
     path, or a character outside `A-Za-z0-9.-` and `:port` stops the
     install).

   A provider that is already running keeps its old coordinator until
   `darkbloom start`.
6. **Step 3/5 — Secure Enclave identity.** Runs `darkbloom-enclave info`
   (`provider-swift/Sources/darkbloom-enclave-cli/EnclaveCLI.swift`), which
   creates the P-256 key if missing. Failure prints a warning; the install
   continues with reduced trust (see [attestation](./attestation.md)).
7. **Step 4/5 — verification setup.** `configure_device_verification` uses the
   local `sw_vers` major version. On macOS 27 or later it skips profile checks,
   download and System Settings, and directs the user to login/start/status for
   App Attest approval. Older macOS only reads `profiles status -type enrollment`,
   preserves existing management without claiming Darkbloom verification, and
   prints the upgrade and temporary legacy notices. The installer never posts
   an anonymous enrollment request, downloads a profile or opens profile setup.
   After installation, run `darkbloom login` with the existing linked account,
   then `darkbloom enroll`; only eligible grandfathered identities may re-enroll.
   Unknown OS versions receive the same login/enroll guidance to select their
   verification path without downloading a profile. On older macOS,
   the CLI checks frozen account/key eligibility even when a Darkbloom profile
   is already installed, and does not reinstall that profile on success.
   Setup choice grants no serving authorization; see the
   [authorization contract](../reference/provider-authorization.md).
8. **Step 5/5 — catalog.** `GET $COORD_URL/v1/models/catalog?type=text`;
   interactive runs print up to 20 entries. Nothing is downloaded.

### 2. Reload your shell

```bash
source ~/.zshrc   # or open a new terminal

3. Check the machine

darkbloom doctor

Continue with the quickstart to download a model, link the account and start serving.

Verify

darkbloom --version          # prints ProviderCore.version, e.g. 0.8.16
ls -l ~/.darkbloom/bin       # symlinks into ../Darkbloom.app/Contents/MacOS/
codesign --verify --deep --strict \
  -R='anchor apple generic and identifier "io.darkbloom.provider" and certificate leaf[subject.OU] = "SLDQ2GJ6TL"' \
  ~/.darkbloom/Darkbloom.app && echo signature OK

Update

Manual

darkbloom update --check-only     # report only
darkbloom update                  # download, verify, atomic replace, restart the service

darkbloom update (provider-swift/Sources/darkbloom/UpdateCommand.swift, Update) drives SelfUpdater (provider-swift/Sources/ProviderCore/Update/SelfUpdater.swift): it fetches GET {coordinator}/v1/releases/latest?platform=macos-arm64, verifies the SHA-256 of the bundle and, when published, of the binary and mlx.metallib, verifies the code signature (production always constructs the updater with verifyCodeSignatures: true), replaces the install, and — if the LaunchAgent is loaded — restarts it via ProcessLifecycle.restartAfterUpdate(). Flags: --check-only, --override-quarantine, --coordinator <url>, --config.

Automatic

provider.auto_update (default true, provider-swift/Sources/ProviderCore/Config/ProviderConfig.swift) is toggled with darkbloom autoupdate enable|disable|status (provider-swift/Sources/darkbloom/AutoUpdateCommand.swift). When enabled the daemon checks once at start (runStartupAutoUpdate, provider-swift/Sources/darkbloom/Start/StartCommand+Modes.swift) and then on a loop (provider-swift/Sources/ProviderCore/ProviderLoop+AutoUpdate.swift):

Step Symbol
First in-daemon check after start autoUpdateInitialDelay
Check interval autoUpdateInterval
Random delay before installing up to provider.update_jitter_seconds (updateJitterSeconds)
Drain of in-flight requests before restart updateDrainTimeout
Skip every check (banner, start, loop, watchdog) DARKBLOOM_NO_UPDATE_CHECK set to any value

The values are in cli-reference.md (the jitter default in its provider.toml table).

A freshly installed version that crashes rollbackThreshold times before surviving defaultStabilizationSeconds is quarantined on this machine (provider-swift/Sources/ProviderCore/Update/UpdateRecoveryState.swift; values in runtime constants). Only that exact version is blocked; a newer release installs normally. darkbloom update --override-quarantine reinstalls it anyway, and darkbloom doctor reports the quarantine under up to date.

The unprivileged updater never touches the root fan helper; after an update run sudo darkbloom fan enable again if you use fan control.

Uninstall

sudo darkbloom fan uninstall    # only if fan control was enabled
darkbloom stop --uninstall      # stops the daemon, removes both LaunchAgent plists
darkbloom unenroll              # opens System Settings to remove the MDM profile; offers to delete config + tokens
rm -rf ~/.darkbloom ~/.config/darkbloom
sudo rm -f /usr/local/bin/darkbloom

darkbloom stop --uninstall (provider-swift/Sources/darkbloom/StopCommand.swift) disarms the watchdog first, deletes ~/Library/LaunchAgents/io.darkbloom.watchdog.plist and io.darkbloom.provider.plist, and disables both labels in launchd. darkbloom unenroll (provider-swift/Sources/darkbloom/UnenrollCommand.swift) opens System Settings → Device Management (macOS does not let a binary remove a profile) and, after confirmation or with --force, deletes ~/.config/darkbloom/, ~/.darkbloom/auth_token and the legacy key files. Remove the # Darkbloom PATH line from your rc file by hand. Model weights live in ~/.cache/huggingface/hub; delete them with darkbloom models remove <id> before removing the CLI.

Related