Documentation
¶
Overview ¶
`outpost connect <host>` is the CLI mirror of the Periscope launcher's "Connect" button: it runs the once-per-idle-window OS-password step that unlocks the host for subsequent SSH connections. POSTs to cloudbox's /h/:host/elevate endpoint, captures the returned matrix_elev cookie, and caches it on disk so later `outpost ssh-proxy` invocations (both human and agentic) can ride on it until idle / absolute expiry.
Daemon-side wiring for the LAN-direct SSH listener and the LAN peer discovery surfaces. Kept in a separate file so `outpost start`'s already-long main flow doesn't grow another ~150 LOC inline.
Both helpers are no-ops when the relevant FileConfig field is empty/off — opt-in by design (privacy + reduced attack surface for a default install).
`outpost jobs / fg / bg / kill` are the external job-control commands the matrix shell points users at when they try to run `fg`/`bg`/`jobs` in-shell — those builtins can't work because subshells in the qiangli/sh interpreter are goroutines, not real OS processes. Outpost records each detached PID via the WithBgPidCallback hook on the shell runner; these commands read that persistent registry.
Windows: see jobs_windows.go — the signal-based job-control model is Unix-specific (no SIGSTOP/SIGCONT/SIGUSR1/SIGUSR2 equivalents on Windows), so the verbs stub out to "not supported."
Command outpost runs on a home host: it pairs with the portal and surfaces local apps (web, shell, desktop, clipboard) through a tunnel.
MCP client glue for the `outpost apps|builtins|status|unpair` family of subcommands. Each subcommand is a thin wrapper that connects to the running daemon's /mcp/ endpoint with a bearer token, calls one tool / reads one resource, and pretty-prints the result.
Three ways to address the daemon (precedence high → low):
- `--host`/`--token` persistent flags on the root command.
- `--remote <name>` persistent flag, pointing at a cached entry in ~/.config/outpost/remotes/<name>.json (written by `outpost remote login <name>`).
- $OUTPOST_HOST / $OUTPOST_ADMIN_ADDR + $OUTPOST_MCP_TOKEN env variables.
- Implicit local: 127.0.0.1:17777 + bearer from the local FileConfig (mode 0600, same OS user). Only this last path requires that the daemon is on the same host as the CLI.
Daemon-not-running: the HTTP dial fails with a friendly error. The dedicated `--offline` flag (on a few mutate subcommands) bypasses MCP entirely and writes the FileConfig directly via admincore — useful for installer scripts.
`outpost outbound …` is the CLI mirror of the admin UI's Outbound section. It drives the running local outpost's admin-UI HTTP API on 127.0.0.1:17777 — it does NOT reimplement the elevate/pinger logic. All commands assume the local outpost is already running (`outpost start`) and paired; the binary will print a friendly hint if the admin UI is unreachable.
Auth model: each invocation reads ~/.cache/outpost/admin.cookie. If missing or expired (1h TTL, wiped on outpost restart), commands print "run `outpost outbound login` first" and exit non-zero. Login itself prompts for the LOCAL OS password (same gate as the admin UI's login page); `connect` prompts for the REMOTE host's OS password (the one cloudbox's elevate endpoint will verify).
`outpost peers` — Wave 3B.1 surface for the reachability ledger and temporal-observation model.
outpost peers history [--json --limit N] Shows the most-recent reachability-ledger entries (one per successful SSH dial). Each row records who we reached, how (transport + endpoint), the handshake latency, and when. outpost peers predicted [--json --hour H] Shows the EWMA presence prediction per peer at the given hour-of-week (0..167; default = current hour). Useful for "is my office printer likely up right now?" and (in Wave 3B.2) the scheduling decisions the daemon makes for pre- warming connections.
Both commands read the on-disk files directly (no MCP roundtrip) since the data is local. Daemon doesn't need to be running.
`outpost pool status` is the operator's read-only view of this outpost's LLM-pool participation. Inspects the persisted FileConfig + probes the locally-configured Ollama daemon directly — does not talk to the running outpost process (no admin-UI login dance, no session cookie). For live watcher state (last push time, in-flight counter), open the admin UI; this CLI is for "is the pool wired correctly?" sanity checks and scripting.
`outpost reach [user@]host` — one-shot reachability probe with a machine-readable verdict ("lan" | "cloudbox" | "offline") and a stable exit code (0 | 10 | 20). Scripts call it before deciding whether to dial LAN-direct or fall through cloudbox.
The probe deliberately stops before the SSH handshake so it's both fast (<2 s) and side-effect-free (no elevation cookie required, no /auth password challenge). LAN classification proves the peer's announced LAN endpoint is currently accepting connections; cloudbox classification proves the matrix portal is reachable from this machine; offline classification means neither path is currently usable.
`outpost remote {login,logout,list}` caches the bearer token + admin endpoint for outposts on other machines so the CLI can target them with `outpost --remote <name> apps stop foo` instead of piping $OUTPOST_HOST / $OUTPOST_MCP_TOKEN on every invocation.
Cache layout (mode 0600, same OS user only):
~/.config/outpost/remotes/<name>.json
{ "addr": "host.local:17777", "token": "<bearer>" }
Names are arbitrary aliases — typically the LAN hostname, but nothing here interprets them. Token is the value of the remote outpost's FileConfig.MCPBearerToken (printed by `outpost mcp endpoint` on that machine).
`outpost repair` — peer-assisted recovery flows.
Wave 3A.1 ships two narrow recipes that compose existing primitives (`outpost ssh exec`, `outpost ssh sftp get`, `outpost upgrade --local`):
outpost repair cloudbox-url --from <peer> Prints the peer's cloudbox base URL so the operator can re-register against the right server when the local config is damaged. Tier-2 (peer must be a configured SSH target). outpost repair binary --from <peer> [--out PATH] Fetches the peer's outpost binary, validates by exec'ing `<candidate> version --json`, swaps it in atomically via the existing upgrade Worker, restarts the daemon. Tier-2.
Both flows go through the SSH client (`outpost ssh exec` / `outpost ssh sftp get`), so the peer must already be an `outpost ssh add`-ed alias. For LAN-direct repair after a corrupt config, add a `--direct` target by IP first.
Phase 2 (Wave 3A.2) adds `outpost repair register` for peer-relayed re-registration with cloudbox.
`outpost run --label X -- <cmd>` is the supported alternative to `launchctl submit`, which silently no-ops inside the matrix-shell because the SSH session inherits a launchd system-domain context that doesn't have `submit` capability (see docs/matrix-shell-deferred-bugs.md #8).
What this verb does: generate a LaunchAgent plist for the operator's command, bootstrap it into the per-user `gui/<uid>` domain via `launchctl bootstrap`, and persist the plist under ~/Library/LaunchAgents so it auto-loads at next login too. Pair with `outpost run --list` (show outpost-managed jobs) and `outpost run --remove <label>` (bootout + delete plist).
macOS only — `launchctl bootstrap` doesn't exist on Linux/Windows. The verb is registered regardless so the help text shows the recipe on every platform, but RunE errors out early on non-darwin so operator scripts get a clear message instead of a cryptic exec failure.
`outpost scan` and `outpost discover probe <url>` — Wave 3A.1 CLI surfaces for LAN peer discovery.
scan — broadcasts an mDNS query for `_outpost._tcp.local`,
prints a table of responding peers (Tier 1).
discover — performs a full hello → probe exchange against a known
probe URL and prints the result with the trust state.
Both commands work without the daemon being paired and without any MCP roundtrip — they're operator tooling that runs in the terminal. The MCP equivalents (outpost_scan_peers, outpost_probe_peer) live in tools_discovery.go for agentic callers.
`outpost scp [user@]host:src dst` — download remote file to local. `outpost scp src [user@]host:dst` — upload local file to remote.
Drop-in for the `scp` command (single file only — see "Out of scope" at the bottom). Reuses the same dial path as `outpost ssh` so LAN-direct + peer-ticket auth kicks in automatically when the peer is reachable on mDNS; otherwise falls back to the cloudbox tunnel. Passwordless after the first `outpost connect`.
`outpost shasum [user@]host:path` — print the sha256 of a remote file in `shasum -a 256` output format ("<hex> <path>"), so it pipes / diffs against the system tool cleanly:
diff <(outpost shasum host:/opt/bin/foo | awk '{print $1}') \
<(shasum -a 256 ./foo | awk '{print $1}')
Rides the same SFTP subsystem `outpost scp` uses; stream-reads the remote file into sha256.New() so no temporary download is materialized.
SSH client-side helpers: `outpost ssh-proxy` (used as an SSH ProxyCommand to bridge a local `ssh` invocation to the remote outpost over the matrix tunnel) and `outpost ssh-config` (prints ~/.ssh/config stanzas for hosts visible to this account).
Shared dial helpers for the interactive/tunnel/sftp paths in `outpost ssh ...`. The MCP-driven `outpost ssh exec` goes through admincore (which has its own chain dial); the interactive verbs can't easily route through MCP (Shell needs the local terminal), so they dial cloudbox directly using the same primitives.
We deliberately do NOT duplicate admincore.dialSSHChain — that method depends on an admincore.Server and runs daemon-side, where EAUTHREQUIRED maps to a 401 APIError. The CLI path can be smarter: when an interactive TTY is available we prompt for the OS password in-process via runConnect, recovering the elev cookie without requiring the operator to break out and run `outpost connect`.
Interactive verbs in the `outpost ssh ...` tree: connect (shell), tunnel (-L), and sftp (file transfer). These dial cloudbox directly via ssh_dial.go's dialSSHTargetChain helper rather than routing through MCP — Shell + SFTP + persistent tunnel listeners don't fit the per-call MCP roundtrip shape.
The MCP `outpost_ssh_exec` tool stays in tools_ssh.go for agentic callers that want structured one-shot exec; this file is the human- operator path.
`outpost ssh [user@]<host> [cmd...]` — drop-in `ssh` invocation that prefers a LAN-direct connection (with cloudbox-issued peer-ticket auth) over the cloudbox-tunneled path. Designed for agentic callers that want a passwordless SSH after the first `outpost connect`.
Flow:
- Parse [user@]host.
- mDNS browse for `host` (match on AgentName / AssignedHostname).
- Cookie lifecycle: read cached matrix_elev; if missing, runConnect.
- If a LAN peer with `lan-ssh-ws` endpoint is found: - Trade cookie at cloudbox for a peer ticket. - Verify peer's mDNS-advertised host-key fingerprint after the SSH handshake (defense against LAN MITM that races mDNS). - WSS dial directly to the LAN endpoint with PeerTicket.
- Otherwise fall back to the cloudbox-tunneled path (existing behavior of `outpost ssh-proxy`).
- Run interactive shell or exec the provided command.
`outpost ssh ...` — the self-sufficient SSH subcommand tree.
Wave 1 surface (this file): `list`, `add`, `rm`, `show`, `exec`. All five route through the local daemon's /mcp/ endpoint and call the matching `outpost_*_ssh_target` / `outpost_ssh_exec` tool, mirroring how `outpost apps` / `outpost outbound` work today.
Wave 2 will add `connect` (interactive shell), `sftp`, `tunnel`, and the `outpost ssh <name>` shorthand for connect.
Backwards compat: this is additive — existing `ssh-proxy` and `ssh-config` commands stay. Operators with hardcoded ~/.ssh/config stanzas keep working; new operators get the in-process path.
`outpost sshd` — standalone LAN SSH server on port 2222.
A drop-in (user-space) replacement for the system sshd, serving the same in-process SSH server the daemon mounts at /ssh and on FileConfig.SSHListenAddr — interactive shell, exec, SFTP (scp), port-forwarding — but as a foreground one-shot command that needs NO daemon, NO pairing, and NO cloudbox/internet connectivity.
Use cases:
- A machine without sshd enabled (default macOS, most Windows boxes) that you want to reach from another machine on the LAN with the stock `ssh`/`scp` commands.
- Bootstrapping: the target machine has only the outpost binary; run `outpost sshd` there, then drive the setup from a laptop with `ssh -p 2222 <user>@<ip>` or `outpost ssh <user>@<ip>:2222` — no internet required.
Cloudbox is strictly optional: when the host happens to be paired, the server picks up the same extras the daemon's LAN listener gets (paired-peer direct-tcpip allowlist, cloudbox-tunneled `ssh -J` second hops). Unpaired, those simply stay off and everything else works.
Auth is the same OS-password gate as every other outpost SSH surface: the username must be the OS user running `outpost sshd`, verified via PAM / dscl / LogonUserW. There is no cloudbox vouching on this listener — every connection answers the password challenge.
Source Files
¶
- apps.go
- build.go
- builtins.go
- cluster.go
- cluster_init.go
- config.go
- connect.go
- depart.go
- detach_unix.go
- discovery_wiring.go
- docs.go
- doctor.go
- git.go
- git_verbs.go
- jobs.go
- kubectl.go
- main.go
- mcp.go
- mcpclient.go
- outbound.go
- peers.go
- peers_help_mint_invite.go
- pool.go
- reach.go
- remote.go
- repair.go
- repair_register.go
- repair_remote_binary.go
- restart.go
- rollback.go
- run.go
- scan.go
- scp.go
- service.go
- service_linux.go
- service_unix.go
- shasum.go
- shell.go
- ssh.go
- ssh_dial.go
- ssh_interactive.go
- ssh_runtime.go
- ssh_tree.go
- sshd.go
- status.go
- supervisord.go
- supervisord_watchdog.go
- trace.go
- unpair.go
- upgrade.go
- upgrade_apply.go
- upgrade_history.go
- version.go