RFC-0005: Run supervisor services
Version: 0.1.0 | Status: draft | Phase: spec Owners: @Will Eaton
1. Summary
[RFC-0005:C-SCOPE] Scope (Informative)
A run supervisor service is a long-running process the run supervisor starts beside the engine to give its tasks a capability they cannot hold themselves. The standing example is cluster access: a connector that creates a disposable virtual cluster for the run and serves its API to the agent, while Kueue admission and host policy stay on the host. A JIRA or forge client is the same shape. Services are opt-in per workload: an autoresearch run that edits a control-plane resource may need a whole virtual cluster, while most agent workflows need no service at all and start none. Sandboxed tasks reach a service only through the OpenShell gateway, and command tasks reach it directly.
Services belong to the manifest, not to the workflow graph: the graph says what runs and in what order under RFC-0002:C-PLAYBOOK-SHAPE, and a service is part of the environment tasks run in. A task asks for one by naming it in needs under RFC-0002:C-PLAYBOOK-CAPS.
The broker is the built-in instance of this pattern, with a fixed capability set. This RFC covers services a manifest declares. The boundary it draws is the sandbox: command and evaluate tasks and hooks already share the run supervisor's reach, a service's credentials included.
[RFC-0005:C-TERMS] The run supervisor (Informative)
The run supervisor is the process that runs one workload's engine: it compiles the workflow, dispatches every task, holds the run's credentials, and owns the sandboxes its agent turns run in. There is exactly one per run. It is created when the run starts and torn down when the run ends, on a laptop as a process and in a cluster as a pod. The sandbox is the untrusted side of the boundary it draws; the controller, which launches runs, is not part of it. Earlier documents call the run supervisor the loop pod and its image the loop image.
2. Specification
[RFC-0005:C-SERVICE-DECLARATION] Service declaration (Normative)
A manifest of any workflow type MAY declare services as an array of [[service]] tables, extending the schema RFC-0001:C-MANIFEST defines and validated under its rules. A manifest that declares none runs exactly as it would without this RFC.
Each service MUST declare a name and a tool, and MAY declare arguments, a tool version requirement, and secrets. A name MUST match [a-z][a-z0-9_]* and MUST be unique among the manifest's services. The capability a service provides is "service." followed by its name, so no service can take a name the engine or the broker uses. Arguments MUST be a list of strings, passed to the tool's executable verbatim and never through a shell. The engine MUST reject an argument that names a path inside the pack or the workspace. A version requirement uses semantic-version requirement syntax. Each secret entry MUST name a secret and MUST state the external system it authorizes and the scope within that system.
The engine MUST refuse the run, naming the service and the field, when a declaration violates this clause, before compiling the workflow.
[RFC-0005:C-SERVICE-SECRETS] Service credentials (Normative)
The engine MUST deliver each secret a service names only to that service's process environment, and MUST itself write no secret value into the sandbox, the workspace, a task's inputs, a prompt, the session log, or a captured file.
A service's secrets are credentials under RFC-0001:C-CAPABILITY-DISCLOSURE, held by the service. The resolved disclosure MUST list each service as a relay: the tool, the secrets it draws with the system and scope each declares, the tasks it is exposed to, and the gateway endpoint it adds to their sandboxes. A secret granted at launch is covered only when a service in the manifest names it with equal or broader scope.
[RFC-0005:C-SERVICE-CAPABILITY] Services as capabilities (Normative)
A declared service provides the capability "service.
A service is available when its tool is attested under RFC-0005:C-SERVICE-CATALOG and every secret it names was granted to the run. The engine MUST decide availability before any task is dispatched. A task that needs an unavailable service is handled exactly as RFC-0002:C-PLAYBOOK-CAPS requires: a required one truncates the plan and an advisory one is skipped with its dependents. The engine MUST report the unattested tool or the ungranted secret by name. A service that is available but fails to start or to stay up is governed by RFC-0005:C-SERVICE-LIFECYCLE.
[RFC-0005:C-SERVICE-LIFECYCLE] Service lifecycle (Normative)
The engine MUST start a service no later than the first dispatch of a task that needs it, and MUST NOT start a service no dispatched task needs. It MUST give the service the run identifier as CRUCIBLE_RUN_ID and a free loopback port as CRUCIBLE_SERVICE_PORT, and MUST NOT dispatch a task that needs the service until the service is ready: an MCP initialize handshake that succeeds for an MCP transport, any HTTP response for plain HTTP. The run identifier MUST be unique across runs and the same across a run's resumptions.
A service that is not ready within the engine's readiness bound, or has exited when a task that needs it is about to dispatch, MUST fail that attempt as a transport failure, retried under the run's transport policy; the task did not run. A service that exits while a task that needs it is running MUST fail that task's attempt as a transport failure. The engine MUST restart an exited service before the next dispatch that needs it, and after the engine's restart bound for one run MUST treat further attempts that need it as transport failures without restarting it.
One instance of each service runs at a time, shared by every task that needs it, epilogue tasks included. When the run ends, stops, or suspends, the engine MUST signal each service to stop, MUST allow it the engine's grace period, and MUST then kill it. A resumed run MUST start new instances. The engine MUST run services so that they do not outlive the run supervisor if it dies.
crucible check MUST print the readiness bound, the restart bound, and the grace period.
[RFC-0005:C-SERVICE-REACH] How tasks reach a service (Normative)
A service binds only the loopback port the engine gives it. The engine MUST expose each service through a forward of its own on the OpenShell gateway, and the forward MUST refuse any request that does not carry the per-run, per-service bearer credential the engine issued for it. The engine MUST give that credential only to tasks that need the service.
An agent task that needs a service with an MCP transport MUST be configured with it as an MCP server named "service.
A sandbox's egress policy MUST include a service's forward exactly when some task that sandbox runs needs the service. Tasks that share a session MUST declare the same needs, and the engine MUST reject a workflow in which they do not, naming the session.
[RFC-0005:C-SERVICE-RECORD] Identity, check, and log (Normative)
The run's identity digest MUST cover every resolved service: name, tool, the attested version and executable, arguments, secret names with their declared system and scope, and the engine's readiness, restart, and grace bounds. Secret values MUST NOT enter it. A resumed run whose catalog attests a different version MUST record the new resolution and MUST NOT reuse the old one.
crucible check MUST print each resolved service with its tool, attested version, transport, secret names, and the tasks that need it.
The session log MUST record when each service starts, on which port, becomes ready, and exits, with its exit status, whether it stopped or was killed, and the task whose dispatch started it.
Spend a service causes outside the run supervisor, such as cluster work it submits, is not counted against the run's cost ceiling; the target's own quota bounds it.
[RFC-0005:C-SERVICE-CATALOG] The service catalog (Normative)
The run supervisor attests the tools a service may run in a service catalog. In a run supervisor image the catalog is a document the image carries at a fixed path. On a run with no image, the catalog is the document the operator names in CRUCIBLE_SERVICE_CATALOG, and with none named no tool is attested. crucible check MUST print which catalog it read.
Each catalog entry MUST give the tool's name, the absolute path of its executable in the run supervisor, its version, and the transport it serves: MCP streamable HTTP at a stated path, MCP over stdio, or plain HTTP. It MUST state whether the tool creates resources outside the run supervisor and whether it accepts concurrent clients. By listing a tool, the catalog asserts that the tool binds only the loopback port it is given, labels anything it creates outside the run supervisor with the run identifier it is given, and reclaims those resources when signalled to stop. A tool whose entry says it does not accept concurrent clients serves one task at a time, and the engine MUST NOT dispatch concurrently two tasks that need it.
A tool is attested for a service when the catalog lists it by the declared name at a version the declared requirement admits. The engine MUST run a service only from the executable its catalog entry names, never from a path the pack or the workspace supplies.
[RFC-0005:C-SERVICE-HANDOFF] Credentials a service hands a task (Normative)
A service MAY hand a task that needs it a credential for a target the service created for this run, such as the API of a virtual cluster it provisioned. The engine MUST deliver such a credential only into the sandbox of a task that needs the service, at a path the catalog entry states, and MUST NOT deliver it to any other task. The credential MUST be disclosed under RFC-0001:C-CAPABILITY-DISCLOSURE as entering agent context, with the target and scope the catalog entry states for the tool.
A service that hands off credentials MUST hand off only credentials scoped to targets it created for the run and reclaims when signalled to stop, which its catalog entry asserts. A credential for the run supervisor's own cluster, or for anything the run did not create, MUST NOT be handed off.
Changelog
v0.1.0 (2026-09-28)
Initial draft