|
| 1 | +# The Mac bridge — a soft link between a cloud agent and a local Mac mini M4 |
| 2 | + |
| 3 | +This is the canonical, reader-friendly guide to how a **Linux cloud agent** |
| 4 | +(no Apple Silicon, no Metal) drives a **local Mac mini M4** to run everything |
| 5 | +MLX-dependent — the MLX verifier, the K3 spec-decode harness, the evidence-gate |
| 6 | +reruns — without any inbound network path to the Mac. |
| 7 | + |
| 8 | +It is a "soft link" in the literal sense: there is **no socket, no SSH, no VPN, |
| 9 | +no tunnel** between the two machines. The link is built entirely out of things a |
| 10 | +cloud agent already has — a git checkout, git push permission, and a read-only |
| 11 | +`gh` — plus the Mac's existing outbound-only GitHub Actions runner. **Git is the |
| 12 | +wire.** |
| 13 | + |
| 14 | +- **Deep-dive design** (transports M1/M2/M3, fleet-integration evaluation): |
| 15 | + [`docs/design/mac-bridge-cloud-agent-access.md`](design/mac-bridge-cloud-agent-access.md) |
| 16 | +- **Runner operator setup** (register runner, model paths, HF pre-warm): |
| 17 | + [`docs/ops/mac-m4-runner-setup.md`](ops/mac-m4-runner-setup.md) |
| 18 | +- **Implementation**: [`scripts/mac_bridge/`](../scripts/mac_bridge/), |
| 19 | + [`inference_engine/bridge/manifest.py`](../inference_engine/bridge/manifest.py), |
| 20 | + [`.github/workflows/mac-bridge.yaml`](../.github/workflows/mac-bridge.yaml) |
| 21 | + |
| 22 | +--- |
| 23 | + |
| 24 | +## 1. Why a soft link (the constraints) |
| 25 | + |
| 26 | +| # | Constraint | Consequence | |
| 27 | +| --- | --- | --- | |
| 28 | +| C1 | **No inbound path to the Mac.** It sits behind NAT, no public IP, no port-forward. | The transport must be initiated *from* the Mac, or relayed. The Mac's Actions runner already long-polls GitHub outbound-only — reuse that. | |
| 29 | +| C2 | **Cloud agents are ephemeral and git-native.** They reliably have a repo checkout, git push, read-only `gh`. They do **not** reliably have VPN keys, SSH keys, or workflow-dispatch rights. | The control channel must be plain git. Zero new secrets. | |
| 30 | +| C3 | **The Mac executes whatever lands on it.** A queue that forwards arbitrary shell to a desk machine is a remote-shell backdoor. | The command surface must be a typed **allowlist**, never free-form shell. | |
| 31 | +| C4 | **Evidence discipline.** Benchmark/eval results must pass the K3 evidence gate, not route around it. | The gate runs **on the Mac**, so a non-conforming report fails the bridge run itself. | |
| 32 | + |
| 33 | +The git-bus bridge is the only transport that satisfies **C1 + C2 with zero new |
| 34 | +infrastructure**: git is the RPC bus, the Actions runner is the executor, and the |
| 35 | +request branch is the session. |
| 36 | + |
| 37 | +## 2. Architecture at a glance |
| 38 | + |
| 39 | +``` |
| 40 | + Linux cloud agent (no Metal) GitHub Mac mini M4 (kakeya-mac-m4) |
| 41 | + ─────────────────────────── ──────── ─────────────────────────── |
| 42 | + kakeya_mac.py run self-hosted Actions runner |
| 43 | + │ --preset <name> --param k=v [self-hosted, macOS, ARM64, |
| 44 | + │ kakeya-mac-m4], outbound-only |
| 45 | + ▼ |
| 46 | + request_run.py |
| 47 | + • branch AgentMemory/mac-bridge-<preset>-<nonce>-<suffix> |
| 48 | + • write .mac-bridge/request.json (the manifest) |
| 49 | + • git push ───────────────────────► push event |
| 50 | + │ on: push |
| 51 | + │ branches: mac-bridge/** , |
| 52 | + │ AgentMemory/mac-bridge-* |
| 53 | + ▼ |
| 54 | + mac-bridge.yaml ──long-poll──► runner picks up the job |
| 55 | + concurrency: mac-bridge │ |
| 56 | + (one Mac, never cancel) ▼ |
| 57 | + run_preset.py --manifest … |
| 58 | + • validate vs PRESET allowlist |
| 59 | + (typed, bounded params) |
| 60 | + • execute the preset's FIXED argv |
| 61 | + (no manifest string hits a shell) |
| 62 | + • K3 reports → evidence gate ON-device |
| 63 | + │ |
| 64 | + push results ◄──────────────────────────────┘ |
| 65 | + (commit .mac-bridge/logs/ + |
| 66 | + results/research/*.json back |
| 67 | + to the SAME branch; also |
| 68 | + upload as run artifacts) |
| 69 | + ▲ │ |
| 70 | + │ git fetch / gh run view (read-only) │ |
| 71 | + fetch_results.py ◄─────────────────────────── |
| 72 | + • poll until the result commit appears |
| 73 | + • read logs + result JSON from the branch |
| 74 | +``` |
| 75 | + |
| 76 | +Latency profile: **~10 s dispatch + queue + workload runtime**. This is the right |
| 77 | +shape for test / eval / bench cycles (minutes-scale); it is deliberately *not* an |
| 78 | +interactive debugger (that is the queued M2 tailnet-SSH transport — see the design |
| 79 | +doc). The fix for "too slow" is never to widen the command surface. |
| 80 | + |
| 81 | +## 3. The five components |
| 82 | + |
| 83 | +| Component | Where | Role | |
| 84 | +| --- | --- | --- | |
| 85 | +| **Client** (`kakeya_mac.py`, `request_run.py`, `fetch_results.py`) | cloud agent | stdlib-only front door: `doctor` / `run` / `status`. Branches, writes the manifest, pushes, polls, returns the worktree to the original branch. | |
| 86 | +| **Manifest + allowlist** (`inference_engine/bridge/manifest.py`) | shared library | The security contract: the set of allowed presets, their fixed argv templates, and the typed/bounded params. Unit-tested at 100 % on the Linux CI gate. | |
| 87 | +| **Request manifest** (`.mac-bridge/request.json`) | the request branch | The serialized RPC: preset name + validated params + a nonce. | |
| 88 | +| **Executor** (`mac-bridge.yaml` → `run_preset.py`) | Mac runner | Triggered by the push; materializes LFS checkpoints, validates the manifest against the allowlist, runs the preset's fixed argv, evidence-gates K3 reports, commits results back. | |
| 89 | +| **Runner** (`[self-hosted, macOS, ARM64, kakeya-mac-m4]`) | the Mac mini | The Apple-Silicon executor. Outbound-only; the same trust shape as any self-hosted Actions runner. | |
| 90 | + |
| 91 | +## 4. Security model |
| 92 | + |
| 93 | +- **Command surface = the preset allowlist.** Each preset is a *fixed* argv list; |
| 94 | + no user-controlled manifest string is ever interpolated into a shell. `pytest-path` |
| 95 | + is the only path-taking preset and it constrains the path to a repo-relative |
| 96 | + `tests/` prefix. Params are **typed and bounded** at manifest-validation time — |
| 97 | + before any process starts: |
| 98 | + - `n_samples ≤ 50`, `max_new_tokens ≤ 512`, `block_size ≤ 16`. |
| 99 | + Machine-local facts (verifier/model paths) come from the **runner's environment** |
| 100 | + (`KAKEYA_MAC_VERIFIER_PATH`, `KAKEYA_MAC_DRAFTER_ID`, `KAKEYA_MAC_FTHETA_DIR`), |
| 101 | + never from the manifest. |
| 102 | +- **Trigger surface = push permission on `mac-bridge/**` (or `AgentMemory/mac-bridge-*`).** |
| 103 | + This is the *same* population that can already run code on the runner via the |
| 104 | + `needs-mac-m4` PR label (`integration.yaml`). The bridge does **not** widen *who* |
| 105 | + can run code on the Mac — it widens *what can be conveniently requested* while |
| 106 | + **narrowing** it to an allowlist. |
| 107 | +- **Result integrity.** Results are commits on the request branch — reviewable, |
| 108 | + attributable, and evidence-gated before they merge anywhere. |
| 109 | +- **Resource protection.** `concurrency: mac-bridge` serializes the single Mac |
| 110 | + (never cancels a running job); each preset carries its own `timeout-minutes`, |
| 111 | + with a hard job cap (150 min) above it. |
| 112 | + |
| 113 | +## 5. Quickstart |
| 114 | + |
| 115 | +### Cloud-agent side — zero install, two commands |
| 116 | + |
| 117 | +The client is stdlib-only; a fresh cloud agent needs no configuration beyond what |
| 118 | +it already has (repo checkout, git push, read-only `gh`). |
| 119 | + |
| 120 | +```bash |
| 121 | +# 0. Sanity-check THIS environment (push rights, gh, bridge files): |
| 122 | +PYTHONPATH=.:sdks/python python3 scripts/mac_bridge/kakeya_mac.py doctor |
| 123 | + |
| 124 | +# 1. Run a preset on the Mac and wait for the result: |
| 125 | +PYTHONPATH=.:sdks/python python3 scripts/mac_bridge/kakeya_mac.py run \ |
| 126 | + --preset mlx-env-probe --wait 600 |
| 127 | + |
| 128 | +# 1b. A bench preset with bounded params: |
| 129 | +PYTHONPATH=.:sdks/python python3 scripts/mac_bridge/kakeya_mac.py run \ |
| 130 | + --preset k3-beta-scorecard \ |
| 131 | + --param n_samples=5 --param max_new_tokens=32 --param block_size=8 --wait 5400 |
| 132 | + |
| 133 | +# 2. Poll an earlier request later: |
| 134 | +PYTHONPATH=.:sdks/python python3 scripts/mac_bridge/kakeya_mac.py status \ |
| 135 | + --branch <request-branch> |
| 136 | +``` |
| 137 | + |
| 138 | +`run` auto-detects the cloud-agent branch policy: on an `AgentMemory/<name>-<suffix>` |
| 139 | +checkout it creates the request branch as `AgentMemory/mac-bridge-<preset>-<nonce>-<suffix>` |
| 140 | +(the workflow accepts both namespaces), so the agent never leaves its allowed |
| 141 | +branch template. After pushing it returns the worktree to the original branch. |
| 142 | + |
| 143 | +### Mac-mini side — one command (operator, one-time) |
| 144 | + |
| 145 | +```bash |
| 146 | +# Existing kakeya-mac-m4 host (runner already registered): |
| 147 | +bash scripts/mac_bridge/setup_mac.sh |
| 148 | + |
| 149 | +# Fresh Mac (also installs + registers the Actions runner): |
| 150 | +bash scripts/mac_bridge/setup_mac.sh \ |
| 151 | + --runner-token <TOKEN> --repo-url https://github.com/<owner>/<repo> |
| 152 | +``` |
| 153 | + |
| 154 | +The script is idempotent and ends with a bridge self-test; a green exit means the |
| 155 | +next `mac-bridge/**` push executes. Full operator details (labels, model symlink |
| 156 | +locations, HF pre-warm, repo Actions variables) are in |
| 157 | +[`docs/ops/mac-m4-runner-setup.md`](ops/mac-m4-runner-setup.md). |
| 158 | + |
| 159 | +## 6. Current preset allowlist |
| 160 | + |
| 161 | +Authoritative source: `inference_engine/bridge/manifest.py` (the list below is a |
| 162 | +snapshot; the manifest and its unit tests are the contract). |
| 163 | + |
| 164 | +| Preset | What runs on the Mac | |
| 165 | +| --- | --- | |
| 166 | +| `mlx-env-probe` | MLX/Metal environment + ring probe — "is Apple Silicon ML healthy, which versions" | |
| 167 | +| `mlx-backend-tests` | `pytest tests/backends/mlx/` — real-MLX truth for the fake-MLX Linux suites | |
| 168 | +| `integration-tests` | `pytest -m integration` — the v0.3 GA gate on demand | |
| 169 | +| `pytest-path` | `pytest <path>` with the path validated against a `tests/` prefix rule | |
| 170 | +| `k3-step1-incremental` | hardened Mac harness `--incremental` (Gap-A incremental decode evidence) | |
| 171 | +| `k3-step2-fused` / `k3-step2-fused-allmlx` | hardened Mac harness `--fused-specdecode` (fused spec-decode evidence) | |
| 172 | +| `k3-fused-allmlx-code` / `k3-fused-allmlx-code-trim` | all-MLX fused spec-decode on a code workload (CUDA-trim variant) | |
| 173 | +| `k3-fused-allmlx-natural` | all-MLX fused, natural stop (NIAH) | |
| 174 | +| `k3-fused-singlefused-probe` | single-fused-graph Metal-stability probe | |
| 175 | +| `k3-beta-scorecard` | NIAH ctx280 all-MLX fused + CUDA-trim — Kakeya-vs-MLX-only scorecard | |
| 176 | +| `k3-native-baseline` | labelled native-MLX AR oracle baseline | |
| 177 | +| `k3-kv-quant-eval` | 4-bit KV-quantization evaluation | |
| 178 | +| `k3-drafter-parity` / `k3-drafter-parity-fp32` | drafter port-fidelity / acceptance parity checks | |
| 179 | +| `k3-evidence-gate` | re-validate committed K3 reports on-device | |
| 180 | + |
| 181 | +## 7. Where this is heading |
| 182 | + |
| 183 | +The git-bus bridge is **M1** of a three-tier plan. M2 adds an optional tailnet-SSH |
| 184 | +transport for *interactive* MLX debugging (one Tailscale authkey secret), and M3 |
| 185 | +folds the Mac into the ADR 0009 multi-host capability plane as a `remote-executor` |
| 186 | +TOOL capability. A key boundary, fixed by the latency analysis in the design doc: |
| 187 | +**WAN = control + tool plane; LAN = data plane** — token-level spec-decode drafts |
| 188 | +are latency-critical and must never cross the cloud↔desk boundary. See |
| 189 | +[`docs/design/mac-bridge-cloud-agent-access.md`](design/mac-bridge-cloud-agent-access.md) §4. |
0 commit comments