Skip to content

docs(rfc): propose gateway MCP for creating child sandboxes (RFC 0021) - #4721

Draft
patkraw wants to merge 28 commits into
NVIDIA:mainfrom
patkraw:docs/4574-gateway-mcp-rfc/patkraw
Draft

patkraw wants to merge 28 commits into
NVIDIA:mainfrom
patkraw:docs/4574-gateway-mcp-rfc/patkraw

Conversation

@patkraw

@patkraw patkraw commented Oct 19, 2026

Copy link
Copy Markdown

Summary

RFC 0021 proposes letting an agent inside a sandbox create child sandboxes (sub-agents and tool sandboxes) through an MCP endpoint served by the gateway, within a grant the user sets. Every child's policy is proved to fit inside its parent's, depth, breadth and total limits hold across the tree, the agent holds no credential, and stopping a parent stops its children first.

Related Issue

Closes #4574

Maintainers accepted the issue and assigned RFC 0021. If you'd prefer a separate RFC tracking issue (as with #4610), I'll switch the link.

Changes

  • Add rfc/0021-gateway-mcp-sandbox-creation/README.md in draft state.

Implementation

A proof of concept is on patkraw/OpenShell branch gateway-mcp-poc, with hands-on tutorials for the CLI, the SDKs and MCP. Its end-to-end checks (74/74) run against a dev gateway with the Docker driver. Feedback is especially welcome on the grant model, the policy boundary, the API, and the name ("gateway MCP" may be confused with MCP gateways such as agentgateway).

Testing

  • Checks appropriate to the affected code and behavior pass (documentation-only change)
  • Unit tests added/updated (not applicable)
  • E2E tests added/updated (not applicable)

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (this PR is the RFC)

Draft proposal for letting agents create sub-agent and tool sandboxes
through a gateway-hosted MCP endpoint, with user-set grants, lineage,
prover-checked ceilings and a supervisor door. RFC number to be assigned
from the originating issue.

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
…olicy as a maximum; host callers act as the user; MCP on by default

Also adds sandboxes-from-sandboxes use cases (RL environments) and future
work on returning results and agent-to-agent communication.

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
…/2 proxy, or gRPC-Web/Connect)

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
… heading

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
…nt profiles as future work

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
…e 2, grant tools and token audience

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
…lags; grant get

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
…esult collection

- grant create replaces a revoked or expired grant for the running tree.
- Grant listing and changes are limited to the grant's user or a
  Workspace Admin, checked before any change.
- request_id is scoped to the parent and reserved with the slot;
  duplicates get IN_PROGRESS; limits are charged atomically.
- The walkthrough collects a result with an explicit done signal.
- The proof-of-concept paragraph points to the commit-specific result.

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
…nters, revocation and production gaps

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
Signed-off-by: patkraw <pkrawczuk@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Oct 19, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

All contributors have signed the DCO ✍️ ✅
Posted by the DCO Assistant Lite bot.

@patkraw

patkraw commented Oct 19, 2026

Copy link
Copy Markdown
Author

I have read the DCO document and I hereby sign the DCO.

…r and why tool sandboxes skip depth

Signed-off-by: patkraw <pkrawczuk@nvidia.com>
the gateway proves the child's policy against the parent's current policy,
without provider layers. The lead's children are bounded by the lead's policy,
its grandchildren by its children's policies, and so on, so each level can only
narrow: `child ≤ parent ≤ global maximum`, the model in the spike on agents

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I have a few thoughts around the policy layers here.

Let's assume an use case like this:

There is a lead sandbox running harness and a child sandbox using tools (e.g. send an email).

I want my child sandbox to have access to sending emails, but I actually wouldn't want to have the parent to have that permission. If the parent has that permission, it means it could either send the email itself, or delegate it to the child, right?

In this model, you couldn't tell what the parent actually needs vs permissions that are in parent's policy only because its children need it (and removing a permission from a child would need to propagate to the parent?).

I wonder if there are other ways to organize this.

One thought is that what this RFC describes is another way of grouping sandboxes, today we have global, workspace, sandbox. With this we have global, workspace, tree, sandbox. Perhaps there is a way to represent this "maximum tree policy" and "current tree policy" on the tree level?

Then, if the child requests permissions, we don't evaluate them through the parent's policy, but through tree's policy.


That being said, the "parent's policy is a superset of all childred" is simpler to reason about, so that may be the best option after-all, but I'm also curious if other options were explored. CC @johnnygreco @vishwa-raman

@johnnygreco johnnygreco Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is a good question and is something we've debated a bit. My take is we should focus first on getting the functionality in with as little friction as possible. For example, trying to check that the child's policy is least-privilege would be best practice but still requires research.

In my mind the simplest approach is the child inherits the parents policy by default but can have a less permissive policy (which our policy prover can guarantee). A child can never have permission to do something the parent can't do. If a child needs more permissions, the parent needs to get them first.

^ This isn't the endgame, but feels defensible and is easy to reason about.

The question of grouping sandboxes is interesting. I think the idea of a "tree" could be useful as a generic framing, since this will be used for more than spawning subagents specifically.

sync from the supervisor, or an approved proposal. A sandbox in a tree can
never be widened past its ceiling after it starts.

**Global policy as a maximum.** A gateway-global policy is meant to state what

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel like the maximum policy and one of use cases for the global policy (i.e. kill switch to limit all running sandboxes), these 2 should be separate.

With this model, if you set the global_policy_mode to maximum, would all policies of running sandboxes be updated to adhere to that maximum?

Also, I could see value of setting maximum policy on a workspace level. It could be worth checking if there are any indications this would be useful, perhaps global maximum policy is what's needed.

Comment on lines +213 to +237
```proto
service SandboxChildren {
rpc CreateChildSandbox(CreateChildSandboxRequest) returns (CreateChildSandboxResponse) {
option (authorization) = { auth_mode: "dual" workspace_role: "user" scope: "sandbox:write" };
}
rpc WaitChildSandbox(WaitChildSandboxRequest) returns (WaitChildSandboxResponse) {
option (authorization) = { auth_mode: "dual" workspace_role: "user" scope: "sandbox:read" };
}
rpc ExecChildSandbox(ExecChildSandboxRequest) returns (ExecChildSandboxResponse) {
option (authorization) = { auth_mode: "sandbox" };
}
rpc GetChildSandboxFile(GetChildSandboxFileRequest) returns (GetChildSandboxFileResponse) {
option (authorization) = { auth_mode: "sandbox" };
}
rpc StopChildSandbox(StopChildSandboxRequest) returns (StopChildSandboxResponse) {
option (authorization) = { auth_mode: "dual" workspace_role: "user" scope: "sandbox:write" };
}
rpc ListChildSandboxes(ListChildSandboxesRequest) returns (ListChildSandboxesResponse) {
option (authorization) = { auth_mode: "dual" workspace_role: "user" scope: "sandbox:read" };
}
rpc GetSandboxLimits(GetSandboxLimitsRequest) returns (GetSandboxLimitsResponse) {
option (authorization) = { auth_mode: "dual" workspace_role: "user" scope: "sandbox:read" };
}
}
```

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Alternative approach for most of this API would be to utilize the existing rpcs (e.g. CreateSandbox, ExecSandbox). The authorization layer could then determine limits around what's allowed in a given context.

E.g. if the way to request a child sandbox is different enough from creating a regular sandbox, having separate APIs may be the right approach and it may make it easier to control rules that govern both approaches. At the same time, new APIs come with a cost, we'd need to figure out if/how to include these in the gateway interceptors, etc.

with an index on `parent_sandbox_id` for cascade stop. A child's workspace is
always its parent's.

Lineage is deliberately a separate record rather than an owner field on the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm curious about this one: why wouldn't we expand the sandbox record? Do we need to record the owner, if our authorization works on a workspace level anyways?

Comment on lines +264 to +271
### Grants

A grant is a budget for one tree of sandboxes, set by the user. It answers four
questions: may this tree create sandboxes at all, how large may it grow, which
program may ask, and whether agents may write their children's policies
(`child_policy`). What a child may be given is not part of the grant: every
child is bounded by its parent's policy (see
[Ceiling and policy proof](#ceiling-and-policy-proof)).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm wondering about the naming here.

As I read it, the idea here is to indicate that this sandbox can be a root of a tree, right? And if yes, what are the limits.

It feels like this could be seen as sandbox policy, but for control plane (where our existing sandbox policy is for data plane).
The idea here is to answer questions like: can this sandbox create sandboxes, what's allowed depth of the tree. Perhaps it could also answer "what's the maximum policy for this sandbox"?

Perhaps grant could work, but I feel like openshell sandbox create --grant-... isn't very clear on what that grant is for. In my mind the clearest would be something like openshell sandbox create --tree-policy ... or --subagent-policy or --child-policy (this is too overloaded).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

+1 that --grant isn't explicit enough. I had the same reaction @pimlock. Perhaps we should seriously consider "tree" as a noun here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could it suffice to just have tree-depth as one parameter in the create command? Like openshell sandbox create --tree-depth 3.

If it's set to 0, the sandbox can't spawn other sandboxes. And I think we should allow all parents to propose the child's sandbox.

Comment on lines +46 to +50
- **Harnesses work around this with host services.** Prototypes for Pi and
OpenWorker run a service on the host that holds a user credential and creates
sandboxes when an agent asks. Each such service is harness-specific, keeps its
security checks outside OpenShell, and becomes a second source of truth for
who may create what. Operators have to trust and audit each one separately.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

How would this MCP work in terms of integrating with harnesses?

I can imagine that harnesses have a "spawn-subagent" tool that is built into the harness and the harness' code somehow implements it (launches a thread/subprocess, runs a prompt and can see the progress/output).

Our MCP would somehow compete with that built-in tool? So if I told my agent "spawn a subagent to do X" it would somehow decide between these two? I'm trying to figure out how would this work in a harness-agnostic way.

A deeper integration would require the harness to know about OpenShell, right? This is possibly one of the hooks that the Harness Protocol would support? CC @johnnygreco

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Great question that really hits at the challenge for OpenShell to be truly harness agnostic.

MCP was chosen here to expose the control plane to all harnesses, rather than be a deep integration point for a specific harness. For example, if you said "use the openshell mcp to launch a secured agent on this task: ...", it should work out of the box for most harnesses (@patkraw is planning to do some e2e tests that dig into this more). And this should work inside and outside of sandboxes.

For a deeper integration, harnesses would likely want to use the primitives directly – e.g., through the SDK. The RFC calls out that under the hood, the CLI/MCP/SDK should all use the same primitives.

We plan to have a Dev Note or two showing a deep integration with Pi. And like you point out @pimlock, this becomes super relevant for the protocols we have been discussing.

by OpenShell, proved against a ceiling with the policy prover, limited by a
grant that the user sets when starting the tree's first sandbox or attaches to
it later, and recorded in a lineage so that stopping a parent stops its children
first.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice work on this RFC! 👏

I went through most of it and started a few discussions points. I need more time to organize my thoughts about it, so will add more things over the next few days.

Few high-level comments (adding here, rather than a global comment, since global comments don't support threads):

  • It's worth exploring what the gateway interceptors can do today, perhaps some of the limits could be implemented as interceptors initially to keep the scope smaller (e.g. limiting depth/breath could be implemented with interceptors if we added the lineage info). Perhaps it still makes sense to support these in-tree, but worth exploring as an option.
  • (nit) I feel like the RFC name doesn't represent the main feature here, which it to have a way to spawn and control child sandboxes. The MCP is just a mechanism to expose that feature in the sandbox, but the main feature is the trees.

@FrostGod

FrostGod commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Could we replace MCP with a skill that teaches the agent to spawn and manage child sandboxes through a simple command?
The supervisor already communicates with the gateway over authenticated gRPC. We could expose the child operations through it, keeping credentials with the supervisor and enforcing grants, policy checks and limits in the gateway.
For agents that already execute commands, what does MCP add that this approach wouldn’t cover?

flowchart LR
    A["Agent + skill"] -->|"Spawn command"| S["Supervisor"]
    S -->|"Authenticated gRPC"| G["Gateway checks permissions and creates child"]
Loading

@FrostGod

FrostGod commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

@pimlock please take a look at my first comment, I feel MCP might be a bit of overkill here in this scenario, but the concept is a real usecase
I doubt this is a use case for MCPs though

@FrostGod

Copy link
Copy Markdown
Collaborator

Is a live-file handshake acceptable for the first supported harnesses? Specify the keep-alive/acknowledgment contract and test parent delays and worker crashes. Consider retaining a small final result if one-shot agents are a core use case.

@johnnygreco

Copy link
Copy Markdown
Collaborator

Could we replace MCP with a skill that teaches the agent to spawn and manage child sandboxes through a simple command? The supervisor already communicates with the gateway over authenticated gRPC. We could expose the child operations through it, keeping credentials with the supervisor and enforcing grants, policy checks and limits in the gateway. For agents that already execute commands, what does MCP add that this approach wouldn’t cover?

flowchart LR
    A["Agent + skill"] -->|"Spawn command"| S["Supervisor"]
    S -->|"Authenticated gRPC"| G["Gateway checks permissions and creates child"]
Loading

@FrostGod – MCP is something @pimlock, @drew, and myself have been talking about adding for a long time. It's purpose will be much more than spawning subagents. It's about exposing the control plane via MCP, which has standard discoverability for all major harnesses.

@FrostGod

Copy link
Copy Markdown
Collaborator

@johnnygreco, if it has more usecases that's going to be coming along I think that should be fine 😄 then ,

The angle I am coming from is purely memory optimization, if we are going to introduce MCP only for spawning sub-agents, I would say skills should be enough, you don't need any additional MCP server per gateway, and MCP tools loaded into each sandbox during boot up.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(gateway): let an agent in a sandbox create child sandboxes within a user-set grant (gateway MCP + API)

5 participants