Problem Statement
There is no mechanism to place tamper-proof configuration files inside a sandbox at creation time without building a custom container image.
The filesystem policy's read_only enforcement via Landlock is production-ready, but file delivery into read-only paths requires either baking files into an image or manually configuring driver-level bind mounts. The --upload mechanism cannot help here — it runs after Landlock is enforced as the sandbox user, so it can only write to read_write paths.
This blocks use cases where operators need to inject configuration, persona files, policy overrides, or credential bundles that the agent must read but never modify — without maintaining per-configuration image builds.
Proposed Design
Add a files field to SandboxSpec (or SandboxTemplate) that accepts a list of (path, content, mode) tuples:
message SandboxFile {
string path = 1; // Absolute destination path inside sandbox
bytes content = 2; // File content
uint32 mode = 3; // Unix file mode (e.g., 0o444), optional default
}
message SandboxSpec {
// ... existing fields ...
repeated SandboxFile files = 11;
}
Supervisor-side behavior:
- During
prepare_filesystem() (runs as root, before Landlock), iterate over files entries
- Create parent directories, write file content, set ownership to root and mode to read-only
- Any paths covered by
files entries are implicitly added to the Landlock read_only ruleset (or validated against an explicit read_only policy entry)
- Landlock is applied — agent process cannot modify these files
CLI surface:
openshell sandbox create \
--file ./local/config.yaml:/etc/myapp/config.yaml \
--file ./local/SOUL.md:/sandbox/.agent/SOUL.md:0444 \
--policy ./policy.yaml \
-- python /app/main.py
Key design constraints:
- Files are written by the supervisor as root before privilege drop and Landlock enforcement — same trust model as the existing TLS CA file injection (
write_ca_files())
- Content travels through the existing gRPC channel (gateway → supervisor), not via host filesystem access — compatible with cloud/remote deployments unlike
--mount
- Size limits should be enforced (e.g., 1 MiB per file, 10 MiB total) to prevent abuse of the gRPC channel
- Paths must pass the same validation as
filesystem_policy entries (absolute, no .., max length)
Alternatives Considered
- Custom images: Works but creates an image-per-configuration maintenance burden. Not practical when configs change frequently or vary per sandbox instance.
--mount / bind mounts (#500): Declined by maintainers due to security concerns with bidirectional host access and incompatibility with cloud deployments. File injection through gRPC avoids both issues — content flows through the gateway, not the host filesystem.
--upload to read-only paths: Not possible. Upload runs as the sandbox user after Landlock enforcement. Cannot write to read-only directories.
- Environment variables for config: Unsuitable. The agent process can read and modify its own environment, env vars are visible in
/proc/self/environ, and they don't support structured config files or binary content.
Agent Investigation
Codebase paths examined:
crates/openshell-sandbox/src/policy.rs — FilesystemPolicy struct with read_only/read_write vecs. Enforcement infrastructure is complete.
crates/openshell-sandbox/src/sandbox/linux/landlock.rs — Landlock enforcement. prepare() opens PathFds as root; enforce() calls restrict_self() after privilege drop. File injection would slot between prepare and enforce.
crates/openshell-sandbox/src/lib.rs — prepare_filesystem() (line ~2016) creates/chowns read-write dirs. write_ca_files() writes TLS certs as root before Landlock. This is the existing pattern for pre-Landlock file injection.
crates/openshell-sandbox/src/ssh.rs — Upload via tar-over-SSH. Runs in a child that drops privileges and enforces Landlock before exec (pre-exec hook, line ~1135). Cannot write to read-only paths.
proto/openshell.proto — CreateSandboxRequest / SandboxSpec. No file content field exists. SandboxTemplate has environment and volume_claim_templates but no file injection.
crates/openshell-cli/src/run.rs — CLI sandbox creation. --upload parsed as LOCAL[:REMOTE], executes after sandbox reaches Ready state.
Related issues: #500 (host directory mounts — closed), #785 (volume mounts — open)
Problem Statement
There is no mechanism to place tamper-proof configuration files inside a sandbox at creation time without building a custom container image.
The filesystem policy's
read_onlyenforcement via Landlock is production-ready, but file delivery into read-only paths requires either baking files into an image or manually configuring driver-level bind mounts. The--uploadmechanism cannot help here — it runs after Landlock is enforced as the sandbox user, so it can only write toread_writepaths.This blocks use cases where operators need to inject configuration, persona files, policy overrides, or credential bundles that the agent must read but never modify — without maintaining per-configuration image builds.
Proposed Design
Add a
filesfield toSandboxSpec(orSandboxTemplate) that accepts a list of(path, content, mode)tuples:Supervisor-side behavior:
prepare_filesystem()(runs as root, before Landlock), iterate overfilesentriesfilesentries are implicitly added to the Landlockread_onlyruleset (or validated against an explicitread_onlypolicy entry)CLI surface:
openshell sandbox create \ --file ./local/config.yaml:/etc/myapp/config.yaml \ --file ./local/SOUL.md:/sandbox/.agent/SOUL.md:0444 \ --policy ./policy.yaml \ -- python /app/main.pyKey design constraints:
write_ca_files())--mountfilesystem_policyentries (absolute, no.., max length)Alternatives Considered
--mount/ bind mounts (#500): Declined by maintainers due to security concerns with bidirectional host access and incompatibility with cloud deployments. File injection through gRPC avoids both issues — content flows through the gateway, not the host filesystem.--uploadto read-only paths: Not possible. Upload runs as the sandbox user after Landlock enforcement. Cannot write to read-only directories./proc/self/environ, and they don't support structured config files or binary content.Agent Investigation
Codebase paths examined:
crates/openshell-sandbox/src/policy.rs—FilesystemPolicystruct withread_only/read_writevecs. Enforcement infrastructure is complete.crates/openshell-sandbox/src/sandbox/linux/landlock.rs— Landlock enforcement.prepare()opensPathFds as root;enforce()callsrestrict_self()after privilege drop. File injection would slot between prepare and enforce.crates/openshell-sandbox/src/lib.rs—prepare_filesystem()(line ~2016) creates/chowns read-write dirs.write_ca_files()writes TLS certs as root before Landlock. This is the existing pattern for pre-Landlock file injection.crates/openshell-sandbox/src/ssh.rs— Upload via tar-over-SSH. Runs in a child that drops privileges and enforces Landlock before exec (pre-exec hook, line ~1135). Cannot write to read-only paths.proto/openshell.proto—CreateSandboxRequest/SandboxSpec. No file content field exists.SandboxTemplatehasenvironmentandvolume_claim_templatesbut no file injection.crates/openshell-cli/src/run.rs— CLI sandbox creation.--uploadparsed asLOCAL[:REMOTE], executes after sandbox reaches Ready state.Related issues: #500 (host directory mounts — closed), #785 (volume mounts — open)