CLI
cli/conduit is the command-line client for the panel — a thin layer over the same HTTP
API the dashboard uses, plus the console proxy for live terminals. Zero dependencies,
plain Node ≥ 20 (the live console wants ≥ 22 for the native WebSocket). Nothing here
talks to the cluster directly; if the panel can't do it, the CLI can't either.
Setup#
alias conduit="node ~/Coding/conduit/cli/conduit" # or symlink it into your PATH
conduit context add production --url http://<panel>:3001
conduit auth login # shows a code → approve it in the panel at /approve
conduit statusSign-in is browser-approved: auth login shows a short code, you approve it on the
panel's /approve page while signed in there (passkey/TOTP protected), and the CLI
receives a personal token — no password ever touches a terminal. Tokens land in
~/.config/conduit/config.json (mode 600), one per context —
conduit context add staging --url … and --context staging (or context use) switch
clusters, kubectl-style. A token minted in the panel UI works too: conduit auth token cu_….
Commands#
| Command | What it does |
|---|---|
status | nodes, containers, memory, players online |
groups / tasks / instances | fleet state — list, inspect, scale, software updates (tasks update --rolling), motd, resync, lifecycle actions (instances restart-mc 204) |
blueprints | deployable templates, named by software (+ Purpur/Folia/CanvasMC flavor), category, and the default role; blueprints get <id>. A server's actual role is a separate, editable task property (proxy/lobby/smp/game/hub/web/db/generic). |
deploy <blueprint> | create a server from a blueprint (the /deploy wizard on the CLI): --group, --mode, --min/--desired/--max, --node-group|--node, --version, resource flags |
nodes | cluster nodes — status, Conduit-container count, CPU/memory load |
node-groups | named node sets for placement — list, create <label> --nodes a,b, delete <id> |
players | roster, Player 360 lookup, ban/mute (+undo), kick |
console <vmid> | live terminal on any instance over the console proxy — input, colors, reconnect-free attach; --send "cmd" for one-shots |
logs | console scrollback (--follow tails over SSE), action log, player audit, operator audit |
alerts | alert feed, incidents, channel test fire |
backups | snapshot list, on-demand backup, guarded restore |
maintenance | downtime windows — list, schedule (--start +30m), cancel |
plugins / packs | managed plugin + resource pack inventory |
doctor | path diagnostics from your machine: panel, auth, console proxy, every agent — with fixes |
shell | interactive console — history, tab completion, same commands |
completion bash|zsh | shell completion |
conduit help <command> prints usage. Automation flags work everywhere: --json (raw
API output), --yes (skip confirmations), --quiet, --context <name>. Mutations that
can hurt (restarts, restores, bans, broadcasts) always confirm unless --yes.
Console#
conduit console 204 # attach; Ctrl+C detaches
conduit console 204 --send "list" # one-shot
conduit console 206 --sock redis # per-kind consoles: mc, redis, postgres, mysql, web…The CLI attaches through the same per-replica console proxy (:8801) the web terminal
uses; the agent token never leaves the panel. When several nodes exist it probes which
agent actually serves the vmid — pin with --agent <node-ip> to skip the probe.
Doctor#
conduit doctor checks the path from your machine: panel HTTP, token validity, console
proxy, every agent's :8800. It knows the difference between a dead service and a dead
path — a VIP that answers in-cluster but not from your workstation is reported as a
route/VPN issue, with the replica-address workaround, instead of a false outage.
SSH gateway — the resident console#
The console also lives ON the panel CTs, behind a dedicated gateway sshd (default port
2202, network.sshGateway.port; expose it publicly with a router/firewall DNAT to the
VIP). It is not remote shell access — every connection lands in the Conduit console and
nowhere else:
ssh -p 2202 conduit@<panel-vip>- Registered key (added on
/account, or trusted earlier): straight into the console as your panel user, on a 12-hour session token ("ssh session" under your API tokens). Key checks are live — revoking on/accounttakes effect on the next connection, no sync delay. - Any other key: the username sign-in — two factors. Enter your panel name and
password (factor one, verified before anything else happens — a wrong password
creates no request at all, and it's hard rate-limited). Then the second factor: either
approve in the browser on
/approvewhile signed into the panel, or type your authenticator code right in the terminal (TOTP accounts). After signing in, the gateway offers to trust the key so the next connection is direct. - No key at all (a phone, a locked-down client): the same username + password sign-in, reached over keyboard-interactive instead of a key — a key is only a shortcut past the password + second factor, never a way around them.
- Approving is deliberate: the approval prompt (the
/approvepage and the live popup) shows you who is connecting — source IP, country, key fingerprint — but never the code. To approve you type the code from your own terminal, so a sign-in you didn't start can't be one-click-approved (denying it is one click). And never a system shell: however you arrive you land in the Conduit console and nowhere else — the gatewayrestricts the session (no forwarding, no sftp), runs its own host keys on its own port, and the account it uses is denied outright on the system sshd at :22.
SSH over 443 — for networks that reset the SSH port#
Some networks (hotel/corporate/mobile DPI) reset connections to a non-standard SSH port.
Turn on SSH over 443 in the gateway card (Domains → SSH · CLI gateway; needs a hostname
set) and the gateway also answers on :443, right beside the panel web. An nginx stream
ssl_preread split reads the first bytes of each :443 connection and routes it: a TLS
handshake with a panel hostname's SNI → the web; the gateway hostname's SNI → the sshd; and
anything that isn't TLS at all → the sshd too. So :443 carries the panel, raw SSH, and
TLS-wrapped SSH at once, and a port-based reset can't tell your SSH from HTTPS.
ssh -p 443 conduit@cli.example.com # any SSH client, nothing to install
# if the port itself is inspected, wrap SSH in real TLS so it's indistinguishable from https:
ssh -o ProxyCommand='openssl s_client -quiet -verify_quiet -connect %h:443 -servername %h 2>/dev/null' conduit@cli.example.com
# socat works too: ProxyCommand='socat - OPENSSL:%h:443,verify=0,snihost=%h'The wrap uses TLS purely as camouflage — verification is off because the origin serves a
Cloudflare Origin cert (trusted by CF, not publicly), and SSH's own host key is what actually
secures the session inside. The gateway hostname is a DNS-only record straight to the
origin (SSH can't ride the orange-cloud proxy), so :443 there reaches the sshd, not CF.
Admins on a gateway session have one escape hatch inside the console: system shell,
role-checked against the panel and confirmed before it opens. Everyone else operates
through the console commands — that's the point.
The console#
conduit shell (what a gateway session lands in) is the CloudNet-school console. On a
roomy terminal it's a split: a pinned header with your identity and live fleet counts, a
LOG PANE up top that scrolls on its own (command output and alert events land there), and a
fixed command line at the bottom with a live tab-completion menu — so typing never flickers
the log and a streaming line never disturbs what you're typing. Short terminals fall back to
a sticky inline prompt. screen <vmid> (alias of console) attaches a real server console
and detaches back with Ctrl+C; top runs the full-screen live view and hands the prompt
back; clear wipes the log; exit leaves (and releases the terminal so a gateway session
actually closes).
Live view#
conduit top (also watch/monitor) is the k9s-style glance: nodes with cpu, fleet
totals, players, and every instance with state/players/tps, refreshing on an interval.
q leaves — back to the console prompt when you came from the shell.
Notes#
- API tokens inherit your role and group scope; a read-only token turns every mutation command into a clean 403.
- Credential-vault operations (reveal/rotate) need sudo re-auth, which is session-only — those stay in the panel by design.
- The CLI's API surface is the panel's public HTTP API (docs/api.md); anything missing there is missing here too.