feat(gateway): Declarative sandbox specification
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 8.7k
- Forks
- 1.3k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 253
Description
Problem Statement
Every sandbox today is created imperatively — through the CLI (openshell sandbox create) or the CreateSandbox gRPC RPC. There is no way to declare sandboxes as part of the gateway configuration so they are created automatically on startup.
This gap affects several deployment scenarios:
- Reproducible environments. Teams that need the same sandboxes across gateway restarts must script
openshell sandbox createcalls on top of the gateway startup sequence, adding fragile ordering dependencies and requiring the CLI alongside the gateway. - Appliance-style deployments. When the gateway runs as a system service (RPM/Podman), operators want sandboxes to appear after boot without manual intervention. The existing
resume_persisted_sandboxesmechanism only restarts containers from a prior session — it cannot provision new sandboxes from a config. - Restricted and static environments (automotive, edge). In automotive ECUs, edge appliances, and other locked-down environments, the sandbox fleet is fixed at provisioning time and the system must come up fully operational without interactive API calls or external orchestration. These targets often have no CLI tooling, no human operator at boot, and no network path to a control plane — the gateway config file is the only input surface available.
- CI/CD and testing. Pipelines that spin up a gateway for integration testing must race shell scripts against gateway readiness to create sandboxes.
- GitOps. Organizations managing infrastructure through version-controlled config (Ansible, Helm, Terraform) cannot express desired sandbox state alongside gateway settings.
The gateway has a rich TOML configuration surface (RFC 0003) for everything — drivers, TLS, OIDC, middleware, interceptors — except the sandboxes it manages, which is its core value.
Proposed Design
Add a new [[openshell.sandboxes]] array-of-tables section to the gateway TOML config file. Each entry declares a sandbox the gateway should create on startup.
Configuration example
[[openshell.sandboxes]]
name = "dev-agent"
image = "ghcr.io/nvidia/openshell/sandbox:latest"
workspace = "default"
providers = ["openai", "github"]
policy = "/etc/openshell/policies/dev-agent.yaml"
gpu = 1
required = true
[openshell.sandboxes.labels]
team = "platform"
[openshell.sandboxes.environment]
RUST_LOG = "debug"
Supported fields
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Unique within workspace. Idempotency key on restart. |
image |
string | no | OCI image reference. Falls back to driver default_image. |
workspace |
string | no | Target workspace. Defaults to "default". |
providers |
string[] | no | Provider profile names to attach. |
policy |
string | no | Path to sandbox policy YAML file. |
log_level |
string | no | Supervisor log level. |
gpu |
integer | no | GPU count. Omit for none. |
cpu |
string | no | CPU limit (e.g., "500m", "2"). |
memory |
string | no | Memory limit (e.g., "512Mi", "4Gi"). |
labels |
table | no | Key-value labels. |
annotations |
table | no | Key-value annotations. |
environment |
table | no | Environment variables. |
required |
bool | no | When true, creation failure aborts startup. Default false. |
Startup reconciliation
The reconciliation runs in run_server() after resume_persisted_sandboxes and before the watch/reconcile loops:
- Parse
[[openshell.sandboxes]]entries from the config. - For each entry, check the persistence store for a sandbox with the same
namein the targetworkspace. - If a matching sandbox exists in a non-terminal phase, skip it.
- If no match exists, create the sandbox via the same internal path used by the gRPC handler.
- If creation fails: abort if
required = true, log a warning and continue otherwise.
Key behaviors
- Idempotent on restart. A sandbox created on a prior boot (or via the API) with the same name satisfies the declaration — no duplication.
- Resume interaction. Runs after
resume_persisted_sandboxes, so resumed Docker containers are found by name lookup and skipped. - Deletion + restart = recreation. Deleting a declarative sandbox via the API and restarting the gateway recreates it, providing a "reset to declared state" behavior.
- Config removal ≠ deletion. Removing a declaration from config does not delete the running sandbox. It becomes an ordinary API-managed sandbox.
- Annotation tagging. Created sandboxes are tagged
openshell.io/created-by: declarative-configfor observability.
Implementation components
DeclarativeSandboxConfigstruct inconfig_file.rswith#[serde(deny_unknown_fields)]- New
sandboxes: Vec<DeclarativeSandboxConfig>field onOpenShellRoot reconcile_declared_sandboxes()function incompute/wired intorun_server()- Helm chart
sandboxesvalue tree rendered into the gatewayConfigMap - Docs update to
docs/reference/gateway-config.mdx
Not in scope for this issue
- Warm pools / auto-scaling (maintain N instances of a spec)
- Hot-reload of sandbox declarations (requires gateway restart, consistent with RFC 0003)
- Full
SandboxSpecparity (exotic fields likeagent_socket,runtime_class_name, rawdriver_config) - Declarative deletion (removing a declaration does not delete the sandbox)
- Continuous reconciliation (startup-only, not a controller loop)
Alternatives Considered
External orchestration scripts. The status quo — wrap openshell sandbox create in shell scripts or systemd oneshot units that run after gateway readiness. Works but introduces race conditions, requires CLI installation, and separates sandbox intent from gateway config. Rejected because the config-integrated approach eliminates these issues.
Separate YAML sandbox manifest. A dedicated sandboxes.yaml file referenced from gateway.toml. Would align with the YAML policy format but adds a second config file format, complicates tooling and Helm rendering, and breaks from the single-file TOML model of RFC 0003. Could be revisited if the conf.d drop-in pattern (RFC 0003 Open Question #2) is implemented.
Full desired-state controller. Continuously reconcile sandbox state against declarations — delete on config removal, update on spec change, restart on crash. Significantly more complex and overlaps with a warm pool feature. Startup-only reconciliation is sufficient for the motivating use cases and can be extended incrementally.
Do nothing. Leave sandbox creation as imperative-only. Viable but leaves the configuration gap described above.
Agent Investigation
- Explored the gateway startup flow in
crates/openshell-server/src/lib.rs(run_server()). Declarative reconciliation fits naturally afterensure_default_workspace(line 432) andresume_persisted_sandboxes(line 434), before the watch/reconcile loops are spawned. - Examined the gateway config schema in
crates/openshell-server/src/config_file.rs. TheOpenShellRootstruct uses#[serde(deny_unknown_fields)]— adding asandboxes: Vec<DeclarativeSandboxConfig>field is straightforward. The pattern matches[[openshell.supervisor.middleware]]which already uses a TOML array-of-tables. - Reviewed the
CreateSandboxgRPC handler incrates/openshell-server/src/grpc/sandbox.rs. The internal creation path (spec validation, image resolution, provider validation, UUID generation, persistence, compute driver dispatch) can be reused directly. The only change is the request origin. - Confirmed
resume_persisted_sandboxes()incrates/openshell-server/src/compute/mod.rs(line 1291) only applies to the Docker driver and only restarts previously-existing containers — it has no declarative creation capability. - Checked the full
SandboxSpecproto definition (proto/openshell.protolines 376-398) and CLI flags (crates/openshell-cli/src/main.rslines 1317-1448) to select the declarative config field set. - Searched for "warm", "pool", "prestart", "pre-create", "auto-create", "declarative sandbox" across the codebase — no existing implementation or proposal for this feature.
- Reviewed existing RFCs: RFC 0003 (gateway configuration) is the direct predecessor. Its TOML schema,
deny_unknown_fieldsvalidation, and Helm integration pattern are reused.
Checklist
- I've reviewed existing issues and the architecture docs
- This is a design proposal, not a "please build this" request
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with OpenShellRoot and DeclarativeSandboxConfig in crates/openshell-server/src/config_file.rs, then trace run_server() in crates/openshell-server/src/lib.rs, resume_persisted_sandboxes() in compute/mod.rs, and the CreateSandbox path in grpc/sandbox.rs. Review the Helm chart and docs/reference/gateway-config.mdx as well; done means startup reconciliation, validation, Helm rendering, and documentation cover the declared fields and required/optional failure behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- helm, rust
- Domain
- backend, infrastructure
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100