Table of Contents

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:

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

  • sandboxClasses and sandboxPolicies are immutable administrator-published revisions. sandboxConfigurations is 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 -c at the Kubernetes boundary. Executor requests bind the sandbox and operation identities and expire after 30 seconds. Their bundle_digest is recomputed per request using the canonical fllm-enforcement-v1 algorithm because expires_at is part of the digest. Executor request/result use contract 1.1 with enforcement.process.inner_filter (groups + exceptions) instead of flat seccomp_profile; see the coordination repo sandbox-policy-domains-v1.md. The stored policy digest identifies immutable policy revision content; it is not copied into an executor request.
  • pods/exec into 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_ID to their immutable session identifier and set automountServiceAccountToken: false. Non-sandbox managed endpoints retain their existing workload-identity and service-account behavior.
  • The old direct managedEndpointDeployments/{name}/execute-command action 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 on Platform/sandboxes/execute-command.
  • Sandboxes is a flat ResourceLimitQuotaType alongside Agents; 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 matching nvidia.com/gpu requests and limits; zero emits no GPU resource. /workspace and /tmp are explicit writable mounts. temporary_bytes sizes /tmp as tmpfs (emptyDir.medium=Memory), which is a hard cap. workspace_bytes is the guest write cap for /workspace: ephemeral and shared-ssd modes mount a FUSE quota layer so writes fail with ENOSPC at 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 with SYS_ADMIN. The user and files containers stay unprivileged. emptyDir.sizeLimit and container ephemeral-storage limits 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 /workspace is 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 /workspace volume 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 files only. 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 remain filesystem.upload_bytes, filesystem.download_bytes, and max_file_bytes.
  • Wire format: streamed multipart upload and octet-stream download. File bytes must not travel as exec stdout/stdin or as MCP content_base64 except for the existing tiny inline download cap.