Server Schema

Server configuration defines SSH server connections stored in servers/<id>.json.

Schema

json
{
  "id": "string",
  "name": "string",
  "host": "string",
  "port": number,
  "user": "string",
  "identity_file": "string",
  "kind": "string",
  "auth": {
    "mode": "key_controlmaster | key_plus_password_controlmaster",
    "control_path": "string",
    "persist": "string",
    "persist_source": "configured | migrated | legacy_default (engine-owned output)"
  },
  "runner": {
    "workspace_root": "string",
    "homeboy_path": "string",
    "daemon": boolean,
    "concurrency_limit": number,
    "artifact_policy": "string",
    "require_exact_homeboy_version": boolean,
    "require_fresh_runtime_overlay": boolean,
    "policy": {
      "snapshot_excludes": ["string"]
    },
    "env": {},
    "secret_env": {},
    "resources": {}
  },
  "forward_agent": boolean
}

Fields

Required Fields

  • id (string): Unique server identifier
  • name (string): Human-readable server name
  • host (string): Server hostname or IP address
  • user (string): SSH username for authentication

Optional Fields

  • port (number): SSH port (default: 22)
  • identity_file (string): Path to SSH private key file for authentication
  • kind (string): Optional server classification for extensions and project-specific behavior
  • auth (object): Optional SSH authentication/session policy
  • runner (object): Optional runner capability for Homeboy execution on this server
  • forward_agent (boolean): Enable SSH agent forwarding (default: false)

Example

json
{
  "id": "production",
  "name": "Production Server",
  "host": "example.com",
  "port": 22,
  "user": "deploy",
  "identity_file": "/Users/dev/.ssh/id_rsa",
  "kind": "password-gated",
  "auth": {
    "mode": "key_plus_password_controlmaster",
    "control_path": "~/.ssh/controlmasters/%h-%p-%r",
    "persist": "4h"
  },
  "runner": {
    "workspace_root": "/home/deploy/Developer",
    "homeboy_path": "/usr/local/bin/homeboy",
    "daemon": false,
    "concurrency_limit": 4,
    "artifact_policy": "copy",
    "policy": {
      "snapshot_excludes": ["generated-state", "generated-state/**"]
    },
    "env": {},
    "secret_env": {},
    "resources": {}
  },
  "forward_agent": true
}

Runner Capability

Use homeboy runner enable <server_id> to make an SSH server runner-capable. The server ID is also the runner ID, matching the common runner-machine model: one machine, one server, one runner-capable server.

Runner Environment

Use runner.env for non-secret values that are safe to show in diagnostics, such as public artifact URLs:

json
{
  "runner": {
    "env": {
      "HOMEBOY_PUBLIC_ARTIFACT_BASE_URL": "https://artifacts.example.test"
    }
  }
}

Use runner.secret_env for values that must be read at execution time instead of stored in plaintext config. Each key is the environment variable exposed to the runner process. The value references either an environment variable visible to the Homeboy process on the runner or a file on the runner machine:

json
{
  "runner": {
    "secret_env": {
      "OPENAI_API_KEY": { "env": "OPENAI_API_KEY" },
      "SERVICE_TOKEN": { "file": "~/.config/homeboy/secrets/service-token" }
    }
  }
}

Homeboy rejects likely credential names in newly persisted runner.env entries. Use secret_env for those values; it is also the explicit sensitivity declaration for non-obvious names. Existing legacy entries can be inspected without values using homeboy runner doctor <id> --scope secret-env and migrated into OS keychain-backed references with --repair. Homeboy command output redacts sensitive names in env and keeps secret_env as references only. Secret file contents and referenced environment variable values are not printed by runner config/status diagnostics.

Lab Offload Safety Gates

Two runner settings decide whether detected drift warns or refuses a Lab offload. Both default to unset, which means warn-and-proceed, and both have a per-run environment override:

SettingEnv overrideDefault (unset)When true
require_exact_homeboy_versionHOMEBOY_REQUIRE_EXACT_RUNNER_VERSIONPatch drift within the same MAJOR.MINOR proceeds with a warning; MAJOR/MINOR drift refusesThe controller↔runner version gate requires a byte-identical Homeboy version on the runner
require_fresh_runtime_overlayHOMEBOY_REQUIRE_FRESH_RUNTIME_OVERLAYA stale runtime-overlay build warns on stderr and the offload runs the old buildA runtime overlay whose built artifact is provably behind its source checkout refuses the offload
json
{
  "runner": {
    "require_exact_homeboy_version": true,
    "require_fresh_runtime_overlay": true
  }
}

Precedence for both is the same: a truthy environment value (1, true, yes, on) escalates that single run regardless of configuration; anything else — including a falsy value, which is indistinguishable from "not set for this run" — falls through to the runner setting, whose unset default is false. The environment variables are escalation overrides only, so neither can relax a runner configured strict.

require_fresh_runtime_overlay escalates only builds proven stale. An overlay whose freshness cannot be established — no containing checkout, no artifacts, or an unreadable source history — is reported as unknown and never refused. Freshness itself is derived from the artifact’s newest file mtime against the containing checkout’s history, which is an indication rather than a proof: a freshly created checkout rewrites mtimes and makes every artifact in it look newly built. Set this on runners where silently executing a stale build is worse than a failed dispatch.

Managed SSH Sessions

Key-authenticated servers can keep an explicitly bounded authenticated session available for unattended work:

json
{
  "auth": {
    "mode": "key_controlmaster",
    "control_path": "~/.ssh/controlmasters/%h-%p-%r",
    "persist": "8h"
  }
}

Run homeboy server connect <server_id> while the key is available. Later commands attach to that control socket instead of requesting another key signature. This is useful for hardware-backed or interactive key agents that cannot sign while the controller session is locked.

Servers that accept a key and then require an operator-entered password can opt into managed control-master reuse:

json
{
  "auth": {
    "mode": "key_plus_password_controlmaster",
    "control_path": "~/.ssh/controlmasters/%h-%p-%r",
    "persist": "4h"
  }
}

Homeboy never stores the password. Run homeboy server connect <server_id> to establish the interactive session, then later homeboy ssh, file transfer, deploy, logs, and other server-backed commands reuse the active SSH control master.

auth.persist is required when adding a managed-session policy. It is an OpenSSH ControlPersist value such as 4h, 1h30m, yes, or no; colon-form values are not accepted. This is the local control-socket idle lifetime, not a policy imposed by the remote server. A shorter lifetime reduces the time an authenticated socket remains usable; a longer lifetime avoids repeated interactive authentication.

persist_source is engine-owned output, not a configuration input. configured identifies an explicit persisted operator choice. legacy_default identifies an older omitted value still using its historic 4h behavior. Updating that legacy record with an explicit persist safely records migrated. homeboy server show and managed-session output report the effective value and source.

SSH Key Management

Homeboy manages SSH keys in two ways:

Identity Files

SSH keys referenced in identity_file should exist on the local filesystem. Common locations:

  • macOS/Linux: ~/.ssh/id_rsa, ~/.ssh/id_ed25519
  • Windows: %USERPROFILE%.sshid_rsa

Keychain Integration

SSH key passphrases are stored in the OS keychain:

  • macOS: Keychain Access
  • Linux: libsecret / gnome-keyring
  • Windows: Windows Credential Manager

Homeboy automatically retrieves passphrases from the keychain when establishing SSH connections.

Storage Location

Servers are stored as individual JSON files under the OS config directory:

  • macOS/Linux: ~/.config/homeboy/servers/<id>.json
  • Windows: %APPDATA%homeboyservers<id>.json

SSH Key Generation

Generate a new SSH key pair:

bash
ssh-keygen -t ed25519 -C "[email protected]"

Copy the public key to the remote server:

bash
ssh-copy-id user@hostname

Or manually:

bash
cat ~/.ssh/id_ed25519.pub | ssh user@hostname "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

SSH Agent Forwarding

When forward_agent is enabled, SSH keys from your local agent are forwarded to the server. This allows:

  • Git operations on the remote server using your local credentials
  • Access to other servers that trust your SSH keys
  • Reduced need for keys on remote servers

Enable agent forwarding cautiously – it grants the remote server access to your forwarded SSH identities.