Guest modifications to a path previously written via host vm.writeFile are silently discarded when the guest process exits. The guest sees its own edit (reads it back fine, exits 0), but afterwards both the host filesystem API and a later in-VM process see the original host-written bytes. Writes to paths the host never touched persist correctly.
This bites hard when the embedding host seeds a workspace with vm.writeFile before running guest code: every guest edit of a seeded file is lost with no error anywhere, while newly created files survive — which makes the data loss easy to miss in testing.
Repro (embedded @rivet-dev/agentos-core, 0.2.15 and 0.2.19)
const { AgentOs } = require("@rivet-dev/agentos-core");
const vm = await AgentOs.create({
permissions: { fs: "allow", network: "deny", childProcess: "allow", process: "allow", env: "deny", binding: "deny" },
});
await vm.mkdir("/home/agentos", { recursive: true });
await vm.writeFile("/home/agentos/seeded.txt", Buffer.from("original\n")); // host write
await vm.execArgv("node", ["-e",
'require("fs").writeFileSync("/home/agentos/seeded.txt","EDITED\\n")'
], { timeout: 20000, captureStdio: true }); // guest edit, exits 0
console.log(Buffer.from(await vm.readFile("/home/agentos/seeded.txt")).toString());
// actual: "original\n" expected: "EDITED\n"
const cat = await vm.execArgv("cat", ["/home/agentos/seeded.txt"], { timeout: 20000, captureStdio: true });
console.log(cat.stdout);
// actual: "original\n" — a LATER guest process also sees the stale content
Observations
- Overwrite, append, and truncate by the guest are all lost;
readdirRecursive reflects the host-written size.
- Guest writes to paths never touched by the host persist and are visible to
readFile/readdirRecursive — including across guest processes (A writes, B edits, host sees B's content).
- Guest
unlink of a guest-written file is visible to the host; behavior is specific to host-written content.
- Host
mkdir is unaffected: guests can create/edit files inside host-made directories normally.
- Reproduced on darwin/arm64 and linux/arm64 (
node:24-bookworm-slim), core 0.2.15 and 0.2.19, with and without software/defaultSoftware.
Looks like host writes land in a base/admin layer while guest copy-on-write over those paths lives in an overlay that is dropped at process exit instead of being merged.
Our workaround: host-write only a manifest file, then have an in-VM bootstrap process write the actual seeded files, so all guest-editable content is guest-authored.
Guest modifications to a path previously written via host
vm.writeFileare silently discarded when the guest process exits. The guest sees its own edit (reads it back fine, exits 0), but afterwards both the host filesystem API and a later in-VM process see the original host-written bytes. Writes to paths the host never touched persist correctly.This bites hard when the embedding host seeds a workspace with
vm.writeFilebefore running guest code: every guest edit of a seeded file is lost with no error anywhere, while newly created files survive — which makes the data loss easy to miss in testing.Repro (embedded
@rivet-dev/agentos-core, 0.2.15 and 0.2.19)Observations
readdirRecursivereflects the host-written size.readFile/readdirRecursive— including across guest processes (A writes, B edits, host sees B's content).unlinkof a guest-written file is visible to the host; behavior is specific to host-written content.mkdiris unaffected: guests can create/edit files inside host-made directories normally.node:24-bookworm-slim), core 0.2.15 and 0.2.19, with and withoutsoftware/defaultSoftware.Looks like host writes land in a base/admin layer while guest copy-on-write over those paths lives in an overlay that is dropped at process exit instead of being merged.
Our workaround: host-write only a manifest file, then have an in-VM bootstrap process write the actual seeded files, so all guest-editable content is guest-authored.