Secure Sandbox Contract v1
Operator note: for instance setup (platform agents and external MCP), see Sandbox Deployment. To bring a v1-only cluster forward (sidecars, quota image, current guest executor), see Upgrade a v1-only sandbox environment. To inspect persistent-workspace Azure Files share contents in the Azure Portal when storage account keys are disabled, see View sandbox persistent-workspace files in Azure Portal. Portal Browse requires Storage File Data Privileged Reader (not only SMB Share Reader).
The cross-repository secure-sandbox contract is maintained in the coordination repository:
- Repository:
foundationallm/foundationallm-aks-pods-sandbox - Branch:
zst-security-sandbox-v2 - Contract:
docs/contracts/secure-sandbox-contract-v1.md - Source inventory:
docs/implementation/step-1-source-inventory.md
FoundationaLLM implementation for this contract is developed on
zst-security-sandbox-v2-core.
FoundationaLLM ownership
Management API and FoundationaLLM.Platform/sandboxes own the public resource,
authorization, and lifecycle contract. Context API, Cosmos-backed code-session
records, AKS endpoint deployment services, content protection, State API, quota,
events, and telemetry are internal implementation details behind that contract.
The MCP adapter is not a second control plane. Merged
PR #4
is the required baseline: the adapter uses Entra authentication and
X-USER-IDENTITY forwarding to call Management API; execute and delete are
conversation-addressed Platform actions; and the adapter has no Kubernetes
credentials, Kubernetes client, pod lifecycle ownership, pod-level RBAC, sandbox
configuration authority, or local authoritative database. FoundationaLLM's
Context API and AKS backend remain the only Kubernetes implementation seam.
Change rule
FoundationaLLM models, validators, clients, APIs, and tests must remain compatible
with secure-sandbox contract major version 1. A breaking wire-format,
authorization, lifecycle, ownership, or enforcement change requires coordinated
review and a new major contract version in both repositories.
Step 5 command-boundary decisions
sandboxClassesandsandboxPoliciesare immutable administrator-published revisions.sandboxConfigurationsis an administrator-controlled published combination protected by a rotating ETag; each update points to exact immutable class and policy revisions.- Sandbox Configuration create, update, and deletion and class/policy deletion share one renewable storage-backed distributed lease. A referenced exact class or policy revision returns HTTP 409 on deletion; configuration deletion remains allowed.
- An agent stores an administrator-selected Sandbox Configuration ID, while an external MCP harness supplies an ID authorized to its managed identity. Sandbox create accepts no class, policy, image, runtime, capacity, network, or enforcement property. Platform authorizes configuration read, resolves exact referenced revisions, then persists a detached enforcement snapshot with the Context sandbox record. Class, policy, digest, secret references, and node pool on that snapshot are frozen at create; new class or policy revisions affect new sandboxes only. Idle timeout and maximum lifetime are live on the Sandbox Configuration. An in-place edit applies to running sandboxes after resource-provider caches refresh. Values copied onto the snapshot are a fallback when the live configuration cannot be read.
- AKS pod exec is only a transport for
/usr/local/bin/fllm-sandbox-exec --request-base64 <base64-json>. The platform never sends a model command to/bin/sh -cat the Kubernetes boundary. Executor requests bind the sandbox and operation identities and expire after 30 seconds. Theirbundle_digestis recomputed per request using the canonicalfllm-enforcement-v1algorithm becauseexpires_atis part of the digest. Executor request/result use contract 1.1 withenforcement.process.inner_filter(groups + exceptions) instead of flatseccomp_profile; see the coordination reposandbox-policy-domains-v1.md. The stored policy digest identifies immutable policy revision content; it is not copied into an executor request. pods/execinto sandbox pods is restricted by Kubernetes RBAC to ContextAPI's managed identity. Ed25519 bundle signing remains deferred residual risk.- Executor output must be the structured v1 result and must echo the request identity and bundle evidence. Malformed, mismatched, expired, or non-zero executor transport results fail closed.
- Sandbox pods bind
FLLM_SANDBOX_IDto their immutable session identifier and setautomountServiceAccountToken: false. Non-sandbox managed endpoints retain their existing workload-identity and service-account behavior. - The old direct
managedEndpointDeployments/{name}/execute-commandaction is intentionally not compatible: it is no longer advertised and rejects calls because that path has no Sandbox Configuration snapshot. Public command compatibility remains only onPlatform/sandboxes/execute-command. Sandboxesis a flatResourceLimitQuotaTypealongsideAgents; it reuses the existing quota principal, partition, event, and resource-count evaluation machinery. Evaluation and authoritative Context lifecycle-record creation execute under a renewable, per-owner storage lease, so concurrent Platform instances do not overlap their check/create windows. No reservation resource or second quota model exists. The remaining hard transaction boundary is the Context service/Cosmos DB call: the Platform lease cannot force a subsequent request routed to another Context instance to observe the preceding lifecycle write under account-level session or eventual consistency. A strict quota guarantee therefore requires Context to perform conditional count-and-create in the lifecycle record's authoritative Cosmos partition (or expose an equivalent atomic create API); a process-local Platform lock would not close that boundary.- Sandbox class image digest, CPU, and memory are copied into the persisted
managed-deployment snapshot and generated pod container specification.
Runtime class publication is restricted to
kata-vm-isolation, and sandbox pod generation sets that runtime class. Positive GPU capacity is emitted as matchingnvidia.com/gpurequests and limits; zero emits no GPU resource./workspaceand/tmpare explicit writable mounts.temporary_bytessizes/tmpas tmpfs (emptyDir.medium=Memory), which is a hard cap.workspace_bytesis the guest write cap for/workspace: ephemeral and shared-ssd modes mount a FUSE quota layer so writes fail withENOSPCat the class limit. Ephemeral backing is a local emptyDir; shared-ssd backing is the conversation folder on the shared Premium share. The FUSE sidecar (fllm-workspace-fuse) uses Bidirectional mount propagation, which Kubernetes allows only on privileged containers, so that sidecar runs privileged withSYS_ADMIN. The user and files containers stay unprivileged.emptyDir.sizeLimitand containerephemeral-storagelimits remain host backstops and are not the guest enforcement mechanism. Other persistent SKUs use the Azure Files share quota or dedicated volume size (which may be larger than the class value). Ephemeral/workspaceis not durable across pod replacement.
Contract 1.2 — platform files sidecar
Conversation file transfer into and out of /workspace is a platform HTTP
files API, not an executor/workspace-io concern. Exec remains the only
path for shell and python.
- Guest process:
network.inbound = deny,direct_interface = none(bwrap --unshare-net). The user command has no network and must not listen. - Files sidecar: a second container in the same sandbox pod mounts the same
/workspacevolume and listens on cluster-internal TCP 8081 (files). This is a platform transfer agent, not a guest inbound listener. - Callers: Context API only. Cluster NetworkPolicy allows Context API pods to the sidecar port and denies every other source.
- Exposure: ClusterIP Service port
filesonly. No Gateway HTTPRoute, no public DNS, and no Core API route to the sidecar. - Auth: a per-sandbox bearer token is minted at create time, stored on the Context session record, and injected only into the sidecar. The user container never receives the token.
- Path jail:
/workspace/...only; reject traversal, NUL, trailing slash, symlink escape, and hardlink escape. Size caps remainfilesystem.upload_bytes,filesystem.download_bytes, andmax_file_bytes. - Wire format: streamed multipart upload and octet-stream download. File
bytes must not travel as exec stdout/stdin or as MCP
content_base64except for the existing tiny inline download cap.