API & CLI

The hostssh CLI, REST API, and MCP server — four surfaces, one core. Verbs, --json output, cron/CI usage, and AI-agent tools.

API & CLI

HostSSH exposes one core through four surfaces: the dashboard you click, the hostssh CLI you script, the REST API you integrate, and the hostssh-mcp MCP server an AI agent drives. They are not four codebases — they are four front doors onto the same agent + control-plane core.

The parity principle

Dashboard, CLI, MCP, and REST all call the same control-plane + agent core — anything you can click, you can script, automate, or ask an agent to do.

This is the "complete control" the product promises: no operation is dashboard-only. The CLI is the substrate (the agent is the binary); the REST API is the control-plane surface underneath the dashboard; the MCP server is the agent-facing wrapper over both. Pick the surface that fits the caller — a human at a terminal, a CI job, a billing system, or Claude. See Control surfaces for the full five-surface map (Web-SSH is the cross-cutting "get me onto the box" layer).

Every surface enforces the same two guardrails:

  • License gating — privileged verbs (capture, restore, clone, migrate, transfer, provision) refuse to run without a valid license. Verification is offline-capable, online-authoritative. See Licensing.
  • Emergency restore is never gated — restoring an existing image works even on an expired or revoked license, across every surface. A backup you can't restore isn't a backup, and we never hold your data hostage.

The hostssh CLI

The agent is the CLI — a single static Go binary. Full feature parity with the dashboard, scriptable, and the layer everything else calls. Install it with the one-line installer.

Verbs

VerbWhat it does
hostssh captureCapture this server to an encrypted .hsi image (on-demand or scheduled)
hostssh restoreRestore an image in place — roll a box back to a prior snapshot
hostssh cloneDuplicate a server to another machine (e.g. staging from prod)
hostssh migrate --new-ipRelocate to a fresh VPS with automatic IP rewrite (Mode 3)
hostssh transfer --new-keyMint a one-time, scope-locked transfer key for a peer pull (Mode 2)
hostssh provisionSpin up a fresh VPS via a provider driver and prepare the agent
hostssh ssh <server>Jump to any fleet server through the gateway with an ephemeral key
hostssh statusHealth, license, last backup, disk, engine/agent version
hostssh licenseActivate, inspect, transfer, and check the license (activate/status/transfer)

The three transfer modes map to verbs directly: Mode 1 (Offline/Download) is capture + restore --image file.hsi; Mode 2 (Peer transfer) is transfer --new-key on the source then pull on the target; Mode 3 (Managed migration) is migrate --new-ip. See Backups & recovery for the human walkthrough.

Machine-readable output (--json)

Every verb takes --json for stable, versioned, parseable output — the contract that makes cron and CI safe. Human tables are for humans; --json is for scripts.

hostssh status --json
{
  "server": "app-01",
  "license": { "tier": "business", "valid": true, "expires_at": "2027-01-31T00:00:00Z" },
  "last_capture": { "image": "hsi_8f3a", "at": "2026-06-20T03:00:11Z", "bytes": 4831838208 },
  "storage": { "provider": "r2", "bucket": "gm-fleet-backups", "reachable": true },
  "engine": "1.4.0", "agent": "1.4.0"
}

Long-running verbs (capture, restore, migrate) stream NDJSON progress events to stdout under --json, then a final result object — so a CI job can tail progress and assert on the outcome in one pass.

In cron and CI

The CLI is built to live in crontabs, runbooks, and pipelines. Nightly DR, for example:

# /etc/cron.d/hostssh-nightly — capture at 03:00, alert on failure
0 3 * * * root hostssh capture --json >> /var/log/hostssh/capture.log 2>&1 || \
  hostssh notify --event capture.failed

A CI gate that proves recoverability on an ephemeral target (the "a backup you haven't restored isn't a backup" drill) reads cleanly with --json piped to jq:

set -euo pipefail
IMAGE=$(hostssh capture --json | jq -r '.image')
hostssh migrate --new-ip --image "$IMAGE" --target ephemeral --provider railway --json \
  | tee migrate.ndjson
jq -e 'select(.event=="verify") | .ok == true' migrate.ndjson

Exit codes are stable: 0 success, non-zero on failure, with a distinct code for license-denied (12) so a pipeline can tell "the backup failed" from "the license lapsed." Pair the CLI with Connections (BYOK) so your storage and provider credentials are already wired.


The REST API

The control-plane API underneath the dashboard — for integrations that aren't a person at a terminal: WHMCS billing, CI/CD, customer automation, and the reseller/provisioning flows. It mirrors the CLI verbs one-to-one.

Authentication

  • mTLS — clients pin the control-plane cert; the agent ⇄ control-plane channel is mutually authenticated (same posture as license heartbeat and telemetry).
  • Scoped API keys — each key carries an org, a permission set, and an optional server scope. Keys are least-privilege by default; a WHMCS integration key that can provision need not be able to restore. Mint and revoke keys in the control-plane dashboard (the same RBAC catalog the UI uses, fail-closed).
Authorization: Bearer hssk_live_…        # scoped API key
X-HostSSH-Org: org_8f3a                   # org context

Endpoints (v1)

The action endpoints mirror the verbs; the resource endpoints back the fleet, image, and license views.

VerbMethod + path
capturePOST /v1/servers/{id}/capture
restorePOST /v1/servers/{id}/restore
clonePOST /v1/servers/{id}/clone
migratePOST /v1/servers/{id}/migrate (body: new_ip, target)
transferPOST /v1/images/{id}/transfer (mints a transfer key)
provisionPOST /v1/servers (body: ResourceSpec — size/region/provider/ephemeral)
statusGET /v1/servers/{id} · GET /v1/servers (fleet)
licenseGET /v1/license/status · POST /v1/license/transfer
backup statusGET /v1/servers/{id}/images · GET /v1/images/{id}

Connections and retention live alongside (/v1/connections, /v1/retention) — see Connections §7 for that surface. Provider selection on provision/migrate is by capability, not hard-coded vendor — the control plane picks the driver (Hostinger, Hetzner, DO, Vultr, AWS, Railway, generic-SSH) from the ResourceSpec and policy. See Provisioning.

Example — capture a server

curl -X POST https://api.hostssh.com/v1/servers/app-01/capture \
  -H "Authorization: Bearer hssk_live_…" \
  -H "X-HostSSH-Org: org_8f3a" \
  -H "Content-Type: application/json" \
  -d '{ "target": "r2", "async": true }'
{ "job_id": "job_a91c", "image": null, "state": "running",
  "stream": "https://api.hostssh.com/v1/jobs/job_a91c/events" }

Long operations return a job with an SSE/NDJSON stream URL; poll GET /v1/jobs/{job_id} or subscribe to the stream for live progress (the same event shape the dashboard's migration wizard renders).

Example — provision + migrate (Mode 3, WHMCS-style automation)

curl -X POST https://api.hostssh.com/v1/servers/app-01/migrate \
  -H "Authorization: Bearer hssk_live_…" \
  -H "X-HostSSH-Org: org_8f3a" \
  -d '{
        "image": "hsi_8f3a",
        "new_ip": true,
        "target": { "provision": true, "capability": "full_vps", "region": "eu", "size": "kvm4" },
        "dns_cutover": true
      }'

The control plane provisions the VPS, installs the agent, restores brain + DBs + volumes, rewrites the IP, re-issues TLS, optionally flips DNS, and ends with a verification report. The whole flow is one call — the same one the dashboard makes.


The MCP server — hostssh-mcp

hostssh-mcp is an MCP server that exposes HostSSH operations as tools, so AI agents (Claude, etc.) drive the fleet conversationally — "back up the prod box," "clone .211 to a fresh Hostinger VPS and move the IP," "show backup health for all servers." It runs embedded in the agent (local control) and in the control plane (fleet-wide), authenticated by a license token + scoped permissions. This is on-brand for an AI-native ops audience and dogfoods how the Girard Media fleet is already operated.

Tools

ToolMaps toDestructive?
capturehostssh capturewrites an image
restorehostssh restore⚠️ yes — confirmation required
clonehostssh cloneprovisions/overwrites a target — ⚠️ confirm
migratehostssh migrate --new-ip⚠️ yes — confirmation required
provisionhostssh provision⚠️ spends money — confirm
ssh_exechostssh ssh + command⚠️ runs a command on the host
list_servershostssh status (fleet)read-only
backup_statusimage/backup healthread-only
license_statushostssh license statusread-only

Guardrails

  • License-guarded — every tool checks the license before acting; the same gating as the CLI and REST. list_servers, backup_status, and license_status are read-only and safe to call freely.
  • Confirmation on destructive opsrestore, clone, migrate, provision, and ssh_exec require an explicit confirmation token (or a human-in-the-loop approval) before they mutate anything or spend money. An agent cannot relocate a box or stand up a paid VPS on a single under-specified sentence.
  • Structured I/O — tools take and return typed, structured payloads (the --json shapes), so the agent's reasoning stays grounded in real state rather than scraped text.

Example — tool call and result

A backup_status call across the fleet:

{ "tool": "backup_status", "arguments": { "scope": "fleet", "stale_after_hours": 26 } }
{ "servers": [
    { "server": "app-01", "last_capture": "2026-06-20T03:00:11Z", "fresh": true,  "tier": "business" },
    { "server": "data-01", "last_capture": "2026-06-18T03:00:09Z", "fresh": false, "tier": "business" }
  ],
  "stale": ["data-01"] }

A guarded migrate — note the required confirmation:

{ "tool": "migrate", "arguments": {
    "server": "app-01", "image": "hsi_8f3a", "new_ip": true,
    "target": { "provision": true, "provider": "hostinger", "region": "eu" },
    "confirm": "migrate app-01 hsi_8f3a"
  } }

Without the matching confirm string the tool returns a confirmation_required error describing exactly what would change — never a silent relocate.


Choosing a surface

You are…Use
A person doing one-off opsthe dashboard or the CLI
A cron job / runbook on the boxthe CLI with --json
A CI pipelinethe CLI (exit codes) or REST (jobs + streams)
A billing/automation system (WHMCS, reseller)the REST API with a scoped key
An AI agenthostssh-mcp

All four hit the same core, enforce the same license + emergency-restore rules, and return the same structured shapes. Build against whichever fits — and trust that the others behave identically.

Status: the CLI verbs and --json contract are the stable surface (Phase 1). The REST API and hostssh-mcp track the same core; exact endpoint paths, the job/stream envelope, and the full MCP tool schema are firming up alongside the control plane and may shift before v1 — treat the shapes here as the intended contract, not a frozen reference. See Architecture §6 for the stack underneath.