Steadmesh

Frequently asked questions

Find a useful first task, get connected and understand what stays with your team between conversations. The setup, access and teardown answers below link to the detailed guides.

Version scope. Answers describe v0.6.0 and were checked against its source. Where an answer describes a choice in a particular setup rather than product behaviour, it says so. Commands that are still awaiting a clean-install check are marked not yet clean-install tested.

Working with a team

What can I ask the team to do?

Start with a task that has a clear outcome and uses the access you have configured. The example engineering team is a good place to try a small repository change: ask for updated test instructions, a regression test or a focused fix, with a reviewer checking the result. You can also ask seats to save project decisions in shared memory or organise longer work with owners and dependencies.

Tell your representative what you need, what matters and how you want the result reviewed. The team works from its instructions, declared message routes and grants. Connecting a service does not give every seat access to it, and a request in chat cannot add permissions. See the tutorial to adapt the team to your work.

Can it follow up later?

Yes. A seat can create a one-off reminder, a follow-up or a recurring check using automations. The platform stores the schedule and wakes that seat with the instruction, using its own harness, model and permissions. Be specific about what to check, when to check it and when to stop.

Schedules survive restarts, but a busy seat handles a queued run after its current work. Overlapping occurrences are skipped, and downtime produces one catch-up run rather than a replay of every missed occurrence. Delivery still depends on the channel: terminal replies have the in-memory queue limits described below.

Setup and models

Where do API keys go, and which stage creates them?

A model connection refers to a Kubernetes Secret in the platform namespace, or to a supported external secret-store reference. The platform's model proxy uses the real key. The seat talks to the proxy and doesn't need the provider key.

Which Terraform stage creates the Secret is a choice made by each setup. The example organisation creates its model, GitHub and terminal Secrets in the organisation stage, from TF_VAR_ variables in .env. The repository's quickstart modules create the Slack, terminal, model and Linear Secrets in the platform stage, from model_api_keys and related variables. You can also create the Secrets yourself and pass existing_secret_refs.

Terraform state holds secrets that Terraform creates. Secrets created by Terraform are stored in that stage's state, even when the variables are marked sensitive. Keep state private, and keep key values out of instructions, screenshots and source control.

GitHub and browser access have a separate credential boundary. With GitHub sandbox delivery, the seat receives a credential it can read, and browser-session delivery has its own boundary (see below and Sandbox access). Changing the value behind a supported secret reference makes the platform refresh the credential without a Terraform run (credential rotation). Key rotation against real providers remains unrecorded: the real-services demo skipped that step.

Where is the Pi key?

Use the key for the model provider you choose. Pi is an agent harness, the program that reads files, runs tools and edits code, rather than a model provider. Choose Pi for a seat and give that seat a model connection. Steadmesh's Pi adapter points Pi at the seat's local model proxy with a placeholder key, and the platform adds the real provider credential. A setup where every seat runs Pi still needs the credentials of whichever model provider those seats use.

Sources: harnesses/pi/CONTRACT.md, harnesses/pi/pi.go.

Which LLM does it use?

The one you configure. Each seat has a model: a connection, a model ID and an API, and the harness must support that API. The Pi adapter speaks Anthropic Messages, OpenAI Responses and OpenAI Chat Completions. Claude Code and Codex support narrower sets, listed in the harness matrix. The quickstart defaults every seat to Claude Code with claude-sonnet-5-5 on Anthropic. You can override this per seat with seat_harnesses.

API compatibility is a starting point; tool calling and other features still need checking with your chosen provider and model. Declare the endpoint, model ID, credential reference and API together, and set context-window and other settings to match the real model. Then check readiness, which runs a probe turn through each seat's own harness and model (harness readiness). Changing only the API label isn't a compatibility test.

Talking to the team and following the work

How do I chat with my representative?

Send a Slack DM or use the terminal, depending on your setup. A channel binding maps your external user ID to your representative seat. The example organisation uses the terminal connector only, with one user, me. The quickstart modules support Slack, the terminal, or both: for the terminal, list your user ID in terminal_users, and the platform stage creates a token for you in a Secret. Either way, you connect with:

orgctl chat --context <kube-context> --user <your-terminal-user-id>

not yet clean-install tested orgctl always needs an explicit --context and never uses your current one. It reaches the platform through kubectl port-forward and reads your token from the connection's Secret, unless you pass --token. This is a local tool for your own cluster, not a hosted chat service.

Reconnecting doesn't guarantee delivery. While you're disconnected, the terminal connector keeps up to 200 replies per user in memory. When that fills, it drops the oldest, and it loses the whole queue if the connector is rebuilt or the platform restarts. A connected stream that falls behind can also drop messages. The organisation's own message history is stored durably in Postgres and is separate from this delivery queue.

Sources: connectors/terminal/terminal.go, cmd/orgctl/chat.go.

How do I see the organisation working?

Turn on the console by setting enable_console = true in the platform stage and applying. Then forward it to your machine:

orgctl console --context <kube-context>      # http://localhost:8090 until you stop it
orgctl status  --context <kube-context>
kubectl --context <kube-context> -n <organisation-namespace> get agentseats

not yet clean-install tested The console is read-only. It shows seats, messages, runs, work items and readiness, and the activity view ties messages to seats. Keep orgctl console running while you use it, and stop it when you're done. Use your deployment's real namespace instead of one copied from another example. This website doesn't embed a live console, and the console can't edit the organisation. See Checking readiness.

What does “lease held” mean?

It means a runner currently holds the right to act for that seat. By default the lease lasts 30 seconds and the runner renews it every 10 seconds. A warm, idle runner also holds a lease, so a held lease doesn't mean the model is thinking or that work is moving. Read it together with the progress time, the current task and any queued messages. A stalled progress timestamp is worth investigating even while the lease looks healthy.

Lease generations reject stale platform writes when ownership moves to a new runner. That guards platform state, but it doesn't promise that an external side effect can never repeat. With the default warm_then_stop idle policy, a seat with no pending work stops after its idle timeout. That's 15 minutes by default, and the quickstart sets it with seat_idle_timeout. The warm policy keeps the seat running. Stopping idle seats reduces activity, but the platform, database, storage and cluster can still cost money.

Sources: services/store/seats.go, cmd/seat-runner/config.go, controller/lifecycle/decide.go.

What is a run?

A run is a recorded execution of a seat handling a delivered message: one turn of work. It isn't the lifetime of the pod or the whole native harness conversation. Retries and recovery can create more than one run for the same original message.

A run's detail shows the triggering message, seat, configuration revision, harness, lease generation, timing, recorded steps and connector operations. Operations the platform records are separate from shell activity inside the sandbox and from harness-specific telemetry, so a run is useful for following the work but is not a complete audit trail of everything the harness did. Redaction helps, but screenshots and exported traces still need a privacy review before you share them. See Architecture.

Representatives and memory

What does the representative do, and how is it different?

Your representative is the seat you keep coming back to with questions, requests and preferences. Technically, it is an ordinary seat with a human channel binding, using the harness and model you choose. The compiler marks the seat as a representative and grants it permission to reply to that bound identity. A seat can represent only one human, so binding it to a different external user ID fails validation, because representatives keep separate private histories. An external identity can also be bound only once.

Give it instructions to clarify requests, remember preferences, brief a lead, follow replies and shared notes, schedule check-ins and report results. Choices such as giving it no GitHub access, read-only shared memory or a route only to the lead are design decisions for each setup, not product rules. You can bind a lead seat directly if that suits you. Delegation still depends on declared routes and grants, and instructions in a prompt never grant access.

Sources: pkg/compile/compile.go, Agent tools.

Does each agent keep its context between messages?

Seats retain durable memory and files; continuity of the native conversation depends on the harness. Pi, for example, can continue the same native session across runs, and its session files live on the seat's persistent workspace. That lets it resume after the runner stops or the pod is replaced, as long as those files are still there. Compaction summarises older context and isn't perfect recall.

Native conversation context is separate from what Steadmesh stores durably: private and shared memory, work items, message history and workspace files. Ask the team to record important decisions in the right store or handoff so later work can find them. When a seat changes harness and the native session can't be converted, the runner starts a new session seeded from a portable handoff. That isn't a full transcript conversion. Test whether data survives a lifecycle operation with your own release, storage and retention settings. See Operating an organisation, and the source in harnesses/pi/pi.go and harnesses/harness.go.

GitHub and the sandbox

Do agents have filesystems, and how do they use GitHub?

Yes. Each seat has a persistent workspace volume mounted at /seat. Its size is set by workspace_size_gb and defaults to 5 GiB. Working files and harness session data live there.

There are two ways for a seat to reach GitHub:

  • Platform delivery. The seat calls the connector's defined operations through connections.invoke, such as reading repositories, pull requests and issues, opening a pull request or commenting. The token never enters the sandbox. This isn't a general git clone/push workflow.
  • Sandbox delivery. git and gh run inside the seat through a credential helper, and approved GitHub hosts are reached through the egress gateway. This needs a CNI that enforces NetworkPolicy and an enabled egress gateway. It isn't general internet access.

A sandbox credential can be read. With sandbox delivery, the agent and other processes in the pod can read the credential, even though the helpers avoid writing it into configuration files. A fine-grained personal access token (PAT) is passed through as it is and keeps the full scope you gave it at GitHub. Granting one repository in Steadmesh doesn't narrow a broader PAT. A GitHub App instead gets an installation token limited to the granted repositories and permissions, valid for one hour, which Steadmesh can revoke. A PAT must be rotated or revoked at GitHub. Use sandbox delivery only for work that needs it.

Sandbox delivery requires a configured durable credential ledger key, including when using a PAT. App-token revocations are retried across restarts; provider outages can delay them until the token expires. Upgrades need a coordinated rollout and organisation resync.

See Sandbox access: GitHub and the egress gateway. Source: connectors/github/github.go.

Can a seat try work in a temporary environment?

Yes. From v0.6.0, disposable task workers run bounded tasks in fresh Pods using explicit text inputs. They share the parent's model and output budgets, have no sandbox credentials and get no platform tools by default. Run alternatives compare two to four independent approaches from the same handoff. Choosing one records a decision; it does not apply its files, merge a Git branch or cancel other runs.

Where is the repository configured, and how do agents find it?

The repository is a target on a GitHub access grant, usually a team grant to engineering. A seat calls connections.list to see the connections, operations and targets it may use. Tell the engineers in their instructions to discover the granted repository that way and clone it into their workspace. Include that setup step in the role instructions, and check that the returned targets are the ones you intended.

Leaving the representative without a GitHub grant, so the lead sends it pull request links, is a configuration choice, not a product rule. With sandbox git, what a seat can actually reach also depends on the credential's real GitHub scope (see the PAT caveat above). Sources: services/tools/seat.go, Tutorial: grants.

Data and teardown

Can I cap what my agents spend?

You can enforce cumulative output-token budgets per organisation per UTC day, per seat per UTC day and per execution. Workers and run alternatives share their parent's counters. These limits do not cap input tokens, provider charges or infrastructure costs, so use a provider-side spending limit for a monetary ceiling. Missing or uncertain usage keeps the full reservation charged.

Where is the data stored, and can destroy remove Postgres?

Durable organisation records live in Postgres. Seat workspaces and native sessions live on each seat's persistent volume. On a local kind cluster, those volumes sit inside the kind node, which is a Docker container. That's Kubernetes running in Docker, not a separate Docker or SQLite mode of the product.

Replacing a pod keeps its attached volume, because workspace volumes are retained and have no owner that would delete them. Destroying an organisation with data_retention = "retain" (the default) keeps database records and workspace volumes, while "delete" removes them.

Retention isn't a backup. Deleting the namespace, the database or the kind cluster bypasses retention and can destroy the data. In the quickstart, destroying the platform stage deletes its namespaces, and the database with them. A Postgres dump covers the database, but not seat workspace volumes or the configuration and credentials you'd need to recover. Test a restore before you rely on one. Keep Terraform state private too, because it can contain generated passwords and any credential Secrets Terraform creates.

See Retiring seats and retention and Architecture: data. Source: runtime/kube/render.go.

How do I take it down, and is terraform destroy immediate?

First choose what you're taking down. Each option has different consequences:

  • Stop idle compute. warm_then_stop stops idle seats on its own. Workspaces and memory are kept.
  • Remove a seat. Delete it from the declaration. It retires over a grace period (10 minutes by default; the chart value retirement.grace). During that time it gets no new messages and has one final turn to save a handoff. Its work items are released and its representatives get a summary.
  • Destroy the organisation. This retires its seats at once at the platform level, and keeps or deletes data according to data_retention.
  • Remove the platform or cluster. This deletes the database and volumes along with everything else.

Shutdown isn't instant. The runner allows up to 240 seconds to quiesce, and seat pods get a 300-second termination grace period. Those timings are separate from the retirement grace and from Terraform's own timeouts, so a destroy isn't guaranteed to finish in any fixed time.

Before any destructive command, check the kube context, namespace, Terraform state, data_retention setting and the storage it affects. Export the backups you need, test that they restore, and revoke GitHub PATs separately at GitHub. Destroying the platform stage or deleting a kind cluster can remove the database and every workspace.

In the quickstart, destroy in reverse order: run terraform destroy in organisation/, then in platform/. Don't delete Terraform state files as routine cleanup after deleting a cluster by hand. Keep a backup of the state and reconcile it for the environment you meant to remove. Sources: Retiring seats, services/store/retire.go, cmd/seat-runner/config.go, runtime/kube/render.go.

Why does terraform destroy ask me for a variable?

Terraform still evaluates the configuration during a destroy, so it needs every required input. A common cause is a new terminal where you haven't loaded the file that sets your TF_VAR_… variables, or a terraform.tfvars that's missing from the working directory.

  1. Cancel the prompt. Don't type a guess, especially not for kube_context.
  2. Check that you're in the intended working directory and that it points at the intended cluster.
  3. Load your own trusted variables file. Sourcing a shell file runs it, so never source a file you haven't reviewed.
  4. Run terraform plan -destroy and read the plan before destroying.

How do I know whether my kind cluster is clean before starting?

Look before you install, with an explicit context:

kind get clusters
kubectl --context kind-<name> get namespaces
helm --kube-context kind-<name> list --all-namespaces
kubectl --context kind-<name> get crds | grep steadmesh
kubectl --context kind-<name> get agentorganizations --all-namespaces

not yet clean-install tested Finding existing Steadmesh namespaces, Helm releases, CRDs or organisations means an earlier install is there. Check who owns them, which version they are and what data they hold. That isn't a reason to delete the cluster automatically. Reusing an existing install needs its upgrade path. For a clean test, the simplest route is a fresh, disposable kind cluster with a new name.

Deleting a kind cluster deletes its local database and workspace storage. Decide what to back up before you run kind delete cluster. The from-source route's make e2e-reset deletes its steadmesh kind cluster this way.

Run this check before the first terraform apply, so resource-name or CRD conflicts don't turn into a confusing failure halfway through. See Run from source.