Skip to content

Commit 1c3e0bd

Browse files
committed
docs(agent-workflows): plan runner self-hosting cleanup
1 parent b16a61c commit 1c3e0bd

9 files changed

Lines changed: 1313 additions & 0 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Runner self-hosting cleanup
2+
3+
Status: DESIGNED, awaiting owner review
4+
Date: 2026-07-13
5+
Target branch: `big-agents`
6+
7+
This project makes the agent runner understandable from its deployment configuration alone. It replaces shared and overlapping environment variables with one runner-owned provider contract, removes automatic subscription credential upload, adds explicit bootstrap assets, and makes required mounts fail loudly.
8+
9+
## Outcome
10+
11+
A self-hosted operator should be able to answer these questions by reading one Compose service or one Helm values block:
12+
13+
1. Where is the runner?
14+
2. Which sandbox providers can it use?
15+
3. Which provider is the default?
16+
4. Which credentials belong to the runner itself?
17+
5. Which files are intentionally copied into a run?
18+
6. Which combinations of sandbox, harness, and authentication are supported?
19+
20+
The normal deployment uses one runner service. That runner can support both local and Daytona runs at the same time. Harness choice remains per agent. Sandbox choice remains per run, constrained by the providers the operator enabled.
21+
22+
## Decisions
23+
24+
- The runner is a deployment-level service, not one sidecar per harness or sandbox provider.
25+
- The operator enables sandbox providers explicitly. Unset means local only, not every provider known to the binary.
26+
- Daytona configuration belongs under the runner namespace. Code-evaluator Daytona configuration is separate.
27+
- The runner never discovers an operator's home directory or automatically uploads Pi or Claude OAuth state.
28+
- Subscription authentication is an explicit local self-hosting setup taught in a tutorial.
29+
- Bootstrap assets are declarative, typed, and implemented by both local and Daytona adapters. Version 1 does not execute arbitrary hooks.
30+
- Remote subscription authentication is unsupported until the relevant provider explicitly approves the integration and Agenta defines a safe product contract.
31+
- Pi installation is detected and repaired by the runtime. There is no deployment-wide "Pi is installed" boolean.
32+
- Durable mounts required by a session or workflow artifact fail the run when they cannot be signed or mounted. Sessionless runs remain ephemeral.
33+
- The runner receives a narrow environment and no static Agenta API key. Per-run caller credentials authorize callbacks and trace export. It does not inherit a shared application environment or database and cryptographic secrets.
34+
35+
## Reading order
36+
37+
- [Context](./context.md) explains today's architecture and why the current variables are confusing.
38+
- [Research](./research.md) records repository evidence and the review of PRs #5274, #5285, and #5286.
39+
- [Interface](./interface.md) defines the target environment, Helm, capability, bootstrap, and failure contracts.
40+
- [Plan](./plan.md) splits the cleanup into reviewable implementation PRs.
41+
- [QA](./qa.md) defines the environment, harness, and credential matrix.
42+
- [Documentation plan](./documentation-plan.md) maps the final public docs through Diátaxis.
43+
- [Open issues](./open-issues.md) parks work that should not block the cleanup.
44+
- [Status](./status.md) is the owner review checklist and progress log.
45+
46+
## In scope
47+
48+
- Runner provider selection and configuration.
49+
- Runner-specific Daytona names and validation.
50+
- Runner-to-Services routing and authentication.
51+
- Declarative bootstrap assets for local and Daytona environments.
52+
- Local subscription setup.
53+
- Removal of automatic Daytona OAuth upload.
54+
- Removal of deployment-wide runtime facts and hidden policy switches.
55+
- Required-mount failure behavior.
56+
- Compose, Helm, Railway, self-hosting docs, and the local 8280 QA deployment.
57+
- Capability discovery needed to keep the API and UI honest.
58+
59+
## Out of scope
60+
61+
- Implementing the Docker or E2B providers.
62+
- Sharing a personal subscription between users or tenants.
63+
- Remote subscription support.
64+
- Arbitrary bootstrap scripts, VPN hooks, or network overlays.
65+
- Reworking code-evaluator execution in the same change.
66+
- Backward-compatible aliases for unpublished pre-production variables.
67+
- A custom-role migration bridge. Default roles and tests still need the final mount permissions.
68+
69+
## Related work
70+
71+
- [PR #5274](https://github.com/Agenta-AI/agenta/pull/5274) added mount and snapshot changes that exposed the immediate configuration bugs.
72+
- [PR #5285](https://github.com/Agenta-AI/agenta/pull/5285) proposes Docker and a sandbox allowlist. This project adopts its capability-based provider direction but replaces the permissive cross-layer allowlist contract.
73+
- [PR #5286](https://github.com/Agenta-AI/agenta/pull/5286) adds useful runner authentication and provider-key narrowing. This project rejects its broad Helm `commonEnv` injection and its compatibility escape hatch.
74+
- [Runner self-hosting explainer](../runner-selfhosting-explainer/README.md) contains the detailed as-built explanation that led to this cleanup.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Context
2+
3+
## The current architecture
4+
5+
The agent runner is a long-lived Node service. Services sends it a run request, the runner chooses a sandbox provider, and a sandbox-agent daemon launches the selected harness.
6+
7+
~~~text
8+
Services API
9+
-> runner HTTP service
10+
-> sandbox provider: local or Daytona
11+
-> sandbox-agent daemon
12+
-> harness: Pi or Claude
13+
~~~
14+
15+
The two choices are independent:
16+
17+
- The harness belongs to the agent configuration.
18+
- The sandbox provider belongs to the run, with a deployment default.
19+
20+
One runner process can therefore execute Pi locally, Claude locally, Pi in Daytona, and Claude in Daytona. We do not need separate deployment services for those combinations. Additional runner replicas are a scaling and ownership concern, not a harness-selection requirement.
21+
22+
## Why the current setup is hard to explain
23+
24+
Configuration grew in several directions at once:
25+
26+
- The backend locates the runner with `AGENTA_RUNNER_INTERNAL_URL`.
27+
- The runner defaults a sandbox with `SANDBOX_AGENT_PROVIDER`.
28+
- Daytona uses ambient `DAYTONA_*` variables shared with code evaluation.
29+
- Agent-only overrides such as `DAYTONA_SNAPSHOT_AGENT` sit beside shared defaults.
30+
- `AGENTA_AGENT_SANDBOX_PI_INSTALLED` asks the operator to describe a runtime fact that the runner can detect.
31+
- `AGENTA_SESSION_HARNESS_MOUNTS` exposes an internal implementation choice as a deployment policy.
32+
- Self-managed Pi authentication causes the runner to discover its own Pi login and upload it into Daytona.
33+
- The API, Services, runner, and browser each carry pieces of provider availability.
34+
35+
This makes a Compose file look like several partially overlapping products. An operator cannot tell which variables are read by the runner, which belong to the code evaluator, which describe policy, and which are only workarounds for one snapshot.
36+
37+
## The live failure that exposed the boundary
38+
39+
The local deployment on port 8280 had two runner-like services:
40+
41+
- the normal Compose runner, which had the Daytona API key;
42+
- a subscription sidecar, which held local harness login state but had no Daytona API key.
43+
44+
Services was pointed at the subscription sidecar. A request that selected Daytona reached that runner and failed with:
45+
46+
~~~text
47+
Authentication credentials not found. Set DAYTONA_API_KEY, or both
48+
DAYTONA_JWT_TOKEN and DAYTONA_ORGANIZATION_ID.
49+
~~~
50+
51+
Nothing was wrong with the user's Daytona account. The request reached a runner deployment that was not configured for Daytona. The setup made it easy to split capabilities accidentally.
52+
53+
The target local QA deployment is one trusted development runner with both providers enabled and explicit local subscription bootstrap assets. This is a development convenience, not a production multi-tenant topology. A local harness shares the runner container and can inspect other same-user processes through `/proc`.
54+
55+
## Trust boundaries
56+
57+
### Local provider
58+
59+
The daemon and harness are child processes inside the runner container. The working directory is a starting directory, not a confinement boundary. The harness can read files and environment visible to the container user.
60+
61+
Consequences:
62+
63+
- The runner container must receive only secrets that a local harness is allowed to reach.
64+
- Helm must not inject database, auth, cryptographic, or bucket-wide store credentials into the runner.
65+
- Subscription files mounted into the runner are available to trusted local agents. This is opt-in and single-tenant.
66+
67+
### Daytona provider
68+
69+
The runner stays in the deployment. The daemon and harness execute in a Daytona sandbox. Only material the runner explicitly sends should cross that boundary:
70+
71+
- the resolved model credential for the run;
72+
- workspace and instruction files;
73+
- scoped durable-mount credentials;
74+
- explicitly configured bootstrap assets;
75+
- runner-owned support binaries and configuration.
76+
77+
The runner's Daytona organization credential provisions the sandbox. It is infrastructure control-plane material and must never enter the sandbox environment.
78+
79+
## Product principles
80+
81+
### Configuration describes intent
82+
83+
Operators configure facts they own: enabled providers, a default, credentials, lifecycle values, and mounted bootstrap inputs. The runner detects runtime facts such as whether Pi is already installed.
84+
85+
### One owner per value
86+
87+
A Daytona API key used to provision agent sandboxes belongs to the runner. A Daytona key used by code evaluation belongs to the code evaluator. A shared ambient variable is not a simplification when the two consumers have different images, snapshots, permissions, and lifecycles.
88+
89+
### Explicit data movement
90+
91+
The runner should never infer that a missing model API key means "copy whatever login I happen to have." Runtime authentication is a declared mode, and bootstrap assets are a declared deployment input.
92+
93+
### Fail before side effects
94+
95+
Unknown providers, disabled providers, missing provider credentials, invalid bootstrap paths, and contradictory defaults fail at startup or before sandbox creation. Required mount failures fail the run. There is no silent provider fallback and no silent durable-to-ephemeral downgrade.
96+
97+
### Pre-production cleanup
98+
99+
These configuration surfaces are not a stable public API. The implementation should remove obsolete variables directly instead of carrying aliases, deprecation branches, or insecure compatibility modes.
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Documentation plan
2+
3+
Public documentation lands after the runtime and hosting contract is stable. The pages follow Diátaxis so readers do not have to extract a tutorial from a configuration reference.
4+
5+
## 1. Explanation: how agent runs execute
6+
7+
Purpose: build the mental model.
8+
9+
Explain:
10+
11+
- Services calls one runner service.
12+
- Harness and sandbox provider are independent axes.
13+
- One runner can support local and Daytona simultaneously.
14+
- Local runs share the runner container and are not an isolation boundary.
15+
- Daytona runs cross an explicit file and credential boundary.
16+
- Managed model keys are per-run data.
17+
- Runner provider credentials are deployment infrastructure credentials.
18+
- Session, agent, and transcript mounts provide different persistence.
19+
- Sessionless execution is the only implicit ephemeral case.
20+
21+
Do not include a complete environment-variable table or a copy-paste deployment recipe.
22+
23+
Proposed page: `docs/docs/self-host/agents/how-agent-runs-execute.mdx`.
24+
25+
## 2. Tutorial: run your first local agent with your own subscription
26+
27+
Purpose: teach one end-to-end learning path for a trusted personal self-hosted deployment.
28+
29+
The tutorial should:
30+
31+
1. start a clean OSS Compose stack;
32+
2. locate the single runner service;
33+
3. export or locate a supported Pi or Claude login;
34+
4. mount the credential source read-only using a commented Compose example;
35+
5. enable a local-only `harness-auth` bootstrap asset;
36+
6. start the stack;
37+
7. create a self-managed agent;
38+
8. run a prompt and a tool call;
39+
9. inspect the runner's redacted startup summary;
40+
10. remove the mount and verify the self-managed run fails clearly.
41+
42+
Safety callouts:
43+
44+
- one personal subscription is for that operator, not all users of a shared deployment;
45+
- local agents can inspect files visible to the runner container;
46+
- use API keys or provider-approved organization authentication for multi-user deployments;
47+
- do not target the auth asset at Daytona.
48+
49+
Proposed page: `docs/docs/self-host/agents/tutorial-local-subscription.mdx`.
50+
51+
## 3. How-to: enable Daytona for agent workflows
52+
53+
Purpose: configure remote sandbox execution with managed model credentials.
54+
55+
Cover:
56+
57+
- creating or selecting a Daytona API key;
58+
- enabling both `local,daytona` or Daytona alone;
59+
- choosing the default;
60+
- runner-scoped Daytona environment variables;
61+
- snapshot versus image;
62+
- target and lifecycle values;
63+
- public reachability requirements for durable storage;
64+
- a smoke test and common errors;
65+
- unsupported subscription combinations.
66+
67+
Do not reuse code-evaluator Daytona variables.
68+
69+
Proposed page: `docs/docs/self-host/agents/how-to-daytona.mdx`.
70+
71+
## 4. How-to: customize the runner runtime
72+
73+
Purpose: add binaries, certificates, and deterministic files.
74+
75+
Cover two separate mechanisms:
76+
77+
- build a custom runner image for installed programs;
78+
- use bootstrap assets for per-run files or directories.
79+
80+
Explain local and Daytona materialization. Keep arbitrary scripts, VPN hooks, and plugins out of version 1.
81+
82+
Proposed page: `docs/docs/self-host/agents/how-to-custom-runner.mdx`.
83+
84+
## 5. How-to: connect an external runner
85+
86+
Purpose: deploy the whole runner elsewhere and point Services at it.
87+
88+
Cover:
89+
90+
- `AGENTA_RUNNER_INTERNAL_URL`;
91+
- the runner token;
92+
- private networking and TLS termination;
93+
- the request-scoped callback credential and why no static runner API key is mounted;
94+
- provider and bootstrap configuration on the external runner;
95+
- health versus capabilities;
96+
- storage reachability;
97+
- failure diagnosis.
98+
99+
Do not describe a split where only the harness runs in another local container. The supported relocation unit is the whole runner.
100+
101+
Proposed page: `docs/docs/self-host/agents/how-to-external-runner.mdx`.
102+
103+
## 6. Reference: runner configuration
104+
105+
Purpose: provide exact names, types, defaults, readers, secret classification, and conflicts.
106+
107+
Generate the table from the typed configuration schema if practical. Group by:
108+
109+
- Services-to-runner routing;
110+
- runner server;
111+
- enabled/default sandbox providers;
112+
- Daytona;
113+
- session lifecycle;
114+
- bootstrap;
115+
- callback API;
116+
- intentionally internal debug settings.
117+
118+
Every variable must identify:
119+
120+
- semantic role;
121+
- consumer;
122+
- default;
123+
- whether empty is valid;
124+
- whether it is secret;
125+
- Compose location;
126+
- Helm values path;
127+
- conflicts and startup validation.
128+
129+
Proposed page: `docs/docs/self-host/agents/runner-configuration.mdx`.
130+
131+
## 7. Troubleshooting reference
132+
133+
Organize by symptom, cause, and exact check:
134+
135+
- "Authentication credentials not found" for Daytona;
136+
- requested provider disabled;
137+
- runner unreachable or unauthorized;
138+
- missing local subscription asset;
139+
- remote subscription unsupported;
140+
- snapshot or harness missing;
141+
- session mount signing failed;
142+
- store unreachable from Daytona;
143+
- FUSE transport disconnected;
144+
- capability picker stale.
145+
146+
The error text in docs must be copied from the final structured errors, not invented before implementation.
147+
148+
## 8. Existing page migration
149+
150+
After new pages exist:
151+
152+
- split the current self-host configuration page so its runner section links to the focused reference;
153+
- rewrite guides 07, 08, and 09 around the new how-to boundaries;
154+
- remove shared Daytona snapshot wording;
155+
- remove all old variables;
156+
- remove instructions that rely on an extra subscription sidecar;
157+
- keep redirects for published URLs if any, but do not keep environment aliases;
158+
- update navigation and cross-links;
159+
- verify OSS and EE examples separately.
160+
161+
## 9. Documentation acceptance
162+
163+
- A new reader can explain the runner topology after the explanation page.
164+
- A trusted local operator can complete the subscription tutorial without hidden files.
165+
- A Daytona operator never mounts or uploads a subscription credential.
166+
- Compose comments are sufficient to discover the opt-in local subscription path.
167+
- The reference contains no variable absent from code.
168+
- Search finds no removed variable in public docs.
169+
- Every troubleshooting entry maps to an automated error-path test.

0 commit comments

Comments
 (0)