Skip to content

Opt-in policy to preserve files in the mutable directory across runs; prepare_for_run always clears it #227

Description

@antsok

WasmSandbox::run_impl calls prepare_for_run before entering the guest, and in 0.7.0 that is clear_output_files:

pub fn prepare_for_run(&mut self) -> Result<()> {
    self.clear_output_files()
}

So the only directory a guest may write to, output_dir mounted at /output with DirPerms::MUTATE, is emptied on the way into every run. A file the host places there before the first run is deleted before guest code sees it, and a file the guest creates is deleted by the next run. input_dir is read-only, so it cannot serve a workload that reads, edits and deletes its inputs.

Reproduced with the published Python SDK, hyperlight-sandbox 0.7.0 with backend-wasm 0.7.0 and python-guest 0.7.0, on Windows 11 x86-64 with WHP and CPython 3.13:

import os, pathlib, tempfile
from hyperlight_sandbox import Sandbox

d = tempfile.mkdtemp()
pathlib.Path(d, "staged.txt").write_text("hello from the host")
sb = Sandbox(output_dir=d)
r = sb.run("print(open('/output/staged.txt').read())")
print(r.stderr.strip().splitlines()[-1], os.listdir(d))
sb.run("open('/output/made-by-guest.txt', 'w').write('x')")
print(os.listdir(d))
r = sb.run("import os; print(os.path.exists('/output/made-by-guest.txt'))")
print(r.stdout.strip(), os.listdir(d))
FileNotFoundError: [Errno 44] No such file or directory: '/output/staged.txt' []
['made-by-guest.txt']
False []

I understand the clear is deliberate: it is where the cached quota accounting is reset, and a run should not inherit a previous run's leftovers by default. I am not asking for the default to change.

What I am asking for is an opt-in policy, selected at build time and off by default, under which prepare_for_run leaves the mutable directory alone. Two properties matter more than the spelling:

  • Quotas are reconciled from disk, not from the cache. With preservation on, the file count and byte totals must be recomputed from the real directory before guest entry, so a host-staged file counts against the limits and an over-quota directory fails the run before the guest starts rather than after it has partly written.
  • Reset stays explicit. Snapshot restore or a dedicated reset call is where the directory is cleared under this policy, so an integrator can still return a sandbox to a known state on purpose. Preservation changes what happens on entry, not what restore means.

A SandboxBuilder option such as .preserve_output_files(true), exposed on the Python Sandbox constructor beside output_dir, would be enough. If you would rather express it as a file-lifetime enum, that also works for us.

Alternatives I considered and would rather not ship: copying inputs in through a hidden guest prelude, which invents a second file lifecycle the runtime does not validate; staging to the read-only input_dir, which cannot be edited or deleted by the guest; and a privately patched native wheel, which leaves the published dependency set uninstallable. Happy to contribute the change if the shape is agreed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions