Skip to content

docs(agent-workflows): Plan Daytona secret delivery#5223

Draft
mmabrouk wants to merge 2 commits into
big-agentsfrom
docs/daytona-secret-delivery
Draft

docs(agent-workflows): Plan Daytona secret delivery#5223
mmabrouk wants to merge 2 commits into
big-agentsfrom
docs/daytona-secret-delivery

Conversation

@mmabrouk

Copy link
Copy Markdown
Member

Context

The runner currently resolves model-provider API keys and passes them to Daytona sandboxes as plaintext environment variables. That makes the values readable from the agent process and shell. Daytona Secrets can reduce direct exposure through proxy-side substitution, but their organization scope, resource lifecycle, host matching, and credential-format limits require explicit controls.

This PR is design-only. It does not update the Daytona SDK, create Daytona resources, or change runtime behavior.

Proposed direction

  • Create ephemeral per-sandbox Daytona organization Secret leases only for the exact credentials required by that run.
  • Use exact, non-empty HTTP(S) host allowlists and opaque credential substitution.
  • Never sync the whole Agenta vault or expose Agenta control-plane credentials.
  • Fail closed for unsupported signing, structured JSON, native-protocol, or ambiguous custom-secret cases.
  • Require a live security spike, pinned SDK behavior, crash cleanup, and a janitor before rollout.

How to review

  1. Start with README.md and context.md for scope and threat model.
  2. Check research.md for Daytona feasibility, SDK upgrade risk, and open evidence gaps.
  3. Review design.md for the delivery contract and lease lifecycle.
  4. Review plan.md and qa.md for implementation slices and exit gates.

Validation

  • Confirmed the branch diff contains exactly the seven planning files under docs/design/agent-workflows/projects/daytona-secret-delivery/.
  • git diff --check passes.
  • No runtime tests were run because this PR changes documentation only.

Decision requested

Please confirm whether the first implementation scope should include the Phase 0 security spike plus standard direct model-provider keys and exact-host custom-provider keys. Custom text secrets remain out of scope until each consumer and destination-host policy is explicit.

@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 11, 2026 3:59pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b96a16fa-a515-4e67-8e3d-1bcf7e249e27

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/daytona-secret-delivery

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mmabrouk

Copy link
Copy Markdown
Member Author

🤖 The AI agent says:

🔸 Decision needed: Please review whether the first implementation scope should cover the Phase 0 security spike plus standard direct provider keys and exact-host custom-provider keys.

The main points to challenge are the use of short-lived organization Secrets as per-sandbox leases, the exact host allowlist requirement, the fail-closed credential compatibility matrix, and the cleanup and janitor gates.

Recommendation: approve the security spike first, then implement only credential classes whose values remain opaque and whose destination hosts are known before sandbox creation.

@mmabrouk mmabrouk added the needs-review Agent updated; awaiting Mahmoud's review label Jul 11, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 08faae7d-2848-4432-8222-7629b2b64609

📥 Commits

Reviewing files that changed from the base of the PR and between 1666116 and 9701fb7.

📒 Files selected for processing (7)
  • docs/design/agent-workflows/projects/daytona-secret-delivery/README.md
  • docs/design/agent-workflows/projects/daytona-secret-delivery/context.md
  • docs/design/agent-workflows/projects/daytona-secret-delivery/design.md
  • docs/design/agent-workflows/projects/daytona-secret-delivery/plan.md
  • docs/design/agent-workflows/projects/daytona-secret-delivery/qa.md
  • docs/design/agent-workflows/projects/daytona-secret-delivery/research.md
  • docs/design/agent-workflows/projects/daytona-secret-delivery/status.md

Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/design.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/plan.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/qa.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/research.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/design.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/design.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/design.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/design.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/research.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/research.md Outdated
Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/research.md Outdated
(`services/runner/src/engines/sandbox_agent/sandbox-reconnect.ts:25-77`). A credential lease must
live as long as that sandbox, including stopped or archived periods.

The current `sandbox-agent` Daytona provider has no `pause` method. Its generic `pauseSandbox()`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we fixed this in a patch. see current state of worktree and update accordingly

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says:

Updated from the current worktree. Native pause, reconnect, and delete now exist in the runner-owned Daytona wrapper, and Agenta auto-archive configuration is removed. The design now builds Secret lifecycle on that wrapper. It does not propose another pause fix or an upstream Secret patch.

Comment thread docs/design/agent-workflows/projects/daytona-secret-delivery/research.md Outdated
| `credentials[].value` | credential | Agenta vault, resolved per run and never logged or persisted by the runner |
| `credentials[].usage` | credential-use contract | resolver, stable for that credential kind |

Destination hosts are derived from the resolved consumer route, not transported in a sibling

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says:

🔎 Review note: Host ownership moved to the consumer route. Please check the choice to make the resolver emit effective endpoints for standard providers instead of maintaining a second provider-host registry in the runner.

2. Creates one organization Secret per isolated credential binding with that non-empty host list.
3. For environment bindings, passes `{environmentName: daytonaSecretName}` in the sandbox create
`secrets` map instead of copying the value into `envVars`.
4. For HTTP MCP header bindings, passes the Daytona placeholder as the header value in the ACP

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says:

🔎 Review note: HTTP MCP is now in scope. This line carries the unproven part: we need a live test that Daytona substitutes a placeholder placed directly in an ACP header. The fallback is an environment-backed MCP adapter or runner-side gateway, not plaintext.

| Exact allowlisted HTTPS host | Host receives plaintext; sandbox sees placeholder. |
| Different host | Placeholder or rejected request; never plaintext. |
| Subdomain | No plaintext. |
| Any wildcard declaration | Rejected before Secret creation. |

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says:

🔎 Review note: The wildcard ambiguity is removed. The first version rejects every wildcard even though Daytona supports them.


**Recommendation:** Option A. A separate API key alone does not narrow the permission.

## 2. What happens to SigV4 and Vertex service-account connections?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says:

🔸 Decision needed: My recommendation is to keep SigV4 and Vertex ADC usable only as an explicit non_isolated migration mode, while strict operators can reject them. Bedrock bearer and Azure API-key paths remain isolation candidates.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's go with option A for the moment.


Daytona grants `manage:secrets` across an organization, not only Agenta-created Secrets.

- **Option A:** require a dedicated Daytona organization and dedicated runner credential.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says:

🔸 Decision needed: Can production require a dedicated Daytona organization for Agenta sandboxes? A dedicated runner key alone still has organization-wide manage:secrets authority.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No that is not reasonable. Use a shared organization. In any case we use Daytona only for this

- Confirmed sandbox delete: delete every Secret in the lease, then mark cleanup complete.
- Secret cleanup failure: queue a durable retry and report cleanup as pending.

Archive is not part of the Agenta lifecycle. The current warm-Daytona create specification omits

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says:

🔎 Review note: This is aligned with the current warm-Daytona worktree: Agenta no longer configures archive. Reconciliation still treats a manually archived sandbox as existing so it cannot delete a valid lease.


### Credential changes

The first version does not rotate a live lease in place. The existing session compatibility path

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says:

🔎 Review note: Version one now avoids live Secret rotation. Credential epoch mismatch already invalidates reuse, so fresh sandbox plus fresh lease is simpler and avoids the 15-second propagation window. Later rotation must reassert and verify hosts.

@mmabrouk

Copy link
Copy Markdown
Member Author

🤖 The AI agent says:

This is the second version. Here is what changed:

  • Replaced the compatibility proposal with one consumer-owned credential contract for both local and Daytona.
  • Made the effective model endpoint or validated HTTP MCP URL the source of the exact host.
  • Added HTTP MCP credentials to the design, with direct ACP-header substitution as a Phase 0 live gate.
  • Split Bedrock bearer tokens from SigV4 credentials. Azure API keys remain supported; SigV4 and Vertex ADC now use an explicit non-isolated mode or a gateway.
  • Updated the lifecycle to the current runner-owned pause, reconnect, and delete wrapper and removed Agenta archive behavior.
  • Chose fresh sandbox plus fresh lease on credential-epoch changes for version one.
  • Made custom-secret resolution fail closed and rejected every wildcard.
  • Added organization isolation as a rollout prerequisite.
  • Added open-questions.md with eight decisions that must be resolved before implementation.

I answered every original comment inline and added fresh inline review notes on the revised lines. The most important decisions are the production Daytona organization boundary, the policy for SigV4 and Vertex ADC, and ownership of the effective provider endpoint.

@mmabrouk mmabrouk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Custom and Azure connections already carry a base URL. Standard providers and regional Bedrock
routes also need an exact effective endpoint so the host policy matches the client request.

- **Option A:** the Python connection resolver emits the effective endpoint for every deployment.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

option a lgtm

The runner can obtain the Secret placeholder and put it in ACP session configuration, but Daytona's
documentation demonstrates environment injection rather than this exact path.

**Recommendation:** make this a Phase 0 live gate. If direct placeholder substitution fails, either

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Session state is already durable, but the janitor also needs organization-wide enumeration and
partial-provisioning recovery.

**Recommendation:** define a small runner-owned lease record in the control plane rather than

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree

Users may need to know that a selected cloud credential remains readable inside Daytona. Exposing
mechanism-specific Daytona settings in the agent contract would be the wrong abstraction.

**Recommendation:** expose a portable credential-delivery requirement such as `isolated_required`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no will do it in the docs

Google now documents API-key authentication for some Vertex Gemini paths, while Agenta's current
Vertex connection uses Application Default Credentials through `GOOGLE_APPLICATION_CREDENTIALS`.

**Recommendation:** keep Vertex API keys out of the first scope unless the provider-model contract

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes out of scopee

Secrets first shipped in SDK 0.192.0. The current package is 0.196.0, while the runner still uses
`@daytonaio/sdk` 0.187.0. Self-hosted control-plane compatibility is not yet proven.

**Recommendation:** select one exact `@daytona/sdk` version after the live spike and add startup

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's start by an upgrade to the right version and obviously test

@mmabrouk mmabrouk added implementing Design approved; implementation queued/in progress (not awaiting review) and removed needs-review Agent updated; awaiting Mahmoud's review labels Jul 11, 2026
mmabrouk pushed a commit that referenced this pull request Jul 11, 2026
…connect

Trust the stored sandbox pointer instead of comparing a create-spec fingerprint.
A resumed Daytona turn reconnects the parked instance by id. Snapshot, image, and
target drift is accepted as per-conversation version pinning. Network policy is
converged at reconnect: read the live networkBlockAll/networkAllowList and call
updateNetworkSettings only when they differ from the run's plan. Per-turn env sync
is deferred to the daytona-secret-delivery direction (#5223).

Removes the session_states.sandbox_fingerprint column and migration oss000000011,
the createSpecFingerprint/buildResolvedDaytonaCreate helpers, and the fingerprint
fields on the pointer read/write. Keeps the turn-index compare-and-set guard on
sandbox_id, which uses only pre-existing columns.

Claude-Session: https://claude.ai/code/session_018MaXPNpvzN22kngHno3VMj
mmabrouk added a commit that referenced this pull request Jul 11, 2026
…every turn (#5225)

* feat(runner): warm daytona sessions slice 1 - provider pause/reconnect, pointer guard, typed teardown

Slice 1 of the warm-daytona-sessions plan (PR #5214): the correctness base,
with parking still inert (every teardown reason still deletes).

- daytona provider wrapper adds pause/reconnect with a bounded state machine
  over Daytona transitional states; deleteSandbox helper; create-spec
  fingerprint (snapshot/image/target/env NAMES/network policy).
- sandbox-agent package patch: a failed pause keeps the provider handles so
  the delete fallback works; a reconnected sandbox that fails later attach
  steps is paused, not leaked.
- sessions API: nullable session_states.sandbox_fingerprint column; the
  sandbox pointer write is guarded by a compare-and-set on
  data.latest_turn_index (sandbox_turn_index token; tokenless writes keep
  today's behavior).
- runner: pointer write is awaited, carries the fingerprint and guard token,
  and moved after continuity hydrate so a cold restart sends the right token;
  reconnect only on fingerprint match, best-effort delete on mismatch.
- typed teardown reasons replace the keepWarm boolean; reason-to-disposition
  mapping with PARK_CLEAN_RESUMABLE_TURNS=false (Slice 2 flips it).

Claude-Session: https://claude.ai/code/session_018MaXPNpvzN22kngHno3VMj

* feat(runner): warm daytona sessions, slices 2-5 - park-to-stopped, provider pools, park-to-running, verified live

Completes the warm-daytona-sessions plan (PR #5214) on top of the slice-1
correctness base.

- Slice 2 (park-to-stopped active): clean resumable Daytona turns and idle
  shutdown stop the sandbox instead of deleting it; stop timer default 15
  minutes; auto-archive removed entirely (create field, env override, compose
  and helm forwarding); terminal-vs-transient reconnect classification (only
  not-found/error/destroyed/unknown clear the stored pointer, guarded); clean
  remount before every remote mount so a reattached sandbox never serves a
  stale FUSE mount.
- Slice 3 (provider-aware pools): one SessionPool per provider; local env vars
  and behavior unchanged; Daytona configured by
  AGENTA_RUNNER_DAYTONA_SESSION_IDLE_TTL_MS (0 disables, no separate flag) and
  AGENTA_RUNNER_DAYTONA_SESSION_MAX_WARM (default 20); pool evictions carry
  typed teardown reasons; unknown providers fail closed to cold.
- Slice 4 (park-to-running warm slots): strict awaited capacity for the
  Daytona pool (a stopping sandbox keeps its warm slot until the stop
  confirms, so the billed count cannot overshoot); overflow parks to stopped;
  one-time activity refresh at live park, no recurring pings.
- Slice 5: per-stage [timing] log lines in acquire; Daytona live-window
  default 120000 ms after the E3 gate passed; the TTL env parse accepts 0 as
  a valid off switch (a positive-only parse would silently re-enable the
  default).
- Docs and config: the two new env vars forwarded in compose, env examples,
  and helm; self-host pages updated to the three-state lifecycle.
- Live E3 verification: cold 12.5 s, live-warm 1.39 s, stopped-restart 7.7 s;
  one instance served all three turns; expiry observed as stop; SIGTERM drain
  observed as stop; zero sandbox leaks.

Requires alembic migration core_oss oss000000011 (nullable
session_states.sandbox_fingerprint).

Carries shared-file hunks from the unmerged lanes feat/keepalive-project-scope
(protocol.ts runContext scope and pool key preference) and
fix/sessions-continuity-review (models.py replica_id validation, mdx edits)
per the coordination board's shared-file rule.

Claude-Session: https://claude.ai/code/session_018MaXPNpvzN22kngHno3VMj

* feat(runner): drop sandbox fingerprint, converge network policy at reconnect

Trust the stored sandbox pointer instead of comparing a create-spec fingerprint.
A resumed Daytona turn reconnects the parked instance by id. Snapshot, image, and
target drift is accepted as per-conversation version pinning. Network policy is
converged at reconnect: read the live networkBlockAll/networkAllowList and call
updateNetworkSettings only when they differ from the run's plan. Per-turn env sync
is deferred to the daytona-secret-delivery direction (#5223).

Removes the session_states.sandbox_fingerprint column and migration oss000000011,
the createSpecFingerprint/buildResolvedDaytonaCreate helpers, and the fingerprint
fields on the pointer read/write. Keeps the turn-index compare-and-set guard on
sandbox_id, which uses only pre-existing columns.

Claude-Session: https://claude.ai/code/session_018MaXPNpvzN22kngHno3VMj

* fix(runner): service CodeRabbit review on warm daytona sessions

- Gate the sandbox pointer write on the Daytona plan so a local run
  cannot overwrite a conversation's remote pointer.
- Hydrate the continuity store before the guarded pointer clear so a
  post-restart clear carries the durable turn index.
- pause() throws for the error sandbox state so teardown falls back to
  delete instead of reporting a parked sandbox with a stale pointer.
- Catch live-park activity-refresh failures; the hook is best-effort.
- Dedupe writeSandboxPointer/clearSandboxPointer into one guarded PUT.
- Assert status_code in the tokenless pointer acceptance test.
- ruff format on the warm_daytona_probe QA script (CI red).

Claude-Session: https://claude.ai/code/session_018MaXPNpvzN22kngHno3VMj

* fix(hosting): restore the MCP gate default to off

The branch flipped AGENTA_AGENT_MCPS_ENABLED from false to true across
the compose files, env examples, and the helm comment. The flip is
unrelated to warm Daytona sessions and contradicts the SDK gate and the
helm template, which both default off. Restore false everywhere.

Claude-Session: https://claude.ai/code/session_018MaXPNpvzN22kngHno3VMj

---------

Co-authored-by: Mahmoud Mabrouk <team@agenta.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

implementing Design approved; implementation queued/in progress (not awaiting review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant