Skip to content

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 sets RUNSPEC_<RUNNABLE>_ARG_DEBUG=1 instead of adding --debug to 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 own debug argument 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 when a host profile is loaded) to search every host in the sidebar instead. The choice is remembered.

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.toml holds 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.