Skip to content

About

Sovereign Hybrid Compute (SHC) API toolkit — Python client, VM lifecycle, cloud lab integration

Resources

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

SHC Toolkit

Python client, CLI, and provisioning toolkit for Sovereign Hybrid Compute (SHC) — the quickest way to spin up a VM and manage it from the command line or Python.

Disclosure: The SHC link above is an affiliate link. If you sign up through it, we receive a 5% recurring commission (grandfathered rate) on your spending, at no extra cost to you. We use SHC as the CI backend for our open-source projects and genuinely recommend the service.

Related Projects

What you get

  • shc CLI — order, manage, and snapshot VMs from the command line
  • Python client — SHCClient wraps the full SHC User API
  • NoDNS integration (experimental) — publish DNS records via Nostr events (kind 11111). May be unreliable; use shc nodns --ip <ip> separately from IaC tools.
  • Certbot DNS-01 — get Let's Encrypt certs without opening port 80
  • Provisioning helpers — one-call setup for Caddy + HTTPS on a fresh VM

Install

pip install -e .

Requires Python 3.11+.

Quick Start

AI agents: read "Operating VMs with the shc CLI" in AGENTS.md before ordering. It covers the size-name trap, the order/pay two-step, the payment confirmation gate, and per-VM SSH key commands.

export SHC_API_KEY="shc_live_..."

shc catalog
shc order --hostname my-vm --package-id 23 --pricing-id 55 \
  -o 108=50 -o 126=debian13-cloud -o 167=none \
  --ssh-key ~/.ssh/id_ed25519.pub --pay --verify-reachability
shc list
shc info <service_id>
shc cancel <service_id>

--verify-reachability waits for the VM to come up and polls TCP/22; if it never opens, the VM is cancelled immediately (prorated refund) and the command exits 1 — no paying for an unroutable box. shc pay <invoice_id> is spend-gated: it explains by default and pays with --confirm. The full ephemeral-VM loop (order → run → exfiltrate → close, with receipts): docs/campaign-lifecycle.md.

Zones & Facilities (read before ordering)

Catalog lines live in different physical facilities, and reachability differs per route (earned 2026-09-01: two fresh ssd orders were dead on arrival from an EU route — port 22 closed, cloud-init bootstrap never fired — while a Katy hdd order provisioned SSH-reachable in under a minute):

Line Facility (module group) Notes
nvme-* Katy, Texas (g4) default recommendation
hdd-* Katy, Texas — HDD (g8) same facility, spinning disk
ssd-* Cherryvale, Kansas (g7) unreachable from EU routes 2026-09-01
dev-* Cherryvale, Kansas (g7) same facility as ssd
shc sizes                 # every size with its facility + reachability flag
shc sizes --available     # + live stock probe (~20 API calls)
shc stock                 # live stock across the catalog, in-stock first
shc order --size nvme-4c-16gb --reap 8h ...   # Katy line — no guard

Notes:

  • shc order REFUSES flagged facilities by default (fail-early, 2026-09-04): an order into ssd-*/dev-* exits 1 before submitting — those VMs go billing-active and then never attach to the network (issue #39, still failing 2026-09-04), so waiting out the provisioning timeout cannot help. Debugging the zone deliberately? --allow-unstable-zone opts in (or --dry-run, which only previews), and scripts/dev-zone-probe.py carries the long-wait knobs (--timeout, --net-timeout). The same guard covers shc contextvm orders.
  • shc order --hostname x-reap8h --reap 8h no longer double-tags; the existing tag wins and the CLI says so.
  • provisioning_state may stay "provisioning" forever on a healthy VM — the real health signal is SSH reachability (port 22).
  • shc reinstall requires a STOPPED VM (shc stop → reinstall → start); the CLI prints the exact sequence on conflict.

Python Library

from shc_toolkit import SHCClient

c = SHCClient()
vms = c.list_vms()
c.start_vm(123)
c.create_snapshot(123, name="pre-deploy")

NoDNS + Let's Encrypt DNS-01 (Experimental)

⚠️ Experimental: NoDNS integration may be unreliable. It is a Python-only provisioning feature, not part of the Terraform or Pulumi providers. Use shc nodns --ip <ip> separately from IaC workflows.

NoDNS maps Nostr keypairs to .nodns.shop subdomains. You publish DNS records as kind 11111 Nostr events, and the nodns-bot pushes them to Knot DNS. No domain registration, no DNS provider account.

Architecture

Your machine
  └── nostr-sdk → publishes kind 11111 events to Nostr relays
                    ↓
              nodns-bot → Knot DNS (ns1.nodns.shop)
                    ↓
              Public DNS: <npub>.nodns.shop A <your-vm-ip>

Publish DNS from Python

from shc_toolkit.nodns import NoDNSKeyPair, publish_dns_records

kp = NoDNSKeyPair.generate()
print(f"Domain: {kp.fqdn}")
print(f"Save this nsec to update records later: {kp.nsec}")

publish_dns_records(kp, [
    {"type": "A", "name": "@", "value": "<your-vm-ip>", "ttl": 300},
])

Publish DNS from CLI

shc nodns --ip <your-vm-ip>

Full pipeline: VM → DNS → HTTPS

The provisioning module automates the complete flow on a fresh Debian VM:

from shc_toolkit.client import SHCClient
from shc_toolkit.nodns import NoDNSKeyPair, publish_dns_records
from shc_toolkit.provision import install_caddy, get_cert_dns01

# 1. Order a VM (or use an existing one)
c = SHCClient()
vm = c.get_vm_summary(123)
ip = vm["ips"][0]["ip"]

# 2. Publish A record via NoDNS
kp = NoDNSKeyPair.generate()
publish_dns_records(kp, [{"type": "A", "name": "@", "value": ip, "ttl": 300}])

# 3. Install Caddy + get Let's Encrypt cert via DNS-01
install_caddy(ip)
get_cert_dns01(ip, kp.fqdn, keypair_path="/tmp/nodns-keypair.json")

For the certbot auth hook to work, save the keypair and deploy nodns_vm.py to the VM first. See shc_toolkit/provision.py for details.

Verified end-to-end

$ curl https://npub1mv7l45exqsu5nr5tnefkr33ruhzjj4r8prg6qtcedv4lyf3rzguqptuwm4.nodns.shop
Hello from NoDNS + Let's Encrypt DNS-01!

Certificate issued by Let's Encrypt via DNS-01 through nodns. No port 80, no traditional DNS provider, no domain registrar.

SHC API Reference

The SHC User API has full documentation available at:

This toolkit covers the most common endpoints (VM lifecycle, ordering, snapshots, billing). For operations not yet wrapped (reinstall, backups, etc.), use SHCClient._request() directly or open a PR.

VM Console (noVNC)

API-native console access — see a VM's screen without SSH (works even when the guest is broken, keyless, or mid-bootstrap):

shc console 1077                       # availability check
shc console-session 1077               # single-use noVNC URL (default 30s TTL)
shc console-session 1077 --ttl 300     # 5-minute session

The response carries a console_url (JWT-authenticated Proxmox bridge) — open it in a browser immediately; tokens are single-use. Confirmation is auto-handled (_confirmed_request). For scripted typing/screenshots inside the console, see shc_toolkit/console.py (Playwright-based portal automation: ConsoleSession().open_console() → type_text() → screenshot()).

MCP Transport

The toolkit supports dual transport: REST v2 (default) or MCP Streamable HTTP. Both transports are fully functional for all operations including reads, writes, spend/destructive actions with confirmation flow, and upgrades.

The flagship SHC MCP server at https://mcp.sovereignhybridcompute.com/ exposes 157 tools over Streamable HTTP (the curated everyday subset is 35 ops marked x-shc-core in the spec). The toolkit's TOOL_MAP wraps 157 of the 157 (100% of all MCP-exposed ops; 35/35 = 100% of the x-shc-core curated subset). Every spend and destructive op is confirm-gated with automatic Idempotency-Key generation and X-User-Api-Confirm handling.

CI tests randomly select REST or MCP per run, ensuring both transports receive equal coverage over time without doubling test cost.

Using MCP transport

from shc_toolkit import create_client

# Auto (defaults to REST — change with SHC_TRANSPORT=mcp)
c = create_client(transport="auto")

# Force MCP
c = create_client(transport="mcp")

# Force REST (default)
c = create_client(transport="rest")

Or via environment variable:

export SHC_TRANSPORT=mcp   # or 'rest' or 'auto'

Install the MCP optional dependency:

pip install shc-toolkit[mcp]

Both transports implement the same SHCTransport interface — your code works unchanged regardless of which backend is selected.

Config Option IDs

Option IDs differ by VPS line. Always read them from shc catalog or GET /ordering/catalog.

For NVMe Starter (package_id 23):

Option ID Example values
RAM 106 4096 (base), 8192, 16384
CPU 107 1 (base), 2, 4
Disk 108 8 (base), 32, 50, 100
IPv4 109 1 (base), 2, 4
Template (NVMe/HDD) 126 see the live list below
Template (Dev VPS) 174 same list
GUI 167 none, gnome, kde, xfce, cinnamon, mate

Live template list (32, verified 2026-09-11 via get_config_options — the old 11-entry list here was badly stale): almalinux10-cloud, almalinux9-cloud, alpine323-cloud, arch-cloud, cs10-cloud, debian12-cloud, debian13-cloud, devuan5-cloud, fedora42-cloud, fedora43-cloud, freebsd14-cloud, gentoo-cloud, kali-cloud, netbsd10-cloud, nixos-cloud, ol10-cloud, ol9-cloud, openbsd79-cloud, opensuse-leap156-cloud, rocky10-cloud, rocky9-cloud, ubuntu2204-cloud, ubuntu2404-cloud, ubuntu2604-cloud, win11-pro-byol, win2022-byol, win2022-core-byol, win2025-byol, win2025-core-byol, firecracker-cloud, openwrt-cloud, pve-ve-cloud (Proxmox VE). Not every template accepts the same addons — read live values from shc catalog / GET /ordering/catalog before ordering.

Values are the value field from the catalog, not value_id.

Windows BYOL

Windows is available bring-your-own-license on all VPS lines: Windows Server 2022/2025 (Core or Desktop) and Windows 11 Pro. Windows Server requires >=32GB disk; Windows 11 requires >=64GB disk. Apply your own license after first boot.

GUI requirements

GUI requires >=16GB disk AND >=4GB RAM.

Ephemeral GitHub Actions runners

Disposable per-job CI runners on cheap SHC VPSs. The toolkit orders a VPS, bootstraps the GitHub Actions runner over SSH with --ephemeral, registers it with a unique per-run label, and the workflow destroys the VM in an if: always() teardown job when the benchmark finishes.

Live-measured cold-start: 135.8 s end-to-end on dev-4c-16gb ($0.90/day, ~$0.01 per run prorated). Workload execution matches ubuntu-latest per-shard. Full perf comparison and the Firecracker cold-start reduction target live in docs/github-ephemeral-runners.md.

Firecracker PoC validated: nested KVM works on SHC Dev VPS, μVMs boot in ~2 s vs the 100 s VPS scheduling floor — 50× faster per job, 150× throughput at parallelism 4. Pool-mode architecture and live benchmark numbers in docs/firecracker-pool-mode.md.

export SHC_API_KEY="shc_live_..."
export SHC_GITHUB_ADMIN_TOKEN="ghp_..."   # PAT with repo admin / runners:write

shc github-runner provision \
  --repo Amperstrand/tollgate-module-basic-go \
  --labels shc-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}
shc github-runner destroy --service-id "$SERVICE_ID"
# optional controller-dead failsafe: the VM cancels ITSELF N minutes after
# boot via an on-VM systemd timer + bounded full-scope key
# (SHC_SUICIDE_KEY secret, or SHC_ACCOUNT_EMAIL/PASSWORD for a per-run
# 1-day mint — only full scope can cancel). See shc_toolkit/selfdestruct.py
shc github-runner provision --self-destruct-minutes 120 …

Intended audience: OSS maintainers with expensive / slow / custom CI who want pay-per-minute runners on machines they control.

Zero-GUI Onboarding — shc register

Start from nothing — no SHC account, no browser, one Lightning payment. The first top-up invoice opens as a QR in your browser by default; pay it with any Lightning wallet and the account is live.

pip install -e ".[register]"

shc register                     # THE getting-started path: generates
                                 # everything, opens the browser QR page,
                                 # waits for your payment — done
shc register --interactive       # prompts for email/amount/context name
shc register --amount 5.00 --no-browser   # terminal QR + URL instead
shc topup --context default      # re-fund an existing account later
shc mail                         # read the account's nomail inbox
shc mail --send-to a@b.c --subject hi --text "..." --cashu-token cashuB...

What happens (unattended): nsec generated locally → anonymous POST /register with generated password/names (hard API minimums) → full-scope API key minted over HTTP Basic → Nostr key linked (NIP-98 kind-27235) → BTCPay top-up invoice served on a stable local URL (http://127.0.0.1:8923/ by default; QR + lightning: link, auto-refreshes across invoice reissues) → polled until paid → context saved to ~/.config/shc/contexts/<name>.json (0600: email, password, client_id, api_key, nsec).

Any shc command with no key configured will offer the same wizard (TTY only; disable with SHC_NO_REGISTER=1 or --no-register).

Multiple accounts (profiles): aws/gcloud-style named profiles — run shc register once per account. Identities live at ~/.config/shc/profiles/<name>.json (0600) and are identified by npub (the nsec is the account identity: portable to any Nostr tool, and the recovery/mail key for that account).

shc profile list            # accounts by npub, * marks the active one
shc profile use eddy-e2e    # persist the active account
shc profile show            # full credential set of the active account

Resolution precedence (aws-style): --api-key flag > SHC_PROFILE env > SHC_API_KEY env > active profile. --context <name> still works as an alias; legacy contexts.json and register contexts migrate lazily and non-destructively.

Referral note: /register takes no referral field (attribution is web-session based). Our affiliate link stays in this README; an npub…@nomail.name address strongly implies tool registration.

Known Limitations

  • Nested KVM: Available ONLY on Dev VPS plans (pkg 80–84, Cherryvale, KS). NVMe/SSD/HDD VPS plans do NOT expose VMX/SVM to guests — QEMU runs in TCG (software emulation) only. Empirically verified 2026-07-20: NVMe Starter (pkg 23, Katy-TX) probed via SSH — grep -c 'vmx|svm' /proc/cpuinfo = 0, /dev/kvm absent. Verify after ordering with grep -E 'vmx|svm' /proc/cpuinfo or shc kvm-check <service_id>.
  • Dev zone scheduler hang (issue #28) — RESOLVED: Dev VPS plans (pkg 80–84, Cherryvale, KS) previously failed to provision (scheduler never assigned an IP). Recovered and verified 2026-08-25 via scripts/dev-zone-probe.py: pkg 80 provisioned in 85s (debian12) and 102s (debian13) — confirming the old "debian13 deadlock" (#24) was purely the zone scheduler, not the template. Nested-KVM workloads (Dev plans only) are available again.
  • Hourly proration: You're charged the full daily rate at order time, but get refunded for unused hours when you cancel (minimum 1 hour charge). A 2-hour session on a $0.49/day plan costs ~$0.04. Industry terms: cancel = terminate/delete (billing ends); stop = pause — which on SHC still bills the full daily price. See docs/iac-lifecycle.md.
  • Locations: Katy, Texas (NVMe/SSD/HDD VPS) and Cherryvale, Kansas (Dev VPS plans only).
  • API key lifecycle: API keys expire after 90 days (max 730). A 401 on a working key means it expired — mint a new one at /account/api-keys. Maximum 25 active keys per account.
  • Snapshot/backup limit: All VPS plans (including Dev VPS) support 1 snapshot and 1 backup concurrently (snapshot_limit: 1, backup_limit: 1 per package). Verified working on Dev VPS via front-door E2E (2026-07-01).

Cashu Tollgate — SSH for ecash

A pay-per-minute SSH server that accepts Cashu ecash tokens as payment. Users paste a token as their SSH username and get an interactive bash shell for as many minutes as the token is worth. Includes a static faucet page for minting free test tokens.

→ Full documentation

⚠️ Warning: This creates ephemeral shell accounts on your server. Do not run on production infrastructure without understanding the security implications. Consult your IT department before running at work.

License

MIT

Auto-Generated Client

The toolkit includes an auto-generated Python client (shc_toolkit.generated) with 100% API endpoint coverage (148 endpoint modules, 729 attrs models) generated from the OpenAPI spec via openapi-python-client.

pip install shc-toolkit[generated]  # installs httpx + attrs
from shc_toolkit.generated import Client
from shc_toolkit.generated.api.ordering import get_ordering_catalog

client = Client(base_url="https://blesta.sovereignhybridcompute.com/user-api/v2",
                headers={"Authorization": "Bearer shc_live_..."})
catalog = get_ordering_catalog.sync(client=client)

The generated client provides type-safe raw API calls. For convenience features (retry with jitter, cost tracking, confirmation flow, MCP transport), use SHCClient instead — it wraps the generated layer and adds production niceties. The hand-written SHCClient returns raw dict / list[dict]; users who want typed responses should reach for shc_toolkit.generated directly.

Regenerate from the latest spec: bash scripts/generate_client.sh

Pulumi Support

The recommended way to use SHC from Pulumi is via the Terraform Bridge — no separate Python provider needed:

# Build the TF provider
go build -o terraform-provider-shc .

# Generate a Pulumi SDK from the TF provider
pulumi package add terraform-provider ./terraform-provider-shc --language python

The generated Pulumi SDK includes all resources (Vm, Snapshot, Backup, FirewallRule, Rdns) + the term attribute (v2.4.3 VM term management).

Lifecycle semantics through the bridge (industry-aligned terminology): pulumi destroy / terraform destroy → terminate (SHC: immediate cancel + prorated refund, billing ends) — never a mere stop; power_state = "stopped" maps to GCP's desired_status pattern and pauses without destroying, but unlike AWS/GPC a stopped SHC VM keeps billing its full daily price. Deletion protection = SHC's server-side confirm-gate + Terraform-native prevent_destroy. Full mapping and reasoning: docs/iac-lifecycle.md and terraform-provider-shc/docs/lifecycle-alignment.md.

→ Migration guide from shc-pulumi

Changelog

→ CHANGELOG.md — full version history in Keep a Changelog format.

Testing Status

v2.4.24.3 (llms-full.txt corpus audit)

  • Unit suite (network-isolated by a conftest fixture that blocks all real HTTP; grows with the API surface — pytest --collect-only -q prints the current count) — incl. nostr operate-lane grant exchange, BIP21 stitch table, invoice-polling path regression
  • Corpus audit (2026-08-26, llms-full.txt v2.4.15): confirm-gate, gated/routine op split, register, MCP 157/157 — conformant in both repos; nostr operate-lane + BIP21 stitching added; jit_pay polling f-string bug fixed
  • API: v2.4.24 (148 paths, 177 operations, no spec drift); live smoke verified

v2.4.24.0 Release

  • Unit suite (network-isolated, zero flakes across 5 consecutive runs at release time)
  • mypy type checking: 0 errors (17 source files; generated/ excluded)
  • Cross-repo parity: 5/5 checks pass (size map, feature matrix, resolve_addons contract, billing claims, Dev VPS claims)
  • API: v2.4.24 (148 paths, 197 schemas, 177 operations); live MCP server exposes 157 tools; curated x-shc-core subset is 35; TOOL_MAP wraps 157 entries (100% of all MCP-exposed ops)
  • API resilience: 408 retry, exponential backoff with ±20% jitter, auto-generated Idempotency-Key on all confirmed requests
  • Generated typed client: 932 files, 148 endpoint modules, 729 attrs models
  • CI: dispatch/tag/monthly workflows (unit+smoke+integration, OpenAPI+MCP drift, cross-repo parity, typecheck, coverage, security, ansible, reaper, publish) + auto-issue-creation on drift
  • Ansible: ansible-lint CI + molecule caddy scenario + monthly live E2E

MCP Transport (Verified 2026-06-30)

  • READS: All working (getAccount, getBillingBalance, listVirtualMachines, getOrderingCatalog)
  • WRITES: All working (createVirtualMachineOrder with confirmation flow, cancelVirtualMachine)
  • CI: Randomly selects REST or MCP per run for equal coverage

NoDNS (Experimental — may be unreliable)

  • Code is complete (keypair generation, event publishing, DNS verification)
  • Not actively tested: Published A record may or may not resolve correctly
  • shc nodns --ip <ip> --zone nodns.shop publishes, shc order --nodns auto-publishes after VM creation

ContextVM (Verified 2026-07-01)

  • Bootstrap code complete and tested on NVMe VPS (Debian 13, Katy TX)
  • VM becomes discoverable MCP server on Nostr via wss://relay.contextvm.org

About

Sovereign Hybrid Compute (SHC) API toolkit — Python client, VM lifecycle, cloud lab integration

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages