Skip to content

feat(ssh): auto-provision a harness-server dev backend over SSH - #149

Draft
big-guy wants to merge 1 commit into
ness-dev:feat/ssh-bootstrap-remotefrom
big-guy:ssh-auto-provision-dev
Draft

feat(ssh): auto-provision a harness-server dev backend over SSH#149
big-guy wants to merge 1 commit into
ness-dev:feat/ssh-bootstrap-remotefrom
big-guy:ssh-auto-provision-dev

Conversation

@big-guy

@big-guy big-guy commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Draft / spike stacked on feat/ssh-bootstrap-remote (PR #107's SSH bootstrap).

Extends the SSH bootstrap so Harness provisions a remote harness-server itself instead of relying on a fixed GitHub raw URL, and adds the Docker-based Linux build/run tooling to exercise it end-to-end.

SSH provisioning — Harness ships the installer, push-or-pull the server

  • The bootstrap no longer curls install-headless.sh from a hardcoded GitHub raw URL. Harness ships the installer (via extraResources) and pipes it to the remote over the existing ssh2 connection (env <prefix> sh -s), so the installer always matches the version doing the bootstrap. Resolved via app.getAppPath() (chunk-safe), not __dirname.
  • Server bytes are auto-selected:
    • upload — probe the remote platform (uname); if a matching locally-built tarball exists, putFile it to a /tmp staging dir and run the installer in HARNESS_SERVER_TARBALL mode (no GitHub round-trip; coarse upload % streamed into the progress log).
    • download — no local tarball → installer pulls a published release (HARNESS_SERVER_BASE_URL/VERSION honored).
    • Overrides: HARNESS_PROVISION_RELEASE_ONLY, HARNESS_SERVER_LOCAL_TARBALL, HARNESS_SERVER_TARBALL_DIR.
  • install-headless.sh gains a HARNESS_SERVER_TARBALL branch (skip download + version resolution, verify a sibling .sha256 if present) and locates the extracted top-level dir generically.

Opt-in cross-platform bundling for packaged builds

  • resolveBundledServerDir() (dev → release/headless/, packaged → process.resourcesPath/headless/) + a gitignored resources/headless/ staging dir wired into extraResources with a tar.gz filter, so default builds carry no extra payload.
  • scripts/stage-server-tarballs.mjs + npm bundle:servers / pack:servers.

Headless Docker tooling

  • pack-headless-linux.sh (Docker-based linux arm64/amd64 tarball build), run-headless-container.sh (a clean bare remote — no tarball copied in; provisioned by the app), reset-headless-container.sh (SSH in; stop + uninstall harness-server and wipe ~/.harness, ~/.claude, ~/.codex), and the VNC UI container helper.

Welcome screen

  • system:getBackendInfo surfaces the connected backend's host/uname/version; re-fetched per active backend so a remote shows the server's identity, not the viewer's.

Validated end-to-end: built linux arm64 + amd64 tarballs, spun up clean Ubuntu containers, and provisioned harness-server over SSH via upload mode (server installed + running + tunneled).

🤖 Generated with Claude Code

Consolidated branch work (collapsed onto base 22d025a). Headline is the
SSH auto-provisioning flow; also folds in the merged headless-docker-scripts
helpers, the welcome backend-identity feature, and the slice of main the
merge carried in.

SSH provisioning (Harness ships the installer, push-or-pull the server):
  - The bootstrap no longer curls install-headless.sh from a GitHub raw URL.
    Harness ships the installer (extraResources) and pipes it to the remote
    via `env <prefix> sh -s`, so the installer matches the version doing the
    bootstrap. Resolved via app.getAppPath() (chunk-safe), not __dirname.
  - Server bytes are auto-selected: upload mode probes the remote platform
    (uname) and, if a matching local tarball exists, putFiles it to /tmp and
    runs the installer in HARNESS_SERVER_TARBALL mode (no GitHub round-trip,
    coarse upload % streamed into the progress log); else download mode pulls
    a published release. HARNESS_PROVISION_RELEASE_ONLY / *_LOCAL_TARBALL /
    *_TARBALL_DIR override.
  - install-headless.sh gains a HARNESS_SERVER_TARBALL branch (skip download +
    version resolution, verify sibling .sha256 if present) and locates the
    extracted top-level dir generically.

Opt-in cross-platform bundling for packaged builds:
  - resolveBundledServerDir() (dev release/headless, packaged
    resourcesPath/headless) + a gitignored resources/headless staging dir
    wired into extraResources with a tar.gz filter.
  - scripts/stage-server-tarballs.mjs + npm bundle:servers / pack:servers.
    Default builds stay lean; pack:servers ships the tarballs.

Headless docker helpers:
  - pack-headless-linux.sh (Docker-based linux arm64/amd64 tarball build),
    run-headless-container.sh (clean bare remote — no tarball copied in;
    provisioned by the app), reset-headless-container.sh (SSH in, stop +
    uninstall harness-server), plus the VNC UI container helper.

Welcome screen:
  - system:getBackendInfo surfaces the connected backend's host/uname/version;
    re-fetched per active backend so a remote shows the SERVER's identity, not
    the viewer's.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@big-guy

big-guy commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

@frenchie4111 I wasn't sure what was possible, so I had to prototype it. I played around with something like this trying to get #107 working locally without a lot of fuss.

This builds on the other PR, but it adds two things:

  • The install-headless.sh is pulled from the connecting app instead of GitHub.
  • The script is changed to support a local install and a download install.
  • If the connecting app has the appropriate platform bundled with it, it can copy the tarball over.
  • If not, it tries to download from GitHub.

I think this could be simplified by moving the download entirely into the app and have it push that over. Then install-headless just always unpacks a local tarball, regardless of how it got there. OTOH, there's some advantage to have the server download it directly.

There are some build side changes to support building a bundled version of Harness, but the regular install doesn't change. My idea was that this would be a dev-only change because it makes testing easier.

WDYT?

To try this:

npm run pack:headless:linux
npm run pack:servers
scripts/run-headless-container.sh linux/amd64
open <path to locally built Harness>

Then try connecting to a backend with root@localhost:2223

This should connect, copy the tarball from Harness, install and start harness-server.

You might need to make changes to .ssh/config to make this work, but run-headless-container will give an example.

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