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
| Verb | What it does |
|---|---|
hostssh capture | Capture this server to an encrypted .hsi image (on-demand or scheduled) |
hostssh restore | Restore an image in place — roll a box back to a prior snapshot |
hostssh clone | Duplicate a server to another machine (e.g. staging from prod) |
hostssh migrate --new-ip | Relocate to a fresh VPS with automatic IP rewrite (Mode 3) |
hostssh transfer --new-key | Mint a one-time, scope-locked transfer key for a peer pull (Mode 2) |
hostssh provision | Spin 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 status | Health, license, last backup, disk, engine/agent version |
hostssh license | Activate, 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
provisionneed not be able torestore. 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.
| Verb | Method + path |
|---|---|
| capture | POST /v1/servers/{id}/capture |
| restore | POST /v1/servers/{id}/restore |
| clone | POST /v1/servers/{id}/clone |
| migrate | POST /v1/servers/{id}/migrate (body: new_ip, target) |
| transfer | POST /v1/images/{id}/transfer (mints a transfer key) |
| provision | POST /v1/servers (body: ResourceSpec — size/region/provider/ephemeral) |
| status | GET /v1/servers/{id} · GET /v1/servers (fleet) |
| license | GET /v1/license/status · POST /v1/license/transfer |
| backup status | GET /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
| Tool | Maps to | Destructive? |
|---|---|---|
capture | hostssh capture | writes an image |
restore | hostssh restore | ⚠️ yes — confirmation required |
clone | hostssh clone | provisions/overwrites a target — ⚠️ confirm |
migrate | hostssh migrate --new-ip | ⚠️ yes — confirmation required |
provision | hostssh provision | ⚠️ spends money — confirm |
ssh_exec | hostssh ssh + command | ⚠️ runs a command on the host |
list_servers | hostssh status (fleet) | read-only |
backup_status | image/backup health | read-only |
license_status | hostssh license status | read-only |
Guardrails
- License-guarded — every tool checks the license before acting; the same gating as
the CLI and REST.
list_servers,backup_status, andlicense_statusare read-only and safe to call freely. - Confirmation on destructive ops —
restore,clone,migrate,provision, andssh_execrequire 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
--jsonshapes), 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 ops | the dashboard or the CLI |
| A cron job / runbook on the box | the CLI with --json |
| A CI pipeline | the CLI (exit codes) or REST (jobs + streams) |
| A billing/automation system (WHMCS, reseller) | the REST API with a scoped key |
| An AI agent | hostssh-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
--jsoncontract are the stable surface (Phase 1). The REST API andhostssh-mcptrack 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.