Skip to content

fix(sandbox): make a remote-bound sandbox reachable, healthy and diagnosable - #509

Open
ItamarZand88 wants to merge 6 commits into
mainfrom
itamar/alien-561-byoc-sandbox-fixes
Open

fix(sandbox): make a remote-bound sandbox reachable, healthy and diagnosable#509
ItamarZand88 wants to merge 6 commits into
mainfrom
itamar/alien-561-byoc-sandbox-fixes

Conversation

@ItamarZand88

Copy link
Copy Markdown
Contributor

Summary

A sandbox running in a customer's own cloud account could never be found, could never
start, and said nothing useful when it failed. This fixes all three, plus a management
filter that was stripping the heartbeat grant from every remote-bound resource type.

Step-by-step flow when an app calls bindings.sandbox() against a customer-owned deployment:

  1. Remote Bindings asks the platform which deployment serves this customer's sandbox
    capability, rather than resolving every handle against storage.
  2. The session starts in the customer's account with lambda:RunMicrovm and
    lambda:PassNetworkConnector for the AWS-managed connectors an open-egress session
    attaches. ← the fix lives here
  3. If the cloud refuses the call, the service message it returned comes back attached to
    the error instead of a generic failure.

This PR changes a remote sandbox that could not be located or started into one that starts,
reports healthy, and says why when it doesn't.

What was broken

Remote Bindings resolved every handle against the storage capability, so a customer running
only a sandbox was never found. Even once found, the remote grant deliberately withheld
lambda:PassNetworkConnector on the belief that AWS authorizes it against no resource — it
authorizes it against the connector ARN, and attaches its own connectors to an open-egress
session, so the one sandbox shape remote access permits could not start at all. Separately,
the setup emitter dropped every <type>/* management set whenever a resource had a remote
binding, and <type>/heartbeat went with it.

What I did

  • Resolve a remote binding per capability. Storage, keys and AI still resolve through
    storage; sandbox() resolves the sandbox binding.
  • Grant lambda:PassNetworkConnector, scoped to the AWS-managed connector ARN segment. A
    customer-declared connector can never match that scope, so the grant cannot be used to
    attach a connector the customer defined.
  • Narrow the management filter to exclude only the binding's own permission set — and, for a
    sandbox specifically, whatever reaches a live session. <type>/heartbeat survives, which
    is what the management identity reports with. The blast radius here was wider than
    sandboxes: storage and ai are remote-binding types too.
  • Give the CloudFormation and Terraform setup emitters one shared predicate for that filter,
    so the two cannot drift again, with a generator test pinning each side.
  • Carry the cloud's own refusal text out of a failed sandbox call, bounded in length and only
    when the whole error chain is internal — a public wrapper never quotes an internal layer.
  • Teach the CLI to notice a tracked deployment the platform has already deleted, and add
    deployments untrack for the case where it hasn't.

Files touched

  • crates/alien-bindings/src/remote.rs, remote/access.rs — per-capability resolution; the
    sandbox capability now maps to its wire variant instead of erroring.
  • crates/alien-bindings/src/providers/sandbox/refusal.rs (new) — lifts the service message
    out of a captured refusal body.
  • crates/alien-permissions/permission-sets/sandbox/remote-execute.jsonc — the
    PassNetworkConnector grant, scoped to AWS-managed connectors.
  • crates/alien-core/src/remote_bindings.rs — the shared management-filter predicate.
  • crates/alien-cloudformation/…/remote_stack_management.rs,
    crates/alien-terraform/…/remote_stack_management.rs — both call it.
  • crates/alien-cli/src/deployment_tracking.rs, commands/deployments.rs,
    commands/deploy.rs, commands/onboard.rs — stale-entry detection, untrack,
    --setup-item.
  • client-sdks/platform/*.json — regenerated; the CLI could not deserialize a sandbox
    deployment against the committed spec.

How I tested

  • Manually: deployed a customer-owned sandbox into a test AWS account from the emitted
    CloudFormation template, with no hand-added IAM. Started a MicroVM through
    bindings.sandbox() and ran a 14-check suite over the sandbox surface — exec with exit
    codes and stderr, file read/write, working directory, environment, port exposure, session
    timeout, teardown. 14/14 passed on the template's grants alone. Before the
    PassNetworkConnector grant the same run failed at session start; before the resolution
    fix it never got as far as a deployment.
  • Unit tests: aws_remote_sandbox_management_role_heartbeats_without_reaching_a_session
    (CloudFormation and Terraform), aws_remote_storage_management_role_keeps_its_heartbeat
    (fails against the old prefix filter — this is the regression proof for the storage/AI
    blast radius), frozen_remote_sandbox_heartbeat_permission_is_added_to_management,
    a_refused_create_reports_what_aws_refused_it_with plus four refusal-formatting cases,
    and nine deployment-tracking tests against a fake platform.
  • Anything I couldn't test: the storage/AI heartbeat regression is proven by a test, not
    observed on a live bring-your-own-bucket deployment. Azure and GCP sandbox paths are
    untouched and unexercised here. An already-installed deployment picks up the new grant only
    when its setup package is regenerated.

I also ran a security review on the diff. What it checked:

  • Can the new PassNetworkConnector grant attach a connector the customer declared? No — the
    ARN scope pins the account segment to the literal aws, and a customer connector's ARN
    carries the customer account id (sandbox/remote-execute.jsonc).
  • Does the widened heartbeat survival drag session execution onto the management identity?
    No — asserted directly in
    frozen_remote_sandbox_heartbeat_permission_is_added_to_management.
  • Can the refusal text leak an internal detail to an external caller? No — a layer is quotable
    only when the wrapper carrying it is itself internal, and the text is length-bounded
    (refusal.rs, with an_internal_layer_is_not_quoted_into_a_public_wrapper covering it).
  • Does per-capability resolution let one customer's token reach another's deployment? No —
    the external id and token are unchanged per lookup; only which deployment answers changes.

Nothing turned up.

`sandbox` became a deployment capability on the API side; the committed spec was
never regenerated, so the CLI failed to deserialize any sandbox deployment. Also
drops the phantom logs stackSettings field that #428 reintroduced after #474
removed it, which nothing on the API side generates.
Remote Bindings resolved every handle against the storage capability, so a
customer running only a sandbox was never found. Resolve per binding instead.

The remote grant withheld lambda:PassNetworkConnector on the premise that AWS
authorizes it against no resource. AWS authorizes it against the connector ARN
and attaches its own connectors to an open-egress session, so the only sandbox
shape remote access permits could not start. Grant it, scoped to AWS-managed
connectors, which a customer-declared one can never match.

The CloudFormation emitter dropped every <type>/* management set when a resource
had a remote binding, taking <type>/heartbeat with it. That reaches further than
sandboxes: storage and ai are remote-binding types too, so every
bring-your-own-bucket and BYO-AI deployment lost the grant its management
identity reports with. Exclude only the binding's own set, and for a sandbox
also whatever reaches a session.

Sandbox errors discarded the cloud's refusal, and the CLI trusted tracked
deployments the platform had already deleted.
The management filter dropped session-reaching sets whenever the stack held any
remote binding, so a stack pairing remote-bound storage with an ordinary frozen
sandbox lost that sandbox's session cleanup. Gate on the binding's own kind.

Also corrects the recorded reason the two wildcard connector grants stay
unbounded: a connector's ARN ends in an AWS-assigned id rather than the name the
template sets, so a prefix scope would deny a real one.
Terraform's ManagementRole emitter applied no remote-binding filter, so for a
stack CloudFormation now filters it still compiled sandbox/management into the
cross-account role. Both call remote_binding_claims_management_set, so the two
cannot drift again, and each generator test pins its own side.

The Terraform twin of the remote-grant test still asserted PassNetworkConnector
stays out; it carried the same disproved premise the CloudFormation one did.
Storage and AI are remote-binding types too, so the prefix match stripped
`<type>/heartbeat` from every bring-your-own-bucket and BYO-AI deployment's
management identity, not only from sandboxes. Fails against the prefix filter.
@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown

Greptile Summary

The PR makes customer-owned sandbox bindings resolve by capability, restores required heartbeat and AWS connector permissions, preserves bounded internal cloud diagnostics, and improves stale deployment tracking.

  • Resolves sandbox and storage-family remote bindings through their corresponding customer deployments.
  • Aligns CloudFormation and Terraform management-permission filtering around a shared predicate.
  • Adds scoped AWS-managed network-connector access for remote sandbox sessions.
  • Adds stale-entry detection, explicit untracking, and setup-item selection to deployment workflows.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
crates/alien-bindings/src/remote.rs Introduces lazy per-capability environment discovery while retaining a single provider for deployment-scoped handles.
crates/alien-bindings/src/providers/sandbox/refusal.rs Extracts bounded provider refusal details while preserving internal-error visibility and external sanitization.
crates/alien-core/src/remote_bindings.rs Centralizes the predicate used to exclude remote-owned and session-reaching permission sets from management identities.
crates/alien-permissions/permission-sets/sandbox/remote-execute.jsonc Grants remote sandbox execution access to AWS-managed network connectors through an account-pinned ARN scope.
crates/alien-cli/src/deployment_tracking.rs Validates tracked deployment credentials, removes stale records, and supports explicit untracking through a testable storage abstraction.
crates/alien-cloudformation/src/emitters/aws/remote_stack_management.rs Applies the shared remote-binding permission filter while retaining heartbeat access.
crates/alien-terraform/src/emitters/aws/remote_stack_management.rs Mirrors CloudFormation management filtering through the same shared predicate.

Sequence Diagram

sequenceDiagram
    participant App
    participant Bindings as Remote Bindings
    participant Platform
    participant Manager
    participant AWS
    App->>Bindings: sandbox(name)
    Bindings->>Platform: Resolve customer + sandbox capability
    Platform-->>Bindings: Deployment-scoped manager access
    Bindings->>Manager: Resolve sandbox binding
    Manager-->>Bindings: Sandbox configuration and credentials
    Bindings->>AWS: RunMicrovm with managed connector
    alt Session starts
        AWS-->>Bindings: MicroVM endpoint
        Bindings-->>App: Healthy sandbox session
    else Cloud refuses request
        AWS-->>Bindings: Bounded refusal detail
        Bindings-->>App: Sanitized external error
    end
Loading

Reviews (2): Last reviewed commit: "test(sandbox): assert refusal shape with..." | Re-trigger Greptile

Comment thread crates/alien-bindings/src/providers/sandbox/refusal.rs Fixed
Comment thread crates/alien-bindings/src/providers/sandbox/refusal.rs Fixed
Comment thread crates/alien-bindings/src/providers/sandbox/refusal.rs Fixed
Comment thread crates/alien-bindings/src/providers/sandbox/refusal.rs Fixed
A reason carries a cloud's own sentence, role ARNs included, and this
repository's CI logs are public. Name the expectation in the assertion
message and leave the value to the condition.
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.

2 participants