#!/usr/bin/env bash # pct-run — Run commands inside Proxmox CTs by CT ID only. No IPs needed. # Usage: pct-run [command...] # If no command given, opens a shell. # # Source of truth: Proxmox pct config on each node. # No hardcoded IPs. No prose contract drift. set -euo pipefail # ── CT ID → PVE Node mapping (maintained HERE, not in prose contracts) ── declare -A CT_NODES=( # amdpve (192.168.68.15) [100]=amdpve # abiba [105]=amdpve # kagentz [111]=amdpve # tdunna [112]=amdpve # tanko [113]=amdpve # baggy [115]=amdpve # scottdenya # hwepve (192.168.68.4) — Huawei Matebook 16 [114]=hwepve # mumuni (migrated from minipve 2026-07-20) # minipve (192.168.68.12) [104]=minipve # authentik [110]=minipve # gitea [116]=minipve # syslog-api # storepve (192.168.68.6) [106]=storepve # ra-h-os [107]=storepve # proxmox-backup [108]=storepve # media [117]=storepve # zulip # acerpve (192.168.68.9) [102]=acerpve # adguard # ocupve (192.168.68.5) — no CTs (bare metal GPU .110) # # REMOVED CTs (migrated to bare metal, decommissioned, or VMs): # 101 llm-gpu → bare metal 192.168.68.8 (RTX 3090) # 103 ocu-llm → bare metal 192.168.68.110 (RTX 5070) # 109 docker-vm → KVM VM 192.168.68.7 (use direct SSH) # 118 jitsi → stopped, not in service ) # Each node must be root-accessible via SSH hostname # (Ensure ~/.ssh/config or /etc/hosts has these resolvable) # ── PVE node → IP (needed only because SSH needs an address) ── declare -A NODE_IPS=( [amdpve]=192.168.68.15 [minipve]=192.168.68.12 [storepve]=192.168.68.6 [acerpve]=192.168.68.9 [ocupve]=192.168.68.5 [hwepve]=192.168.68.4 ) resolve_node() { local ct_id="$1" local node="${CT_NODES[$ct_id]:-}" if [[ -z "$node" ]]; then echo "ERROR: CT $ct_id not found in mapping" >&2 exit 1 fi echo "$node" } node_ip() { local node="$1" local ip="${NODE_IPS[$node]:-}" if [[ -z "$ip" ]]; then echo "ERROR: Node $node not found in IP mapping" >&2 exit 1 fi echo "$ip" } main() { if [[ $# -lt 1 ]]; then echo "Usage: pct-run [command...]" >&2 echo " pct-run 112 cat /etc/hostname" >&2 echo " pct-run 114 systemctl status hermes-gateway" >&2 echo "" echo "Known CTs:" >&2 for ct in $(echo "${!CT_NODES[@]}" | tr ' ' '\n' | sort -n); do echo " CT $ct → ${CT_NODES[$ct]}" >&2 done exit 1 fi local ct_id="$1" shift local node node=$(resolve_node "$ct_id") local ip ip=$(node_ip "$node") if [[ $# -eq 0 ]]; then # Interactive shell exec ssh -t "root@${ip}" "pct enter ${ct_id}" else # One-shot command ssh "root@${ip}" "pct exec ${ct_id} -- $*" fi } main "$@"