Skip to content

feat(sandbox): arrays and structs across the sandbox boundary - #7

Merged
Immanuel-Fowler merged 2 commits into
mainfrom
feat/sandbox-arrays-structs
Sep 21, 2026
Merged

Immanuel-Fowler merged 2 commits into
mainfrom
feat/sandbox-arrays-structs

Conversation

@Immanuel-Fowler

Copy link
Copy Markdown
Owner

What

Arrays and structs cross the sandbox boundary, under the rules the C ABI adopted in 0.14.0:

  • int[], float[], bool[] cross as a pointer and a count into guest memory.
  • A struct whose fields are numbers, booleans, or such structs crosses as its #[repr(C)] bytes, written field by field at offset_of! offsets with padding zero, so neither side reads padding or depends on the other's endianness.
  • What the host writes for a call, the host frees after it; what the guest returns, the host copies and frees.
  • The guest allocator is 8-byte aligned. Handler state may be of these types, and a handler may return it.

orchestrate check rejects a handler type that cannot cross, by name, as the compiler's own error. It used to surface from the guest crate's cargo build.

Also fixed on the way

  • Every string argument leaked guest memory. The host allocated it in the guest and never freed it. Under an 8mb cap, 20,000 one-kilobyte calls failed four times, each failure resetting the guest's state. A regression test sends those calls.
  • A handler could not return its own array or string state on any boundary: return seen moved the value out of the actor, the child, or the guest and rustc refused (E0382 in-process, E0507 in the guest). A returned state binding is now a copy. Snapshots for the secret child and the sandbox guest change by one .clone() each.

What still does not cross

An array of strings or structs, or a struct holding one, crosses the in-process, secret, and landline boundaries but not the sandbox. LIMITATIONS-notes.md (new, at the root) carries the per-boundary table and everything else found on this branch, for the paper's limitations section.

Tests

  • runtime_sandbox_serverlet_carries_arrays_and_structs: arrays and nested structs both ways, array state, a string beside an array in one call, an empty array.
  • runtime_sandbox_frees_what_the_host_writes: 20,000 string and array calls under an 8mb cap.
  • runtime_serverlet_handler_returns_its_state: in-process, secret, and sandboxed serverlets returning array and string state.
  • tests/error_cases/sandbox_unsupported_type.orch: the check-time rejection.
  • Snapshots regenerated and reviewed: sandbox_guest (codec and helpers), secret_serverlet (one .clone()).

Changelog entries are under [Unreleased]. No version bump.

🤖 Generated with Claude Code

Immanuel-Fowler and others added 2 commits September 20, 2026 22:54
The sandboxed serverlet carried int, float, bool and string; the other
three boundaries carried arrays and structs too, which undercut the
claim that the boundary is chosen per module with the call site
unchanged. It now carries int[], float[], bool[], and structs whose
fields are numbers, booleans or such structs: the set the C ABI carries
since 0.14.0, under the same rules. An array is a pointer and a count
into guest memory; a struct is its repr(C) bytes, written field by
field at offset_of! offsets with padding zero, so neither side reads
padding or depends on the other's endianness. What the host writes for
a call it frees after the call; what the guest returns the host copies
and frees. The guest allocator is 8-byte aligned so numbers can be read
in place.

Three things the work found:

The host never freed the strings it wrote into the guest. Under an 8mb
cap, 20,000 one-kilobyte calls failed four times, each failure resetting
the guest's state. Every allocation the host makes for a call is now
released after it, and a test sends those 20,000 calls.

A handler could not return its own array or string state on any
boundary: `return seen` moved the value out of the actor, the child or
the guest, and rustc refused in generated code. A returned state
binding is now a copy; for a number that is what happened before. The
secret child and sandbox guest snapshots change by one .clone() each.

A sandbox handler type that cannot cross was reported by the guest
crate's cargo build, after codegen, so `orchestrate check` accepted a
program the build rejected. It is the typechecker's error now, and for
a serverlet in an imported module, whose body the typechecker does not
walk, codegen reports it before Cargo runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The reference, README, roadmap and design doc say what a sandboxed
serverlet carries and what it does not. LIMITATIONS-notes.md is new at
the root: it collects every gap found while preparing the paper,
fixed or not, starting with the per-boundary table of which values
cross where, which is the honest form of the unchanged-call-site claim.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Immanuel-Fowler
Immanuel-Fowler merged commit f35b886 into main Sep 21, 2026
1 of 2 checks passed
@Immanuel-Fowler
Immanuel-Fowler deleted the feat/sandbox-arrays-structs branch September 21, 2026 15:02
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