Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help


implementation: complete

Assistant Contracts (BYOK)

The assistant answers questions about servers using the operator's own LLM key. It exists in two places with deliberately different powers: beside a terminal, where it talks about one machine, and on the dashboard, where it talks about the estate.

Everything below follows from one rule: the model proposes, the operator disposes. Nothing the model says executes itself, except through a setting the operator turned on knowingly.

Providers are configuration, not code

Two wire protocols cover the field:

ProtocolEndpoint shapeAuth
anthropicPOST {base}/v1/messagesx-api-key + anthropic-version
openaiPOST {base}/chat/completionsAuthorization: Bearer

A vendor is a base URL and a model name, so the dashboard ships presets rather than the server shipping adapters: Anthropic and Z.AI (whose compatible endpoint speaks the Anthropic shape) use the first; OpenAI, OpenRouter, vLLM and ollama use the second. Adding a vendor that speaks either shape needs no code.

The key

Stored in the operator's own SQLite file, next to the API token, on a tool that binds loopback only. Three rules keep it there:

  • GET /api/v1/assistant/config returns a view — every setting except the key, plus has_api_key so the form can say "저장됨".
  • A PUT that omits the key keeps the stored one. The dashboard never receives the key, so it cannot echo it back, and saving an unrelated setting cannot wipe the secret.
  • The audit row for a settings change records endpoint, model and flags — never the key.

Host assistant

Beside the terminal. Context is assembled from what the operator allows, each with its own switch:

  • host facts and the latest metrics (send_metrics)
  • the terminal screen, last 2000 characters, read from xterm in the browser (send_terminal)
  • recent commands from exec_log (send_exec_history)

The reply may carry one command, marked by a CMD: line the server strips out. The dashboard types it onto the shell prompt without a newline — the operator's Enter is what runs it.

auto_run (off by default, migration 0009) removes that keystroke. The server decides per reply: auto-run applies only when the setting is on AND a command was actually suggested, so the dashboard cannot run anything on its own initiative, and the run happens once when the answer arrives rather than from a view closure that re-renders.

Fleet assistant

On the dashboard, collapsed until asked for. Context is a snapshot: fleet counts, every host's state with its tags and flags, and what is firing. Down hosts sort first, so truncation never hides exactly what the question is about.

A fleet answer may carry a plan — a CMD: line plus a HOSTS: line naming targets. The plan is rendered, never executed:

  • Asking and running are separate endpoints (POST /assistant/fleet and POST /assistant/fleet/run). The run request restates the hosts, so what executes is what the dashboard showed, not whatever the model said last.
  • auto_run deliberately does not reach here. "Run this after I read it" and "run this on twenty servers" are different decisions, and the second always asks.
  • Targets are checked against the catalog. Invented aliases come back in unknown_hosts — a misunderstanding is shown rather than quietly shrinking the run — and the run endpoint refuses unknown, disabled or stale hosts outright rather than skipping them.
  • Fan-out is capped: 50 hosts per run, 8 at a time, a per-host timeout. Every host's run lands in exec_log through the same path a single exec uses, so a batch is as auditable as a keystroke.

Failure modes

Errors map to the status a caller can act on: 412 when the assistant is off or has no key (fixable in settings), 404 for an unknown host, 502 when the LLM endpoint itself failed, 400 for an empty or oversized question. A timeout is 60 seconds — long enough for a real answer, short enough not to hold a tab open forever.

Surfaces

  • GET/PUT /api/v1/assistant/config
  • POST /api/v1/hosts/{alias}/assistant
  • POST /api/v1/assistant/fleet, POST /api/v1/assistant/fleet/run
  • Dashboard: the fleet panel; host detail: the strip under the terminal; /settings: the single home for provider, key and context switches.
  • MCP: none. An agent already has the tools the assistant would call, and proxying one model through another buys nothing.