feat(sandbox): bundle host-service into the Blaxel image

Closed
SA
3mo ago
+65-14

Third of five. Stacked on #6497 → #6489.

Puts host-service inside the sandbox image. After this a sandbox boots a working host-service with terminals; nothing in the desktop app talks to it yet.

It works end to end

[host-service] listening on http://localhost:4879
[host-service:db] Initialized at /data/host.db, migrations from /app/drizzle
[pty-daemon] spawned pid=246 socket=/tmp/superset-ptyd-….sock daemonVersion=0.3.0

Through the private preview URL, trpc/health.check returns {"status":"ok","cloudRegistered":false,…}. cloudRegistered: false is expected — nothing registers as a host, by design.

Auth is two independent layers

Verified against the running sandbox rather than assumed:

RequestResult
no preview token401 — Blaxel's edge rejects
preview token, no PSK401 UNAUTHORIZED — host-service rejects
preview token + PSK200

So a leaked preview token alone does not grant access to protected procedures. Consequence for PR 4: cloudWorkspace.access must return the host-service secret alongside the preview token, since clients need both.

Four things the image has to get right

Each of these was a real failure found by booting the bundle, not by reading:

  1. npm pkg set type=module — the bundle is ESM; without it Node parses /app/*.js as CommonJS and dies on the first import.
  2. A resolution-only package set. The bundle marks several deps external and then imports them at module load, so they must resolve even where the code path never runs: @mastra/duckdb, @anush008/tokenizers, onnxruntime-node, libsql, @parcel/watcher, @xterm/headless. Found by booting and installing whatever it asked for until it reached env validation.
  3. Distinct build-context names. addLocalDir's third argument defaults to the source's basename, so packages/host-service/dist and packages/pty-daemon/dist both became COPY dist — silently shipping host-service's bundle as the pty daemon. Caught by reading the generated Dockerfile.
  4. /pty-daemon/node_modules symlink. The daemon is a separate process importing node-pty, but Node resolves upward from /pty-daemon/dist while the install lives in /app. Symlinked rather than installed twice, so the daemon and host-service can't diverge on the native addon's version.

Worth knowing

That resolution-only list is mostly mastra's storage/embedding stack reached through provider-auth's credential store. onnxruntime-node and duckdb are not small, and they exist purely to satisfy an import. Trimming host-service's mastra dependency would shrink both this list and the image materially — worth its own piece of work, not this PR.

Also: the Blaxel image build failed once with a generic States.TaskFailed and succeeded on retry with no changes. Second time I've seen that, so treat build failures as worth one retry before diagnosing.

Verification

typecheck 0 · lint 0 · booted from the built image in a real sandbox · pty-daemon spawned · tRPC reachable through the preview URL with both auth layers exercised.


Summary by cubic

Bundles host-service and pty-daemon into the Blaxel sandbox image so a sandbox boots a working host-service with terminals on 4879. Previously the image omitted them; now it ships their bundles and DB migrations, enforces ESM, and avoids native compilation.

  • Copies the host-service bundle and drizzle migrations into /app and the pty-daemon bundle into /pty-daemon/dist using distinct build-context names to prevent dist collisions.
  • Installs pinned native deps from the host-service package (better-sqlite3, node-pty) and a resolution-only set for externalized imports: @mastra/duckdb, @anush008/tokenizers, onnxruntime-node, libsql, @parcel/watcher, @xterm/headless; fails the build if node-pty prebuilds are missing.
  • Sets npm pkg set type=module; symlinks /pty-daemon/node_modules to /app/node_modules so both processes resolve the same native addons.
  • Requires a prebuilt host-service bundle. Action: run bun run --cwd packages/host-service build:host.
  • No desktop app wiring changes.