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 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.
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 ...` — 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.
Source Files
¶
- apps.go
- builtins.go
- cluster.go
- cluster_init.go
- config.go
- connect.go
- depart.go
- detach_unix.go
- discovery_wiring.go
- docs.go
- git.go
- jobs.go
- kubectl.go
- main.go
- mcp.go
- mcpclient.go
- outbound.go
- peers.go
- peers_help_mint_invite.go
- pool.go
- remote.go
- repair.go
- repair_register.go
- repair_remote_binary.go
- restart.go
- rollback.go
- run.go
- scan.go
- shell.go
- ssh.go
- ssh_dial.go
- ssh_interactive.go
- ssh_tree.go
- status.go
- trace.go
- unpair.go
- upgrade.go
- upgrade_apply.go
- upgrade_history.go
- version.go