Skip to main content

Assistant

The Strataplane assistant helps teams write and change agent configuration: build a Telegraf parser from a sample payload, add an input to a layer, find out why a host gets a value, prepare a change set. It follows the design of the Grafana Pi assistant (elohmeier/grafana-pi-app): one agent, four tools, everything else is a shell command over a session filesystem, and writes are reviewed change sets.

1. Principles​

  • Bash first. The model has read, write, edit and bash. Strataplane capabilities are commands in the shell (sp render, telegraf-parse, …), not extra tools. Skills add instructions, never tools.
  • Same commands as the CLI. The shell commands are the stratactl commands, from one registry (architecture.md §8). A workflow the assistant can do, a human can script, and vice versa.
  • The spec is files. The working copy of the spec is mounted at /spec. Edits there are the change set's overlay.
  • Acts as the user. Every command calls the API with the user's session, so ACLs apply. The assistant has no privileges of its own. Its requests name their chat in X-Assistant-Chat, so the audit tells them apart: their events have the actor kind assistant, the chat id as actor and the user in on_behalf_of; a header that names no chat of the caller (another user's, a deleted or an unknown one) is refused with 400 assistant.invalid_chat. A user's actions in the audit include the assistant's on their behalf.
  • No remote writes without review. Its only remote writes stage change sets: create, validate and submit them for review. Approval and merge stay with humans, in the UI or with stratactl, under the normal approval policies (§4).
  • No network in the sandbox. The shell is just-bash in a Web Worker with no network, process or link commands. Commands reach Strataplane only through the typed broker.

2. Runtime​

Svelte UI
└─ ChatView ── AssistantSession (framework-free TS)
├─ Pi Durable harness (@earendil-works/pi-durable), one per chat
│ ├─ models: pi-ai provider "strataplane" → streamProxy → POST /api/v1/assistant/llm/stream
│ ├─ extension: tools read/write/edit/bash, system prompt sections, skills
│ └─ storage: LogStorage (MemoryStorage + chat log) → /api/v1/assistant/chats/{id}/commits
├─ SessionWorkspace (virtual FS, copy-on-write transactions)
├─ just-bash worker (coreutils, rg, jq-wasm, sed, awk, diff)
├─ wasm worker: telegraf-parse, vector-vrl (WASI shim)
└─ StrataplaneBroker (typed API client, user's session)
Rust server
├─ LLM proxy: OpenAI-compatible (Chat Completions / Responses) and Anthropic Messages; key never sent to the browser
├─ chat log store: one writer per chat (epochs), exactly-once commits (seq + digest), replacing rows, tombstones
└─ preview endpoints used by the commands (render, explain, impact, validate)
  • Durable chats. The harness commits every step (user input, streamed partials, tool calls and results, compaction, the session filesystem as the app.workspace document) before the UI shows it. Reloading continues an interrupted answer; read calls are replayed, write, edit and bash calls are reported to the model as interrupted because they may already have changed state. Retry backoffs are durable sleeps and resume when the chat is next open. A task cut off by crashes five times in a row is parked; the chat shows it with a retry action (retryTask).
  • Chat log API follows the grafana-pi-app contract (open → new writer epoch, creating the chat; commits with sequence number and digest; paged log replay), implemented in Rust over chat / chat_row (chat_row). LogStorage applies a commit in memory only after the server stored it; opening the chat in another tab takes it over, and the previous view stops.
  • Bounded logs. Task and submission records and document bases replace their predecessors, so a long streamed answer stores a handful of rows. app.workspace writes a base once 32 deltas follow the last one (checkpointWhen), which bounds both replay and stored rows. Transcript entries are never pruned; compaction only changes what the model sees.
  • Models are configured per tenant by its admins (llm_provider): base URL, write-only API key, model list with context window and output limits, thinking settings and whether the model takes images. Images (screenshots the assistant attaches) reach the model in tool results; each request carries the latest three, older ones become a note, and a model without image input gets only the note. The default model and a prompt addendum the client cannot remove are the assistant Settings object (spec-format.md §3.8). Local OpenAI-compatible servers (llama-server, vLLM) work for air-gapped installs.
  • Browser only. The assistant runs while a user has it open.

3. Session filesystem​

PathKindContent
/spec/**resource, writableWorking copy of the spec tree at the chat's base revision. Writes stage an overlay; files are canonicalised on commit.
/spec/.base.jsongenerated, read-onlyBase revision, tree hash, change set id if one exists.
/inventory/hosts.ndjsongenerated, read-onlyHosts visible to the user: name, kind, labels, owner. Lazily loaded, cached 5 min.
/inventory/agents.ndjsongenerated, read-onlyAgent instances: kind, version, health, sync, desired/applied revision.
/catalog/telegraf/<version>/<section>/<plugin>.jsongenerated, read-onlyPlugin schema, sample.conf and README from the catalog bundle. Listed for every imported version, content fetched on read.
/catalog/telegraf/<version>/reference/docs/<name>.mdgenerated, read-onlyTelegraf's documentation of the versions the UI was built with (CONFIGURATION.md, DATA_FORMATS_INPUT.md, …).
/catalog/telegraf/<version>/reference/src/<section>/<plugin>.gogenerated, read-onlyThe plugin's Go sources of those versions, one file per plugin (common/<package>.go for shared code such as the SNMP translator). A version's reference is one static asset of the UI, fetched on the first read.
/catalog/elastic-agent/<version>/inputs/<type>.jsongenerated, read-onlyInput types and known options.
/catalog/vector/<version>/<section>/<type>.jsongenerated, read-onlyVector component schemas per section, and global/options.json.
/agents/<host>/<kind>/rendered.{toml,json}generated, read-onlyCurrently served artifact (secrets masked), fetched on read.
/agents/<host>/<kind>/logs.ndjsongenerated, read-onlyRecent heartbeat log entries / failed component units.
/samples/scratch, persistedSample payloads the user uploaded or pasted.
/workspace, /sessionscratch, persistedWorking files, notes (/session/findings.md).
/tmpscratchNot persisted.
/artifactsgenerated, read-onlyBulky command outputs (impact lists, render diffs) as JSON for jq.
/.agents/skillsgenerated, read-onlyBundled skills.

Every mutation (tools, bash, commands) runs in one copy-on-write transaction per tool call. read returns a content revision, and write/edit can require it, so concurrent edits are detected.

4. Commands​

The shell offers the commands of cli.md whose effect is not remote-write: the working copy, previews, reads, telegraf-parse, vector-vrl, creating, validating and submitting change sets, and exploring journey targets on an exploration runner (remote-run). In the shell the CLI is called sp: sp render is stratactl render. /spec is the working copy, so sp spec pull without a directory (or with /spec) moves its base to another revision; into any other directory it writes a plain copy. Bulky outputs (impact lists, render diffs) also go to /artifacts as JSON for jq.

There is no command to review, merge, roll out, pin, write secrets or change agents; those are remote-write and exist only in stratactl.

5. telegraf-parse and vector-vrl​

Runs a Telegraf parser config on sample payloads, in the browser worker, without a Telegraf sandbox. The module, its ABI and where it runs are described in agents/telegraf.md §8.

telegraf-parse --version 1.40.1 --parser json_v2 --config /workspace/parser.toml \
[--metric-name http] [--time 2026-10-21T12:00:00Z] SAMPLE…

--version defaults to the most common version among the agents that the working copy's layer targets. Output is the line protocol plus a summary (metric count, tag and field keys, type conflicts); the full JSON goes to /artifacts.

Caching. Results are deterministic for a fixed --time; the durable session caches them by (version, parser, sha256(config), sha256(samples)), so a resumed run does not recompute.

Data handling. Samples are often real payloads. The wasm runs locally in the browser or CLI, so samples are not sent to Strataplane unless the user uploads them into a chat (/samples/, stored with the chat). A sample reaches the LLM only if the model reads the file; admins choose which providers and models are available.

vector-vrl runs a VRL program, the source of a Vector remap transform, on sample events the same way: in the browser worker, with the module of agents/vector.md §8.

vector-vrl --version 0.60.0 --program /workspace/remap.vrl \
[--time 2026-10-24T12:00:00Z] [--timezone Europe/Berlin] EVENTS…

An events file holds a JSON array, one JSON value or JSON lines; - reads stdin. --version defaults to the most common version among the tenant's Vector agents. Output is one line of JSON per event (or dropped), the compile diagnostics and runtime errors on stderr, and a summary; the full JSON goes to /artifacts, cached like telegraf-parse by (version, sha256(program), sha256(events), timezone) for a fixed --time. Events, like samples, stay in the browser.

6. Example: building a parser​

user: Here is the JSON our payment API exposes at /status. I want queue depth and
latency per queue on all payment hosts. [attaches status.json]

assistant (bash):
$ jq 'paths | map(tostring) | join(".")' /samples/status.json | head
$ cat /catalog/telegraf/1.40.1/parsers/json_v2.json | jq '.properties | keys'
$ cat > /workspace/parser.toml <<'EOF'
[[inputs.http.json_v2]]
measurement_name = "payment_queue"
[[inputs.http.json_v2.object]]
path = "queues"
tags = ["name"]
[inputs.http.json_v2.object.fields]
depth = "int"
latency_ms = "float"
EOF
$ telegraf-parse --parser json_v2 --config /workspace/parser.toml /samples/status.json
payment_queue,name=settlement depth=12i,latency_ms=48.2 1792598400000000000
payment_queue,name=refunds depth=0i,latency_ms=3.1 1792598400000000000
$ sp hosts --selector 'app=payment,stage=prod' | wc -l
14
(edits /spec/layers/app-payment.toml: instance inputs.http.payment_status)
$ sp validate && sp impact --by stage
telegraf: 22 agents change (prod 14, preprod 8); approvals: team-payments, four-eyes (prod)
$ sp render pay03.example.com | rg -A12 'payment_status'
$ sp changeset submit -m "Payment queue metrics from /status"
CS-418 in review: https://strataplane.example.com/change-sets/418

7. Skills​

SkillContent
telegraf-authoringInstance keys and aliases, layer vs bundle, parser iteration with telegraf-parse, namepass/routing conventions, how to read plugin schemas.
elastic-agent-inputsInput types, streams, use_output, processors, data stream naming.
vector-authoringInstance keys and component ids, inputs and wildcard routes, remap iteration with vector-vrl, settings.global and its restart, how to read component schemas.
layeringPrecedence bands, selectors, merge directives, explain-driven debugging, avoiding per-host layers.
troubleshootingReading health/sync, heartbeat logs, component units; out-of-sync and stalled-watcher diagnosis.
change-setsImpact review, approval requirements, writing good change set descriptions.

8. Journeys​

The assistant writes Journey files under /spec/journeys/ like any spec file and tries them on an exploration runner. Its mounts, commands and the journey-authoring skill are in journeys.md §8.3.