Skip to content

feat(sandbox): add the Local and Kubernetes backends and the cloud bindings - #401

Open
ItamarZand88 wants to merge 1 commit into
itamar/alien-75-sandbox-4-emittersfrom
itamar/alien-75-sandbox-5-backends
Open

feat(sandbox): add the Local and Kubernetes backends and the cloud bindings#401
ItamarZand88 wants to merge 1 commit into
itamar/alien-75-sandbox-4-emittersfrom
itamar/alien-75-sandbox-5-backends

Conversation

@ItamarZand88

Copy link
Copy Markdown
Contributor

Summary

Adds the code that actually runs a sandbox session: the Local and Kubernetes backends, and the bindings an application calls on AWS, Azure and GCP. Layer 4 rendered the infrastructure; this makes a session start, run a command, and stop.

When an application asks a Kubernetes sandbox to run something:

  1. It presents the ServiceAccount token Kubernetes already mounted in its pod to the session broker.
  2. The broker checks that token with a TokenReview, takes a warm pod from the pool, and labels it with the session.
  3. It mints a capability signed with a key the application never sees, derived from the sandbox's own id rather than named by the caller.
  4. The application drives the session over the agent port, presenting that capability on every call.

What I did

Three decisions are worth pointing at, each of which came out of a review finding rather than a plan:

  • The broker derives the signing key; it does not accept one. The claim request used to carry the secret name. A workload holding one sandbox's handle could name a sibling's secret and get a capability minted under that sibling's key. The name is deterministic, so the field is gone from the wire format.
  • Azure refuses what it cannot enforce. The Azure data plane takes no egress policy and no per-session ceiling, and the binding carries neither — so a declared deny was accepted and dropped, leaving a stack reading as restricted while the code ran with open egress. Azure now publishes false for both capabilities, which turns the declaration into a plan-time refusal. allow is still accepted: it asks for no restriction, so a backend that ignores it fails loudly on the first blocked connection rather than quietly under-protecting.
  • Terminate reports failure. Both the Local and Azure paths discarded the result of their delete and returned success. Terminate is the containment kill switch — reporting success for a daemon error or a throttle tells the caller untrusted code has stopped when it has not. Only an absent session is success now.

A fourth is smaller but had the same cause: the Azure get treated any error whose rendered text contained 404 as "session gone", and the data plane formats the whole response body into that text. A throttle, or a path containing 404, read as gone — which starts a second sandbox while the first keeps running. The status is carried structurally now.

Files touched

  • crates/alien-infra/src/sandbox/ — the Kubernetes controller, warm pool, session broker and its route.
  • crates/alien-local/ — the Docker-backed local manager and its loopback route.
  • crates/alien-bindings/src/providers/sandbox/ — the five backends behind one trait.
  • crates/alien-k8s-clients/, crates/alien-aws-clients/, crates/alien-azure-clients/ — the API surfaces those call.

How I tested

  • cargo test across the touched crates — the Kubernetes broker, warm pool, path confinement, capability minting and the five providers.
  • Mutation-tested every fix above by breaking it and watching a test fail: flipping Azure's egress capability back to true, removing the chart's ingress rule, and reverting the 404 classifier to substring matching. Each fix has a test that fails without it.
  • Live suites (#[ignore]d, run by hand): a real GKE Autopilot cluster for the Kubernetes backend, and Docker for the Local one.

Security review of this diff, since it mints the credential that reaches inside a session:

  • A workload minting a capability under another sandbox's key — the broker derives the key name from the sandbox id, and a request naming one no longer deserializes (kubernetes_route.rs).
  • A capability replayed against another session — the AWS token is scoped to its MicroVM by the platform, and the Kubernetes capability is bound to the claimed session and checked at the agent.
  • A declared egress restriction silently dropped — refused at plan time on any backend that cannot enforce it, with the capability table as the single source of that answer.
  • Untrusted code left running after terminate — every delete failure now propagates except an absent session.
  • A rejected ServiceAccount token accepted — the apiserver answers 200 with authenticated: false, so the verdict is read rather than the status code, and namespace matching is a full-prefix check.

One thing this diff does not close, stated plainly rather than left to a reader: the broker authenticates the caller but does not check which sandbox it may claim from. claim verifies a ServiceAccount in the deployment's namespace and then takes the sandboxId from the request. Binding an authenticated identity to an entitled set of sandboxes needs state the broker does not have yet.

@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds the runtime layer for sandbox sessions across Local, Kubernetes, AWS, Azure, and GCP.

  • Adds Local and Kubernetes session execution backends, routing, warm-pool management, and capability authentication.
  • Adds cloud sandbox bindings and direct API clients for session lifecycle, commands, files, and previews.
  • Propagates sandbox configuration, capabilities, imports, and provider registration through the infrastructure and bindings layers.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains.

No blocking failure remains.

Important Files Changed

Filename Overview
crates/alien-bindings/src/providers/sandbox/aws.rs Implements Lambda MicroVM session lifecycle and agent access, with session ownership checked against the bound image ARN before privileged operations.
crates/alien-bindings/src/providers/sandbox/azure.rs Implements Azure sandbox translation, client-side command deadlines, and containment-confirming asynchronous termination.
crates/alien-bindings/src/providers/sandbox/local.rs Adds the Local sandbox binding and enforces command deadlines by terminating an overrunning session.
crates/alien-infra/src/sandbox/kubernetes_broker.rs Adds the Kubernetes session broker responsible for authenticated claims and scoped capability issuance.
crates/alien-infra/src/sandbox/kubernetes_warm_pool.rs Adds Kubernetes warm-pod pool reconciliation and session allocation behavior.
crates/alien-azure-clients/src/azure/sandbox_data_plane.rs Adds the direct Azure sandbox data-plane client with structured HTTP errors and explicit asynchronous-delete semantics.
crates/alien-aws-clients/src/aws/lambda_microvms.rs Adds the signed Lambda MicroVM API client, including lifecycle operations and per-session endpoint-token minting.

Sequence Diagram

sequenceDiagram
  participant App
  participant Binding as Sandbox binding
  participant Backend as Local/Cloud backend
  participant Session as Sandbox session
  App->>Binding: create or get session
  Binding->>Backend: provider-specific lifecycle request
  Backend-->>Binding: scoped session identity
  App->>Binding: run command with deadline
  Binding->>Session: authenticated execution request
  alt command completes
    Session-->>Binding: output and exit status
    Binding-->>App: command output stream
  else deadline exceeded
    Binding->>Backend: terminate session
    Backend-->>Binding: containment confirmed
    Binding-->>App: deadlineExceeded
  end
Loading

Reviews (24): Last reviewed commit: "feat(sandbox): add the Local and Kuberne..." | Re-trigger Greptile

Comment thread crates/alien-bindings/src/providers/sandbox/azure.rs Outdated
Comment thread crates/alien-bindings/src/providers/sandbox/aws.rs
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from 231ec0c to a212c6a Compare August 11, 2026 18:40
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from a212c6a to c3c2316 Compare August 11, 2026 18:49
Comment thread crates/alien-bindings/src/providers/sandbox/aws.rs
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from c3c2316 to 0cea165 Compare August 11, 2026 19:16
Comment thread crates/alien-bindings/src/providers/sandbox/azure.rs
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from 0cea165 to f5a829c Compare August 11, 2026 19:24
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from f5a829c to 29e5d1f Compare August 11, 2026 19:49
Comment thread crates/alien-bindings/src/providers/sandbox/azure.rs
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from 29e5d1f to f81e5a8 Compare August 11, 2026 20:08
Comment thread crates/alien-bindings/src/providers/sandbox/aws.rs Outdated
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from f81e5a8 to 64d4aeb Compare August 11, 2026 21:22
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from 64d4aeb to 11c5234 Compare August 11, 2026 21:26
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch 2 times, most recently from 469d8cf to a7c48f5 Compare August 11, 2026 22:24
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from a7c48f5 to 3a41136 Compare August 12, 2026 06:32
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from 3a41136 to 962f567 Compare August 12, 2026 06:55
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from 962f567 to c09d4f5 Compare August 12, 2026 07:05
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from c09d4f5 to cd59cbf Compare August 12, 2026 07:22
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from cd59cbf to c9f399b Compare August 12, 2026 07:49
Comment thread crates/alien-bindings/src/providers/sandbox/azure.rs
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch 2 times, most recently from e0c9b87 to c48ff7e Compare August 12, 2026 08:15
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from c48ff7e to fd792bb Compare August 12, 2026 09:15
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from fd792bb to bc948c5 Compare August 12, 2026 11:01
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch 2 times, most recently from 30f1c5e to 668ad36 Compare August 12, 2026 12:14
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from 668ad36 to f34c5ec Compare August 12, 2026 13:25
@ItamarZand88

Copy link
Copy Markdown
Contributor Author

Both remaining findings are correct-as-designed rather than defects — here's the evidence for each.

AWS session ownership at image scope. The image is one per declared sandbox, not a shared namespace: the emitter binds imageArn to the ARN AWS assigns to the single image resource it emits for that one sandbox (crates/alien-terraform/src/emitters/aws/sandbox.rs), and the binding reads that ARN back off the resource. Two bindings that resolve to one image are two bindings on one declared sandbox — the sharing the declaration asked for. Ownership is now proven per-session by an ARN-scoped GetMicrovm comparing the session's own imageArn against the bound image (owned_microvm, crates/alien-bindings/src/providers/sandbox/aws.rs), so a sibling sandbox's session id is refused before any token is minted.

Azure command returns after its deadline. This is forced by the API, not a choice:

  • The ADC data-plane executeShellCommand has no server-side timeout — the request body is command plus an optional workingDirectory and nothing else (read out of the preview SDK). The timeoutInSeconds that looks relevant belongs to sessionPools (dynamicsessions.io), a different product; Sandboxes are azuredevcompute.io/SandboxGroups.
  • delete is accept-then-poll async — "returns before it is gone; confirm by polling get_sandbox to 404" (crates/alien-azure-clients/src/azure/sandbox_data_plane.rs:66).

So at the instant the deadline fires, "the command is contained" is not yet a knowable fact. Returning deadlineExceeded then would assert containment the code never checked. The deadline bounds the command; the call returns once containment is confirmed (bounded at deadline + terminate-confirm window), and it reports SandboxUnreachable / "may still be running" rather than a false deadlineExceeded when it cannot confirm. A punctual return and a guaranteed-containment claim can't both hold on an accept-then-poll delete API — the design picks the honest guarantee.

@greptile-apps please re-review.

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.

1 participant