Forms & credentials
The Forms tab
Every discovered runnable appears on the Forms tab as a typed run dialog
generated from its runspec.toml — the operator counterpart to the agent's
tools. The dialog gives you:
- typed fields with validation (choices as dropdowns, ranges enforced, required fields marked);
- a Saved runs lane — deterministic presets (runnable + target + fixed args) that open a params-only dialog, or run immediately when they have no open params;
- optional and defaulted arguments folded into a collapsed "Default arguments" section, so the common case is a short form;
- date and date-time pickers for arguments that declare
ui = "date"/ui = "datetime"; - multi-line text fields that grow as you type (Shift+Enter for a newline);
- for runnables with subcommands: the inherited global arguments and each command's own arguments, sectioned;
- a Debug logging checkbox that runs the runnable with
--debug(DEBUG lines and full tracebacks in the output). The console setsRUNSPEC_<RUNNABLE>_ARG_DEBUG=1instead of adding--debugto the command line, so a runnable that has no debug flag (logging turned off, or a host on an older runspec) ignores it rather than failing. A runnable that declares its owndebugargument shows that field instead.
A runnable whose runspec.toml says discoverable = false (an internal
helper) gets no Forms card, isn't offered to the agent, and its runs are left
out of the History tab. A Hiding: … note beside History's Show
hidden switch names what's being left out and on which hosts; the switch (or
a click on the note) brings those runs back. To hide a runnable from the console without touching its runspec.toml,
use [tool_visibility] hidden_from_all (Settings → Agent tools); that hides
it from Forms and the agent but not from History.
In History, the Runnable column has a filter (with a search box) to show
only some runnables, and a -name word in the search box leaves out every
runnable whose name contains name (-heartbeat -sync).
Finding a runnable
The search box at the top of the tab searches the host selected in the
sidebar. Every word you type must match the runnable's name, description,
venv or one of its subcommands. Next to the box, switch between that host and
All hosts (named All in
While you search one host, a line under the box says how many matches are on other hosts, with a link that widens the search. In the all-hosts view a runnable found on several hosts is one card with an N hosts tag; clicking it asks which host to run on, then opens the form for that host. The sidebar selection does not change.
The host: and group: filters from the Marketplace work here too:
host:web keeps hosts whose name contains "web", group:prod keeps hosts in
a sidebar group whose name contains "prod". Either one always searches every
host in the profile, so group:prod restart finds restart tools across your
production hosts.
Results render as tables with raw/copy toggles. From History (or a run card's pencil), Edit & rerun reopens the same form pre-filled with the previous run's values — except secrets, which are never recorded or pre-filled.
Credentials
Named credentials let runnables authenticate to things — Jira, ServiceNow, internal APIs — without secrets ever living in config files, chat, or the audit log.
- Install the extra and add entries under Settings → Credentials (this is UI-only — the agent can use credentials but never read, create, or see their secret values):
pip install "runspec-console[credentials]"
- Secret values are stored in the OS keychain (Windows Credential
Manager / macOS Keychain / Secret Service).
runspec_credentials.tomlholds only metadata — label, kind, scope — and can therefore safely ride Config Sync; each operator supplies the secret values on their own machine once.
A credential has a kind — userpass, token, or an SSH key — and a
label. Two mechanisms apply them to runs:
Finding out what you need
Settings → Credentials reads the runnables discovered on your connected
hosts and lists, on the right, What your runnables need: one card per
service a runnable reads a credential for (Jira, Grafana, …), the names it
reads, which runnables and hosts read them, and whether a saved credential
already covers it. Each card has an Add token / Add username +
password button that opens the dialog with a label that matches, so you only
type the secret. Non-secret settings the same runnables read (JIRA_URL,
GRAFANA_ORG_ID) are listed too; set those in the venv's
Env files. A password argument that reads no env var
gets its own card, whose button adds a credential bound to that argument.
Each saved credential shows what it is used by (runnables, plus console
features such as Config Sync, MCP servers and Telemetry). One that nothing uses
is flagged, often with the label it was probably meant to have: Jira PAT
sets JIRA_PAT_TOKEN, which nothing reads, while Jira sets JIRA_TOKEN and
JIRA_PAT. While you edit a credential, the dialog shows which runnables would
pick it up. Only connected hosts are read, so connect a host (or switch to a
profile that includes it) to see its runnables' needs.
Name-derived environment variables
The credential's label derives environment variables by convention, injected into the run's environment (never into the command line):
| Label + kind | Environment variables set |
|---|---|
Jira (userpass) |
JIRA_USERNAME, JIRA_PASSWORD, JIRA_USER |
Jira Dev (userpass) |
JIRA_DEV_USERNAME, JIRA_DEV_PASSWORD, JIRA_DEV_USER |
Deploy (token) |
DEPLOY_TOKEN, DEPLOY_PAT |
The aliases (*_USER, *_PAT) mean one credential covers the common
spellings different tools expect — a credential labelled JSM works for
runspec-jsm (JSM_USER/JSM_PAT) with no per-tool configuration. Name a
credential to match what the runnable reads from its environment; select one
or more in the run dialog's credential section (the form remembers your picks
per runnable — ids only, never secrets).
One credential across many runnables — a naming convention
The derivation above becomes much more powerful when your runnables agree on
a convention. Instead of per-tool credentials, standardise on
platform-level names — say Windows and Linux — and have every
runnable declare its identity arguments with an env fallback to the
matching derived variables:
[win-service-restart]
description = "Restart a Windows service on a target machine"
[win-service-restart.args]
target = { type = "str", description = "Machine to act on" }
service = { type = "str", description = "Service name" }
username = { type = "str", env = "WINDOWS_USERNAME", description = "Windows account" }
password = { type = "password", env = "WINDOWS_PASSWORD", description = "Windows password" }
A credential labelled Windows (kind userpass) derives exactly
WINDOWS_USERNAME / WINDOWS_PASSWORD (plus the WINDOWS_USER alias) — the
label drives the names, so keep labels to the platform word. Now selecting
that one credential in any conforming runnable's run form fills its
identity: one Windows credential and one Linux credential cover the whole
catalogue. Argument resolution order still applies — a value typed directly
into the form wins over the env fallback, so one-off overrides stay easy.
The convention also travels well: ship the credential metadata in your team's Config Sync repo or a white-label config seed —
[[credential]]
id = "windows"
label = "Windows" # the label drives WINDOWS_* derivation
kind = "userpass"
[[credential]]
id = "linux"
label = "Linux"
kind = "userpass"
— and every operator's console arrives knowing the two credentials exist; each operator enters the secret values once in Settings → Credentials (secrets live only in their OS keychain, never in the synced or seeded files). The agent path gets the same derived variables, so conforming runnables work identically from chat.
Argument bindings
For runnables whose interface is arguments (--user / --password) rather
than env vars, a credential can declare username_arg / secret_arg
bindings. When you select such a credential in the form, the username
pre-fills its field (visible, editable) and the secret rides a protected
environment channel — it never appears in the form, the command line, or the
audit trail — with the bound fields badged "from credential".
Credentials and the agent
In-scope credentials apply to agent-driven runs automatically. A credential is
in scope when its host/runnable patterns match, or, if it has no scope at
all, only where the runnable declares it: an argument whose env is one of the
credential's env var names, or an argument its username_arg/secret_arg
binds. An unscoped credential is never sent to every host; set
scope_hosts = ["*"] if you really want that. For in-scope credentials: bound
arguments are filled, password-typed arguments are removed from the agent's tool schema entirely (the model can neither see
nor supply them), and the same name-derived env vars are set. The agent can
pick a credential by id when several match — it only ever handles ids, never
secret values. Credentials also survive sudo for runnables that declare
run_as.