outpost

command
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 62 Imported by: 0

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):

  1. `--host`/`--token` persistent flags on the root command.
  2. `--remote <name>` persistent flag, pointing at a cached entry in ~/.config/outpost/remotes/<name>.json (written by `outpost remote login <name>`).
  3. $OUTPOST_HOST / $OUTPOST_ADMIN_ADDR + $OUTPOST_MCP_TOKEN env variables.
  4. 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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL