---
title: "vfs"
description: "The vfs namespace — the engine's Luau API reference for vfs."
section: "API Reference"
slug: "api-vfs"
canonical: "https://origozero.ai/docs/api-vfs"
updated: "2026-09-07T03:15:14.096864705+00:00"
tags: ["api", "reference"]
---

# vfs

The `vfs` namespace — 79 functions.

## globals/vfs/clearPlayShadow {#globals-vfs-clearplayshadow}

```lua
vfs.clearPlayShadow() -> boolean
```

Forget the entire play-shadow set after a bulk promote or discard.
Tracking only — never touches the bytes.

**Returns** `boolean` — Always true.

```lua
vfs.clearPlayShadow()
```

## globals/vfs/copy {#globals-vfs-copy}

```lua
vfs.copy(src: string, dst: string) -> (boolean, string?)
```

Copy a file OR directory from `src` to `dst`, `cp -r` style. A
directory recurses — every descendant is replicated at the same
relative path under `dst`, `.refs` sidecars included. `.meta`
sidecars are minted fresh, so a copy is a distinct asset with its
own identity. Both paths are absolute. The source may live in any
layer (writable, library mount, builtin, runtime-generated); the
destination must be a writable route.

**Parameters**

- `src` `string` — Source absolute VFS path (file or directory).
- `dst` `string` — Destination absolute VFS path.

**Returns** `(boolean, string?)` — True on success; (false, errmsg) on failure.

```lua
vfs.copy("/zero/runtime/recordings/take1.mp4", "/zero/source/clips/take1.mp4")
```

## globals/vfs/currentAuthor {#globals-vfs-currentauthor}

```lua
vfs.currentAuthor() -> { id: string, name: string? }?
```

The agent this call is attributed to — the author a `/source` write
made right now would be recorded under in the play shadow. Nil when the
call carries no actor identity, which is the case for engine-authored
work and for a caller that presented no token. Compare its `id` against
`vfs.playShadowAuthors()` to separate your own pending edits from a
co-author's.

**Returns** `{ id: string, name: string? }?` — `{ id, name }` for the acting agent, or nil when unattributed.

```lua
local me = vfs.currentAuthor()
print(if me ~= nil then me.id else "unattributed")
```

## globals/vfs/durability {#globals-vfs-durability}

```lua
vfs.durability(paths: string | { string }) -> { durable: boolean, warning: string?, playShadow: string?, shadowed: { string }? }
```

What became of the bytes just written at `paths` — one path, or an
array of them answered as ONE write. `durable` is true when they are where
the call filed them, and false when the play shadow took any of them: live
in this session, disk source untouched, discarded on a guarded play-exit
unless kept. `durable` is present whatever the answer is, so its absence is
never a reading. A non-durable answer carries `warning` (the state, for a
reader scanning values rather than checking a field), `playShadow` (the
routes to disk and what each costs a session other people are running in)
and `shadowed` (which of the given paths the shadow holds).

This is the answer, off the same shadow set and in the same words, that the
`write_file` / `edit_file` / `capture` tools attach to their own results and
that `asset.create` reports as its second return value. Ask it here at any
other site that lands files, so every write surface states where the bytes
went in one set of terms.

**Parameters**

- `paths` `string | { string }` — One VFS path, or an array of paths answered together as one
write. A path resolves the way `vfs.write` resolves its own — absolute or
`@`-rooted as it stands, a bare one under `/source/` — so the answer is
about the file that write landed. A value that is not a path — an
AssetRef, a record, a number, a string with nothing in it — raises rather
than being answered off the empty set it reads as; a list with no entries
names nothing and answers durable.

**Returns** `{ durable: boolean, warning: string?, playShadow: string?, shadowed: { string }? }` — `{ durable, warning?, playShadow?, shadowed? }` — the last three present exactly when `durable` is false.

```lua
local ok = vfs.write(path, body)
local d = vfs.durability(path)
if not d.durable then log.warn(d.warning .. " " .. d.playShadow) end
```

## globals/vfs/evict {#globals-vfs-evict}

```lua
vfs.evict(path: string, opts: VfsOpts?) -> boolean
```

Drop the in-memory bytes for `path` from the writable
MemFs layer without removing the asset. Use after processing
large binaries to reclaim RAM.

**Parameters**

- `path` `string` — VFS path whose bytes should be evicted.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True if MemFs bytes were dropped; false otherwise.

```lua
vfs.evict("/zero/source/textures/imported_big.png")
```

## globals/vfs/exists {#globals-vfs-exists}

```lua
vfs.exists(path: string, opts: VfsOpts?) -> boolean
```

Is the path known to the VFS? Checks the Stage-1 metadata
(`.meta` sidecar / ManifestView) — NOT "are the bytes locally
cached?". Use `vfs.read(path) ~= nil` to confirm bytes are
reachable.

**Parameters**

- `path` `string` — VFS path to check.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True if the path is known regardless of byte cache state.

```lua
assert(vfs.exists("@builtin/models/Cube"))
```

## globals/vfs/isDirectory {#globals-vfs-isdirectory}

```lua
vfs.isDirectory(path: string, opts: VfsOpts?) -> boolean
```

Is ONE path a directory? Answers from reality — the writable
layer's children, a resolver-served folder listing, an explicit
empty-directory marker — so a loose file whose extension collides
with an assetType name (`notes.json`) reads as the file it is while
a real `<name>.<type>/` folder reads as a folder. Costs the same
whatever the containing folder holds; use `vfs.list` when you want
every entry's kind, this when you hold one path.

**Parameters**

- `path` `string` — VFS path to classify.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True if the path is a directory.

```lua
if vfs.isDirectory("/zero/source/Goblin.dynamicAsset") then print("folder asset") end
```

## globals/vfs/isSaveExcluded {#globals-vfs-issaveexcluded}

```lua
vfs.isSaveExcluded(path: string, opts: VfsOpts?) -> boolean
```

Does this path hold content the machine keeps to itself?
`/source/tmp/` is session scratch and `/source/local/` is this
machine's own durable content — each directory itself included,
and everything under it. Both are writable, hot-reloadable and
enumerable like the rest of `/source/`; what separates them is
where they stop. The engine filters them out of every world save
and every peer broadcast, so they reach no world, carry no
manifest row there, and a staging verb handed one refuses it by
name. The match reads a whole path segment, so
`/source/tmpfoo/` is ordinary content. Ask here whenever your
code has to agree with what a world can hold.

**Parameters**

- `path` `string` — VFS path to classify.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True when the path is held back from world saves and sync.

```lua
if not vfs.isSaveExcluded(p) then table.insert(publishable, p) end
```

## globals/vfs/list {#globals-vfs-list}

```lua
vfs.list(path: string?) -> { VfsListEntry }
```

List entries in a VFS directory.

**Parameters**

- `path` `string` _(optional)_ — Directory path (defaults to `/zero`).

**Returns** `{ VfsListEntry }` — Array of `{ name, isDirectory }` tables.

```lua
for _, e in ipairs(vfs.list("/zero/source")) do print(e.name) end
```

## globals/vfs/memResident {#globals-vfs-memresident}

```lua
vfs.memResident() -> { { path: string, bytes: number, kind: string } }
```

List the MemFs entries that are NOT resident-by-default — the writable
in-memory layer's binary blobs and its large text files (text at or above
the inline-text size threshold). These are the bytes `vfs.evict` can
reclaim: the ones kept in RAM rather than left to fall through to the
on-disk BlobStore cache. Small text (resident by default) is omitted. The
audit counterpart to `vfs.evict` and to reading with `{ keep = true }` —
use it to see what encoded bytes are held in RAM, and why.

**Returns** `{ { path: string, bytes: number, kind: string } }` — Array of `{ path, bytes, kind }`; `kind` is `"binary"` for non-text content and `"large-text"` for oversized text. Empty when the VFS isn't up.

```lua
for _, e in ipairs(vfs.memResident()) do print(e.path, e.bytes, e.kind) end
```

## globals/vfs/mkdir {#globals-vfs-mkdir}

```lua
vfs.mkdir(path: string, opts: VfsOpts?) -> boolean
```

Create a directory. `mkdir -p` semantics — idempotent.
Errors if a file already exists at the same path. While play is
running an authored `/source` directory waits for the lock to
lift and the refusal RAISES with the reason; writing a file under
the path creates it as part of that write, and scratch under
`/source/tmp/` creates as in edit mode.

**Parameters**

- `path` `string` — Directory path.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True if the directory exists after the call. A creation the running play session refuses raises with the whole reason.

```lua
vfs.mkdir("/zero/source/scenes/")
```

## globals/vfs/move {#globals-vfs-move}

```lua
vfs.move(src: string, dst: string, opts: { quiet: boolean? }?) -> (boolean, string?)
```

Move a file from `src` to `dst`. By default fires the destination's
write side effects; pass `opts.quiet = true` to suppress them.
While play is running a move takes the source away, so authored
`/source` content that predates play is refused and the refusal
RAISES with the reason; content this play session created moves and
stays tracked on the play shadow.

**Parameters**

- `src` `string` — Source absolute VFS path.
- `dst` `string` — Destination absolute VFS path.
- `opts` `{ quiet: boolean? }` _(optional)_ — Optional `{ quiet: boolean? }`.

**Returns** `(boolean, string?)` — True on success; (false, errmsg) when the paths themselves refuse it. A move the running play session refuses raises with the whole reason instead.

```lua
vfs.move("/zero/source/a.luau", "/zero/source/b.luau")
```

## globals/vfs/mutationSeq {#globals-vfs-mutationseq}

```lua
vfs.mutationSeq() -> number
```

Lifetime count of VFS mutations the engine has APPLIED — the drain's
clock. A write queues its side effects (an asset's content reload, the
assetType's `onChange`, a component or scene registration) and a later
frame runs them; this number advances as each one completes. Read it,
write, then poll for a larger value to learn the queue has moved past the
point you wrote at — instead of waiting a guessed number of frames. It
counts every mutation kind, so it answers about the pipeline rather than
about one file; `asset.reloadSeq(ref)` is the per-asset reading.

**Returns** `number` — Count of applied VFS mutations this session. Monotonic.

```lua
local at = vfs.mutationSeq()
vfs.write("/zero/source/tmp/note.txt", "hi")
repeat task.wait() until vfs.mutationSeq() > at
```

## globals/vfs/pendingWrites {#globals-vfs-pendingwrites}

```lua
vfs.pendingWrites() -> { string }
```

List the `/source` paths with an in-flight local write the synced
manifest has not reflected yet — the read-your-writes frontier. A
just-written file appears here until its upload round-trips and the
synced dirty state catches up; `world.vcsStatus` unions these so a
fresh edit reads back as dirty immediately. Empty when fully synced.

**Returns** `{ string }` — Array of VFS paths with pending (unconfirmed) local writes.

```lua
for _, p in ipairs(vfs.pendingWrites()) do print(p) end
```

## globals/vfs/playShadowAuthors {#globals-vfs-playshadowauthors}

```lua
vfs.playShadowAuthors() -> { [string]: { id: string, name: string? } }
```

The agent behind each currently-shadowed `/source` path: the ZeroMind
user id the write was attributed to, and the username to show for it.
Several agents drive one engine at once and every one of their in-play
source edits sits in the same shadow set, so this is how a review, a
refusal or a verdict tells one agent's pending work from another's. A
path written with no actor identity carries no entry — it belongs to no
agent in particular, and stays settleable by any of them.

**Returns** `{ [string]: { id: string, name: string? } }` — Map of shadowed path to `{ id, name }`.

```lua
local mine = vfs.currentAuthor()
for path, who in pairs(vfs.playShadowAuthors()) do
if mine == nil or who.id ~= mine.id then print(path, "belongs to", who.name) end
end
```

## globals/vfs/playShadowPaths {#globals-vfs-playshadowpaths}

```lua
vfs.playShadowPaths() -> { string }
```

List the `/source` paths edited during running play that are currently
held as copy-on-write SHADOWS (MemFs-only, on-disk original untouched) —
the universal play shadow-copy set. These are the in-play edits persist
promotes over the originals on confirm, or drops on a guarded discard.
Empty outside play or when nothing was edited.

**Returns** `{ string }` — Array of normalized VFS paths currently shadowed.

```lua
for _, p in ipairs(vfs.playShadowPaths()) do print(p) end
```

## globals/vfs/promotePlayShadow {#globals-vfs-promoteplayshadow}

```lua
vfs.promotePlayShadow(path: string) -> string
```

Promote a single play-shadow edit into a canonical write. Re-asserts
the live overlay bytes through the full write pipeline with the play
write lock released, then unmarks the path. The bytes stay in the
engine end to end, so binary content promotes exactly. Takes ONE file
path, and needs the write lock released, so run it inside a pause you
take and hand back. A promotion that cannot happen raises with the
reason: the path is not shadowed, the path is a folder covering
shadowed edits, play is running, the workspace is read-only, or the
write-through failed. A shadow entry whose bytes are gone is dropped
as promoted, so the path comes back with nothing written for it.

**Parameters**

- `path` `string` — Shadowed VFS path to promote (one of vfs.playShadowPaths()).

**Returns** `string` — The path the call settled — it is no longer shadowed.

```lua
engine.paused = true
local promoted = vfs.promotePlayShadow("/zero/source/cover.jpg")
engine.paused = false
```

## globals/vfs/read {#globals-vfs-read}

```lua
vfs.read(path: string, opts: VfsOpts?) -> string?
```

Read a file from the virtual filesystem. Binary-safe.
Returns file contents as a string, or nil if the file is not
known. Relative paths resolve under `opts.root` (default
`/source/`). When called from a coroutine and the bytes
aren't locally cached, transparently yields the coroutine
while the lazy fetch runs.

**Parameters**

- `path` `string` — VFS path.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `string?` — File contents (binary-safe), or nil.

```lua
local src = vfs.read("@builtin/components/Camera.luau")
```

## globals/vfs/readAsync {#globals-vfs-readasync}

```lua
vfs.readAsync(path: string, opts: VfsOpts?) -> string
```

Asynchronous binary-safe read. Returns a promise ID that
resolves to the file contents. Useful for reading render
textures from the main thread without blocking.

**Parameters**

- `path` `string` — VFS path.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `string` — Promise ID — pass to `task.await()`.

```lua
local data = task.await(vfs.readAsync("/zero/runtime/screenshots/last.png"))
```

## globals/vfs/reload {#globals-vfs-reload}

```lua
vfs.reload(modulePath: string?) -> boolean
```

Clear entries from the `require()` cache so the next
`require(name)` re-runs the module's source. Pass a single
module identity to drop only that entry; call with no
arguments to drop every cached module.

**Parameters**

- `modulePath` `string` _(optional)_ — Module identity to reload (omit to reload all).

**Returns** `boolean` — For a single identity, whether a module was cached under that name and has now been dropped — `false` says the name matched nothing. The no-arg form returns true.

```lua
vfs.reload("@mylib/utils.helpers")
```

## globals/vfs/remove {#globals-vfs-remove}

```lua
vfs.remove(path: string, opts: VfsOpts?) -> (boolean, string?)
```

Remove a file. Refuses to remove directories unless
`opts.recursive = true`. Refuses protected system roots. While
play is running, authored `/source` content that predates play is
refused and the refusal RAISES with the reason; content this play
session created is removable, a folder included.

**Parameters**

- `path` `string` — VFS path to remove.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/", recursive = false }`.

**Returns** `(boolean, string?)` — True on success; (false, errmsg) when the path itself refuses the removal. A removal the running play session refuses raises with the whole reason instead, so a `pcall` around the call reads it — and the lock answers ahead of whether the path is there, so a locked `/source` path raises whether or not it holds anything.

```lua
vfs.remove("/zero/source/scratch.luau")
```

## globals/vfs/revertPlayShadow {#globals-vfs-revertplayshadow}

```lua
vfs.revertPlayShadow(path: string) -> string
```

Revert a single play-shadow edit: restore the pre-play copy captured
at the first play-mode write (the last edit-mode state, unstaged edits
included) into the live slot — or remove the file when it did not exist
at that moment — then unmark the path. Hot-reload picks the original
back up, so the running session actually reverts. Takes ONE file path,
and needs the write lock released, so run it inside a pause you take
and hand back. A revert that cannot happen raises with the reason, on
the same terms as vfs.promotePlayShadow.

**Parameters**

- `path` `string` — Shadowed VFS path to revert (one of vfs.playShadowPaths()).

**Returns** `string` — The path that is now reverted and no longer shadowed.

```lua
engine.paused = true
local reverted = vfs.revertPlayShadow("/zero/source/Foo.component/init.luau")
engine.paused = false
```

## globals/vfs/unmarkPlayShadow {#globals-vfs-unmarkplayshadow}

```lua
vfs.unmarkPlayShadow(path: string) -> boolean
```

Forget a single play-shadow path after it has been promoted (saved
over source) or discarded. Tracking only — never touches the bytes.

**Parameters**

- `path` `string` — VFS path to unmark.

**Returns** `boolean` — Always true.

```lua
vfs.unmarkPlayShadow("/zero/source/Foo.component/init.luau")
```

## globals/vfs/unwatch {#globals-vfs-unwatch}

```lua
vfs.unwatch(watcherId: number) -> boolean
```

Remove a previously registered VFS watcher.

**Parameters**

- `watcherId` `number` — Watcher id returned by `vfs.watch`.

**Returns** `boolean` — True if the watcher was found and removed.

```lua
vfs.unwatch(id)
```

## globals/vfs/watch {#globals-vfs-watch}

```lua
vfs.watch(path: string, callback: (string, string) -> ()) -> number
```

Register a callback that fires when a VFS path is written or
removed. Two match modes: exact, or folder/prefix (key ends with
`/`, and fires for any descendant). The callback runs in the VM
that registered it. Returns a watcher id for `vfs.unwatch`.

**Parameters**

- `path` `string` — Exact path, or folder path ending in `/`.
- `callback` `(string, string) -> ()` — `(mutated_path, kind) -> ()`, kind `"write"` or `"remove"`.

**Returns** `number` — Watcher id.

```lua
local id = vfs.watch("/zero/source/", function(path, kind) print(kind, path) end)
```

## globals/vfs/write {#globals-vfs-write}

```lua
vfs.write(path: string, content: string, opts: VfsOpts?) -> (boolean, string?)
```

Write content to a file. Binary-safe. Overwrites existing
files by default — pass `opts.overwrite = false` to refuse to
clobber. While play is running a `/source` write lands on the play
shadow: it succeeds and reads back, live in the session with disk
source untouched, and is discarded on a guarded play-exit unless
accepted. Scratch under `/source/tmp/` writes through untouched.
Pass `opts.durable = true` to say these bytes ARE the source: the
write reaches canonical `/source` with play still running and the
session still in play, hot-reloading the modules and components that
read it, so the edit is observed running in the same play session
with nothing left to promote. A durable write RAISES with the reason
when the bytes cannot become canonical source.

**Parameters**

- `path` `string` — VFS path to write to.
- `content` `string` — File content (binary-safe).
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/", overwrite = true, quiet = false, durable = false }`.

**Returns** `(boolean, string?)` — True on success; on failure returns false + error message. A durable write raises instead of returning false.

```lua
vfs.write("/zero/source/notes.md", body)
vfs.write("/zero/source/game/Vent.component/init.luau", src, { durable = true })
```

## modules/vfs/README {#modules-vfs-readme}

```lua
require("@builtin/modules/api/engine/vfs") -- vfs (also available as global 'vfs')
```

Virtual filesystem — read, write, list, watch. Public Luau surface over the `__vfs` Internal FFI namespace.

Usage: local vfs = require("@builtin/modules/api/engine/vfs")
Also available as global: vfs

## modules/vfs/clearPlayShadow {#modules-vfs-clearplayshadow}

```lua
clearPlayShadow(): boolean
```

Forget the entire play-shadow set after a bulk promote or discard.
Tracking only — never touches the bytes.

```lua
vfs.clearPlayShadow()
```

## modules/vfs/copy {#modules-vfs-copy}

```lua
copy(src: string, dst: string): (boolean, string?)
```

Copy a file OR directory from `src` to `dst`, `cp -r` style. A
directory recurses — every descendant is replicated at the same
relative path under `dst`, `.refs` sidecars included. `.meta`
sidecars are minted fresh, so a copy is a distinct asset with its
own identity. Both paths are absolute. The source may live in any
layer (writable, library mount, builtin, runtime-generated); the
destination must be a writable route.

**Parameters**

- `src` `string` — Source absolute VFS path (file or directory).
- `dst` `string` — Destination absolute VFS path.

```lua
vfs.copy("/zero/runtime/recordings/take1.mp4", "/zero/source/clips/take1.mp4")
```

## modules/vfs/currentAuthor {#modules-vfs-currentauthor}

```lua
currentAuthor(): { id: string, name: string? }?
```

The agent this call is attributed to — the author a `/source` write
made right now would be recorded under in the play shadow. Nil when the
call carries no actor identity, which is the case for engine-authored
work and for a caller that presented no token. Compare its `id` against
`vfs.playShadowAuthors()` to separate your own pending edits from a
co-author's.

```lua
local me = vfs.currentAuthor()
print(if me ~= nil then me.id else "unattributed")
```

## modules/vfs/durability {#modules-vfs-durability}

```lua
durability(paths: string | { string }): { durable: boolean, warning: string?, playShadow: string?, shadowed: { string }? }
```

What became of the bytes just written at `paths` — one path, or an
array of them answered as ONE write. `durable` is true when they are where
the call filed them, and false when the play shadow took any of them: live
in this session, disk source untouched, discarded on a guarded play-exit
unless kept. `durable` is present whatever the answer is, so its absence is
never a reading. A non-durable answer carries `warning` (the state, for a
reader scanning values rather than checking a field), `playShadow` (the
routes to disk and what each costs a session other people are running in)
and `shadowed` (which of the given paths the shadow holds).

This is the answer, off the same shadow set and in the same words, that the
`write_file` / `edit_file` / `capture` tools attach to their own results and
that `asset.create` reports as its second return value. Ask it here at any
other site that lands files, so every write surface states where the bytes
went in one set of terms.

**Parameters**

- `paths` `string | { string }` — One VFS path, or an array of paths answered together as one
write. A path resolves the way `vfs.write` resolves its own — absolute or
`@`-rooted as it stands, a bare one under `/source/` — so the answer is
about the file that write landed. A value that is not a path — an
AssetRef, a record, a number, a string with nothing in it — raises rather
than being answered off the empty set it reads as; a list with no entries
names nothing and answers durable.

```lua
local ok = vfs.write(path, body)
local d = vfs.durability(path)
if not d.durable then log.warn(d.warning .. " " .. d.playShadow) end
```

## modules/vfs/evict {#modules-vfs-evict}

```lua
evict(path: string, opts: VfsOpts?): boolean
```

Drop the in-memory bytes for `path` from the writable
MemFs layer without removing the asset. Use after processing
large binaries to reclaim RAM.

**Parameters**

- `path` `string` — VFS path whose bytes should be evicted.
- `opts` `VfsOpts?` _(optional)_ — `{ root = "/source/" }`.

```lua
vfs.evict("/zero/source/textures/imported_big.png")
```

## modules/vfs/exists {#modules-vfs-exists}

```lua
exists(path: string, opts: VfsOpts?): boolean
```

Is the path known to the VFS? Checks the Stage-1 metadata
(`.meta` sidecar / ManifestView) — NOT "are the bytes locally
cached?". Use `vfs.read(path) ~= nil` to confirm bytes are
reachable.

**Parameters**

- `path` `string` — VFS path to check.
- `opts` `VfsOpts?` _(optional)_ — `{ root = "/source/" }`.

```lua
assert(vfs.exists("@builtin/models/Cube"))
```

## modules/vfs/isDirectory {#modules-vfs-isdirectory}

```lua
isDirectory(path: string, opts: VfsOpts?): boolean
```

Is ONE path a directory? Answers from reality — the writable
layer's children, a resolver-served folder listing, an explicit
empty-directory marker — so a loose file whose extension collides
with an assetType name (`notes.json`) reads as the file it is while
a real `<name>.<type>/` folder reads as a folder. Costs the same
whatever the containing folder holds; use `vfs.list` when you want
every entry's kind, this when you hold one path.

**Parameters**

- `path` `string` — VFS path to classify.
- `opts` `VfsOpts?` _(optional)_ — `{ root = "/source/" }`.

```lua
if vfs.isDirectory("/zero/source/Goblin.dynamicAsset") then print("folder asset") end
```

## modules/vfs/isSaveExcluded {#modules-vfs-issaveexcluded}

```lua
isSaveExcluded(path: string, opts: VfsOpts?): boolean
```

Does this path hold content the machine keeps to itself?
`/source/tmp/` is session scratch and `/source/local/` is this
machine's own durable content — each directory itself included,
and everything under it. Both are writable, hot-reloadable and
enumerable like the rest of `/source/`; what separates them is
where they stop. The engine filters them out of every world save
and every peer broadcast, so they reach no world, carry no
manifest row there, and a staging verb handed one refuses it by
name. The match reads a whole path segment, so
`/source/tmpfoo/` is ordinary content. Ask here whenever your
code has to agree with what a world can hold.

**Parameters**

- `path` `string` — VFS path to classify.
- `opts` `VfsOpts?` _(optional)_ — `{ root = "/source/" }`.

```lua
if not vfs.isSaveExcluded(p) then table.insert(publishable, p) end
```

## modules/vfs/list {#modules-vfs-list}

```lua
list(path: string?): { VfsListEntry }
```

List entries in a VFS directory.

**Parameters**

- `path` `string?` _(optional)_ — Directory path (defaults to `/zero`).

```lua
for _, e in ipairs(vfs.list("/zero/source")) do print(e.name) end
```

## modules/vfs/memResident {#modules-vfs-memresident}

```lua
memResident(): { { path: string, bytes: number, kind: string } }
```

List the MemFs entries that are NOT resident-by-default — the writable
in-memory layer's binary blobs and its large text files (text at or above
the inline-text size threshold). These are the bytes `vfs.evict` can
reclaim: the ones kept in RAM rather than left to fall through to the
on-disk BlobStore cache. Small text (resident by default) is omitted. The
audit counterpart to `vfs.evict` and to reading with `{ keep = true }` —
use it to see what encoded bytes are held in RAM, and why.

```lua
for _, e in ipairs(vfs.memResident()) do print(e.path, e.bytes, e.kind) end
```

## modules/vfs/mkdir {#modules-vfs-mkdir}

```lua
mkdir(path: string, opts: VfsOpts?): boolean
```

Create a directory. `mkdir -p` semantics — idempotent.
Errors if a file already exists at the same path. While play is
running an authored `/source` directory waits for the lock to
lift and the refusal RAISES with the reason; writing a file under
the path creates it as part of that write, and scratch under
`/source/tmp/` creates as in edit mode.

**Parameters**

- `path` `string` — Directory path.
- `opts` `VfsOpts?` _(optional)_ — `{ root = "/source/" }`.

```lua
vfs.mkdir("/zero/source/scenes/")
```

## modules/vfs/move {#modules-vfs-move}

```lua
move(src: string, dst: string, opts: { quiet: boolean? }?): (boolean, string?)
```

Move a file from `src` to `dst`. By default fires the destination's
write side effects; pass `opts.quiet = true` to suppress them.
While play is running a move takes the source away, so authored
`/source` content that predates play is refused and the refusal
RAISES with the reason; content this play session created moves and
stays tracked on the play shadow.

**Parameters**

- `src` `string` — Source absolute VFS path.
- `dst` `string` — Destination absolute VFS path.
- `opts` `{ quiet: boolean? }?` _(optional)_ — Optional `{ quiet: boolean? }`.

```lua
vfs.move("/zero/source/a.luau", "/zero/source/b.luau")
```

## modules/vfs/mutationSeq {#modules-vfs-mutationseq}

```lua
mutationSeq(): number
```

Lifetime count of VFS mutations the engine has APPLIED — the drain's
clock. A write queues its side effects (an asset's content reload, the
assetType's `onChange`, a component or scene registration) and a later
frame runs them; this number advances as each one completes. Read it,
write, then poll for a larger value to learn the queue has moved past the
point you wrote at — instead of waiting a guessed number of frames. It
counts every mutation kind, so it answers about the pipeline rather than
about one file; `asset.reloadSeq(ref)` is the per-asset reading.

```lua
local at = vfs.mutationSeq()
vfs.write("/zero/source/tmp/note.txt", "hi")
repeat task.wait() until vfs.mutationSeq() > at
```

## modules/vfs/pendingWrites {#modules-vfs-pendingwrites}

```lua
pendingWrites(): { string }
```

List the `/source` paths with an in-flight local write the synced
manifest has not reflected yet — the read-your-writes frontier. A
just-written file appears here until its upload round-trips and the
synced dirty state catches up; `world.vcsStatus` unions these so a
fresh edit reads back as dirty immediately. Empty when fully synced.

```lua
for _, p in ipairs(vfs.pendingWrites()) do print(p) end
```

## modules/vfs/playShadowAuthors {#modules-vfs-playshadowauthors}

```lua
playShadowAuthors(): { [string]: { id: string, name: string? } }
```

The agent behind each currently-shadowed `/source` path: the ZeroMind
user id the write was attributed to, and the username to show for it.
Several agents drive one engine at once and every one of their in-play
source edits sits in the same shadow set, so this is how a review, a
refusal or a verdict tells one agent's pending work from another's. A
path written with no actor identity carries no entry — it belongs to no
agent in particular, and stays settleable by any of them.

```lua
local mine = vfs.currentAuthor()
for path, who in pairs(vfs.playShadowAuthors()) do
if mine == nil or who.id ~= mine.id then print(path, "belongs to", who.name) end
end
```

## modules/vfs/playShadowPaths {#modules-vfs-playshadowpaths}

```lua
playShadowPaths(): { string }
```

List the `/source` paths edited during running play that are currently
held as copy-on-write SHADOWS (MemFs-only, on-disk original untouched) —
the universal play shadow-copy set. These are the in-play edits persist
promotes over the originals on confirm, or drops on a guarded discard.
Empty outside play or when nothing was edited.

```lua
for _, p in ipairs(vfs.playShadowPaths()) do print(p) end
```

## modules/vfs/promotePlayShadow {#modules-vfs-promoteplayshadow}

```lua
promotePlayShadow(path: string): string
```

Promote a single play-shadow edit into a canonical write. Re-asserts
the live overlay bytes through the full write pipeline with the play
write lock released, then unmarks the path. The bytes stay in the
engine end to end, so binary content promotes exactly. Takes ONE file
path, and needs the write lock released, so run it inside a pause you
take and hand back. A promotion that cannot happen raises with the
reason: the path is not shadowed, the path is a folder covering
shadowed edits, play is running, the workspace is read-only, or the
write-through failed. A shadow entry whose bytes are gone is dropped
as promoted, so the path comes back with nothing written for it.

**Parameters**

- `path` `string` — Shadowed VFS path to promote (one of vfs.playShadowPaths()).

```lua
engine.paused = true
local promoted = vfs.promotePlayShadow("/zero/source/cover.jpg")
engine.paused = false
```

## modules/vfs/read {#modules-vfs-read}

```lua
read(path: string, opts: VfsOpts?): string?
```

Read a file from the virtual filesystem. Binary-safe.
Returns file contents as a string, or nil if the file is not
known. Relative paths resolve under `opts.root` (default
`/source/`). When called from a coroutine and the bytes
aren't locally cached, transparently yields the coroutine
while the lazy fetch runs.

**Parameters**

- `path` `string` — VFS path.
- `opts` `VfsOpts?` _(optional)_ — `{ root = "/source/" }`.

```lua
local src = vfs.read("@builtin/components/Camera.luau")
```

## modules/vfs/readAsync {#modules-vfs-readasync}

```lua
readAsync(path: string, opts: VfsOpts?): string
```

Asynchronous binary-safe read. Returns a promise ID that
resolves to the file contents. Useful for reading render
textures from the main thread without blocking.

**Parameters**

- `path` `string` — VFS path.
- `opts` `VfsOpts?` _(optional)_ — `{ root = "/source/" }`.

```lua
local data = task.await(vfs.readAsync("/zero/runtime/screenshots/last.png"))
```

## modules/vfs/reload {#modules-vfs-reload}

```lua
reload(modulePath: string?): boolean
```

Clear entries from the `require()` cache so the next
`require(name)` re-runs the module's source. Pass a single
module identity to drop only that entry; call with no
arguments to drop every cached module.

**Parameters**

- `modulePath` `string?` _(optional)_ — Module identity to reload (omit to reload all).

```lua
vfs.reload("@mylib/utils.helpers")
```

## modules/vfs/remove {#modules-vfs-remove}

```lua
remove(path: string, opts: VfsOpts?): (boolean, string?)
```

Remove a file. Refuses to remove directories unless
`opts.recursive = true`. Refuses protected system roots. While
play is running, authored `/source` content that predates play is
refused and the refusal RAISES with the reason; content this play
session created is removable, a folder included.

**Parameters**

- `path` `string` — VFS path to remove.
- `opts` `VfsOpts?` _(optional)_ — `{ root = "/source/", recursive = false }`.

```lua
vfs.remove("/zero/source/scratch.luau")
```

## modules/vfs/revertPlayShadow {#modules-vfs-revertplayshadow}

```lua
revertPlayShadow(path: string): string
```

Revert a single play-shadow edit: restore the pre-play copy captured
at the first play-mode write (the last edit-mode state, unstaged edits
included) into the live slot — or remove the file when it did not exist
at that moment — then unmark the path. Hot-reload picks the original
back up, so the running session actually reverts. Takes ONE file path,
and needs the write lock released, so run it inside a pause you take
and hand back. A revert that cannot happen raises with the reason, on
the same terms as vfs.promotePlayShadow.

**Parameters**

- `path` `string` — Shadowed VFS path to revert (one of vfs.playShadowPaths()).

```lua
engine.paused = true
local reverted = vfs.revertPlayShadow("/zero/source/Foo.component/init.luau")
engine.paused = false
```

## modules/vfs/unmarkPlayShadow {#modules-vfs-unmarkplayshadow}

```lua
unmarkPlayShadow(path: string): boolean
```

Forget a single play-shadow path after it has been promoted (saved
over source) or discarded. Tracking only — never touches the bytes.

**Parameters**

- `path` `string` — VFS path to unmark.

```lua
vfs.unmarkPlayShadow("/zero/source/Foo.component/init.luau")
```

## modules/vfs/unwatch {#modules-vfs-unwatch}

```lua
unwatch(watcherId: number): boolean
```

Remove a previously registered VFS watcher.

**Parameters**

- `watcherId` `number` — Watcher id returned by `vfs.watch`.

```lua
vfs.unwatch(id)
```

## modules/vfs/watch {#modules-vfs-watch}

```lua
watch(path: string, callback: (string, string) -> ()): number
```

Register a callback that fires when a VFS path is written or
removed. Two match modes: exact, or folder/prefix (key ends with
`/`, and fires for any descendant). The callback runs in the VM
that registered it. Returns a watcher id for `vfs.unwatch`.

**Parameters**

- `path` `string` — Exact path, or folder path ending in `/`.
- `callback` `(string, string) -> ()` — `(mutated_path, kind) -> ()`, kind `"write"` or `"remove"`.

```lua
local id = vfs.watch("/zero/source/", function(path, kind) print(kind, path) end)
```

## modules/vfs/write {#modules-vfs-write}

```lua
write(path: string, content: string, opts: VfsOpts?): (boolean, string?)
```

Write content to a file. Binary-safe. Overwrites existing
files by default — pass `opts.overwrite = false` to refuse to
clobber. While play is running a `/source` write lands on the play
shadow: it succeeds and reads back, live in the session with disk
source untouched, and is discarded on a guarded play-exit unless
accepted. Scratch under `/source/tmp/` writes through untouched.
Pass `opts.durable = true` to say these bytes ARE the source: the
write reaches canonical `/source` with play still running and the
session still in play, hot-reloading the modules and components that
read it, so the edit is observed running in the same play session
with nothing left to promote. A durable write RAISES with the reason
when the bytes cannot become canonical source.

**Parameters**

- `path` `string` — VFS path to write to.
- `content` `string` — File content (binary-safe).
- `opts` `VfsOpts?` _(optional)_ — `{ root = "/source/", overwrite = true, quiet = false, durable = false }`.

```lua
vfs.write("/zero/source/notes.md", body)
vfs.write("/zero/source/game/Vent.component/init.luau", src, { durable = true })
```

## typed/builtin//modules/api/engine/vfs/vfs/clearPlayShadow {#typed-builtin-modules-api-engine-vfs-vfs-clearplayshadow}

```lua
vfs.clearPlayShadow() -> boolean
```

Forget the entire play-shadow set after a bulk promote or discard.
Tracking only — never touches the bytes.

**Returns** `boolean` — Always true.

```lua
vfs.clearPlayShadow()
```

## typed/builtin//modules/api/engine/vfs/vfs/copy {#typed-builtin-modules-api-engine-vfs-vfs-copy}

```lua
vfs.copy(src: string, dst: string) -> (boolean, string?)
```

Copy a file OR directory from `src` to `dst`, `cp -r` style. A
directory recurses — every descendant is replicated at the same
relative path under `dst`, `.refs` sidecars included. `.meta`
sidecars are minted fresh, so a copy is a distinct asset with its
own identity. Both paths are absolute. The source may live in any
layer (writable, library mount, builtin, runtime-generated); the
destination must be a writable route.

**Parameters**

- `src` `string` — Source absolute VFS path (file or directory).
- `dst` `string` — Destination absolute VFS path.

**Returns** `(boolean, string?)` — True on success; (false, errmsg) on failure.

```lua
vfs.copy("/zero/runtime/recordings/take1.mp4", "/zero/source/clips/take1.mp4")
```

## typed/builtin//modules/api/engine/vfs/vfs/currentAuthor {#typed-builtin-modules-api-engine-vfs-vfs-currentauthor}

```lua
vfs.currentAuthor() -> { id: string, name: string? }?
```

The agent this call is attributed to — the author a `/source` write
made right now would be recorded under in the play shadow. Nil when the
call carries no actor identity, which is the case for engine-authored
work and for a caller that presented no token. Compare its `id` against
`vfs.playShadowAuthors()` to separate your own pending edits from a
co-author's.

**Returns** `{ id: string, name: string? }?` — `{ id, name }` for the acting agent, or nil when unattributed.

```lua
local me = vfs.currentAuthor()
print(if me ~= nil then me.id else "unattributed")
```

## typed/builtin//modules/api/engine/vfs/vfs/durability {#typed-builtin-modules-api-engine-vfs-vfs-durability}

```lua
vfs.durability(paths: string | { string }) -> { durable: boolean, warning: string?, playShadow: string?, shadowed: { string }? }
```

What became of the bytes just written at `paths` — one path, or an
array of them answered as ONE write. `durable` is true when they are where
the call filed them, and false when the play shadow took any of them: live
in this session, disk source untouched, discarded on a guarded play-exit
unless kept. `durable` is present whatever the answer is, so its absence is
never a reading. A non-durable answer carries `warning` (the state, for a
reader scanning values rather than checking a field), `playShadow` (the
routes to disk and what each costs a session other people are running in)
and `shadowed` (which of the given paths the shadow holds).

This is the answer, off the same shadow set and in the same words, that the
`write_file` / `edit_file` / `capture` tools attach to their own results and
that `asset.create` reports as its second return value. Ask it here at any
other site that lands files, so every write surface states where the bytes
went in one set of terms.

**Parameters**

- `paths` `string | { string }` — One VFS path, or an array of paths answered together as one
write. A path resolves the way `vfs.write` resolves its own — absolute or
`@`-rooted as it stands, a bare one under `/source/` — so the answer is
about the file that write landed. A value that is not a path — an
AssetRef, a record, a number, a string with nothing in it — raises rather
than being answered off the empty set it reads as; a list with no entries
names nothing and answers durable.

**Returns** `{ durable: boolean, warning: string?, playShadow: string?, shadowed: { string }? }` — `{ durable, warning?, playShadow?, shadowed? }` — the last three present exactly when `durable` is false.

```lua
local ok = vfs.write(path, body)
local d = vfs.durability(path)
if not d.durable then log.warn(d.warning .. " " .. d.playShadow) end
```

## typed/builtin//modules/api/engine/vfs/vfs/evict {#typed-builtin-modules-api-engine-vfs-vfs-evict}

```lua
vfs.evict(path: string, opts: VfsOpts?) -> boolean
```

Drop the in-memory bytes for `path` from the writable
MemFs layer without removing the asset. Use after processing
large binaries to reclaim RAM.

**Parameters**

- `path` `string` — VFS path whose bytes should be evicted.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True if MemFs bytes were dropped; false otherwise.

```lua
vfs.evict("/zero/source/textures/imported_big.png")
```

## typed/builtin//modules/api/engine/vfs/vfs/exists {#typed-builtin-modules-api-engine-vfs-vfs-exists}

```lua
vfs.exists(path: string, opts: VfsOpts?) -> boolean
```

Is the path known to the VFS? Checks the Stage-1 metadata
(`.meta` sidecar / ManifestView) — NOT "are the bytes locally
cached?". Use `vfs.read(path) ~= nil` to confirm bytes are
reachable.

**Parameters**

- `path` `string` — VFS path to check.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True if the path is known regardless of byte cache state.

```lua
assert(vfs.exists("@builtin/models/Cube"))
```

## typed/builtin//modules/api/engine/vfs/vfs/isDirectory {#typed-builtin-modules-api-engine-vfs-vfs-isdirectory}

```lua
vfs.isDirectory(path: string, opts: VfsOpts?) -> boolean
```

Is ONE path a directory? Answers from reality — the writable
layer's children, a resolver-served folder listing, an explicit
empty-directory marker — so a loose file whose extension collides
with an assetType name (`notes.json`) reads as the file it is while
a real `<name>.<type>/` folder reads as a folder. Costs the same
whatever the containing folder holds; use `vfs.list` when you want
every entry's kind, this when you hold one path.

**Parameters**

- `path` `string` — VFS path to classify.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True if the path is a directory.

```lua
if vfs.isDirectory("/zero/source/Goblin.dynamicAsset") then print("folder asset") end
```

## typed/builtin//modules/api/engine/vfs/vfs/isSaveExcluded {#typed-builtin-modules-api-engine-vfs-vfs-issaveexcluded}

```lua
vfs.isSaveExcluded(path: string, opts: VfsOpts?) -> boolean
```

Does this path hold content the machine keeps to itself?
`/source/tmp/` is session scratch and `/source/local/` is this
machine's own durable content — each directory itself included,
and everything under it. Both are writable, hot-reloadable and
enumerable like the rest of `/source/`; what separates them is
where they stop. The engine filters them out of every world save
and every peer broadcast, so they reach no world, carry no
manifest row there, and a staging verb handed one refuses it by
name. The match reads a whole path segment, so
`/source/tmpfoo/` is ordinary content. Ask here whenever your
code has to agree with what a world can hold.

**Parameters**

- `path` `string` — VFS path to classify.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True when the path is held back from world saves and sync.

```lua
if not vfs.isSaveExcluded(p) then table.insert(publishable, p) end
```

## typed/builtin//modules/api/engine/vfs/vfs/list {#typed-builtin-modules-api-engine-vfs-vfs-list}

```lua
vfs.list(path: string?) -> { VfsListEntry }
```

List entries in a VFS directory.

## typed/builtin//modules/api/engine/vfs/vfs/memResident {#typed-builtin-modules-api-engine-vfs-vfs-memresident}

```lua
vfs.memResident() -> { { path: string, bytes: number, kind: string } }
```

List the MemFs entries that are NOT resident-by-default — the writable
in-memory layer's binary blobs and its large text files (text at or above
the inline-text size threshold). These are the bytes `vfs.evict` can
reclaim: the ones kept in RAM rather than left to fall through to the
on-disk BlobStore cache. Small text (resident by default) is omitted. The
audit counterpart to `vfs.evict` and to reading with `{ keep = true }` —
use it to see what encoded bytes are held in RAM, and why.

**Returns** `{ { path: string, bytes: number, kind: string } }` — Array of `{ path, bytes, kind }`; `kind` is `"binary"` for non-text content and `"large-text"` for oversized text. Empty when the VFS isn't up.

```lua
for _, e in ipairs(vfs.memResident()) do print(e.path, e.bytes, e.kind) end
```

## typed/builtin//modules/api/engine/vfs/vfs/mkdir {#typed-builtin-modules-api-engine-vfs-vfs-mkdir}

```lua
vfs.mkdir(path: string, opts: VfsOpts?) -> boolean
```

Create a directory. `mkdir -p` semantics — idempotent.
Errors if a file already exists at the same path. While play is
running an authored `/source` directory waits for the lock to
lift and the refusal RAISES with the reason; writing a file under
the path creates it as part of that write, and scratch under
`/source/tmp/` creates as in edit mode.

**Parameters**

- `path` `string` — Directory path.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `boolean` — True if the directory exists after the call. A creation the running play session refuses raises with the whole reason.

```lua
vfs.mkdir("/zero/source/scenes/")
```

## typed/builtin//modules/api/engine/vfs/vfs/move {#typed-builtin-modules-api-engine-vfs-vfs-move}

```lua
vfs.move(src: string, dst: string, opts: { quiet: boolean? }?) -> (boolean, string?)
```

Move a file from `src` to `dst`. By default fires the destination's
write side effects; pass `opts.quiet = true` to suppress them.
While play is running a move takes the source away, so authored
`/source` content that predates play is refused and the refusal
RAISES with the reason; content this play session created moves and
stays tracked on the play shadow.

**Parameters**

- `src` `string` — Source absolute VFS path.
- `dst` `string` — Destination absolute VFS path.
- `opts` `{ quiet: boolean? }` _(optional)_ — Optional `{ quiet: boolean? }`.

**Returns** `(boolean, string?)` — True on success; (false, errmsg) when the paths themselves refuse it. A move the running play session refuses raises with the whole reason instead.

```lua
vfs.move("/zero/source/a.luau", "/zero/source/b.luau")
```

## typed/builtin//modules/api/engine/vfs/vfs/mutationSeq {#typed-builtin-modules-api-engine-vfs-vfs-mutationseq}

```lua
vfs.mutationSeq() -> number
```

Lifetime count of VFS mutations the engine has APPLIED — the drain's
clock. A write queues its side effects (an asset's content reload, the
assetType's `onChange`, a component or scene registration) and a later
frame runs them; this number advances as each one completes. Read it,
write, then poll for a larger value to learn the queue has moved past the
point you wrote at — instead of waiting a guessed number of frames. It
counts every mutation kind, so it answers about the pipeline rather than
about one file; `asset.reloadSeq(ref)` is the per-asset reading.

**Returns** `number` — Count of applied VFS mutations this session. Monotonic.

```lua
local at = vfs.mutationSeq()
vfs.write("/zero/source/tmp/note.txt", "hi")
repeat task.wait() until vfs.mutationSeq() > at
```

## typed/builtin//modules/api/engine/vfs/vfs/pendingWrites {#typed-builtin-modules-api-engine-vfs-vfs-pendingwrites}

```lua
vfs.pendingWrites() -> { string }
```

List the `/source` paths with an in-flight local write the synced
manifest has not reflected yet — the read-your-writes frontier. A
just-written file appears here until its upload round-trips and the
synced dirty state catches up; `world.vcsStatus` unions these so a
fresh edit reads back as dirty immediately. Empty when fully synced.

**Returns** `{ string }` — Array of VFS paths with pending (unconfirmed) local writes.

```lua
for _, p in ipairs(vfs.pendingWrites()) do print(p) end
```

## typed/builtin//modules/api/engine/vfs/vfs/playShadowAuthors {#typed-builtin-modules-api-engine-vfs-vfs-playshadowauthors}

```lua
vfs.playShadowAuthors() -> { [string]: { id: string, name: string? } }
```

The agent behind each currently-shadowed `/source` path: the ZeroMind
user id the write was attributed to, and the username to show for it.
Several agents drive one engine at once and every one of their in-play
source edits sits in the same shadow set, so this is how a review, a
refusal or a verdict tells one agent's pending work from another's. A
path written with no actor identity carries no entry — it belongs to no
agent in particular, and stays settleable by any of them.

**Returns** `{ [string]: { id: string, name: string? } }` — Map of shadowed path to `{ id, name }`.

```lua
local mine = vfs.currentAuthor()
for path, who in pairs(vfs.playShadowAuthors()) do
if mine == nil or who.id ~= mine.id then print(path, "belongs to", who.name) end
end
```

## typed/builtin//modules/api/engine/vfs/vfs/playShadowPaths {#typed-builtin-modules-api-engine-vfs-vfs-playshadowpaths}

```lua
vfs.playShadowPaths() -> { string }
```

List the `/source` paths edited during running play that are currently
held as copy-on-write SHADOWS (MemFs-only, on-disk original untouched) —
the universal play shadow-copy set. These are the in-play edits persist
promotes over the originals on confirm, or drops on a guarded discard.
Empty outside play or when nothing was edited.

**Returns** `{ string }` — Array of normalized VFS paths currently shadowed.

```lua
for _, p in ipairs(vfs.playShadowPaths()) do print(p) end
```

## typed/builtin//modules/api/engine/vfs/vfs/promotePlayShadow {#typed-builtin-modules-api-engine-vfs-vfs-promoteplayshadow}

```lua
vfs.promotePlayShadow(path: string) -> string
```

Promote a single play-shadow edit into a canonical write. Re-asserts
the live overlay bytes through the full write pipeline with the play
write lock released, then unmarks the path. The bytes stay in the
engine end to end, so binary content promotes exactly. Takes ONE file
path, and needs the write lock released, so run it inside a pause you
take and hand back. A promotion that cannot happen raises with the
reason: the path is not shadowed, the path is a folder covering
shadowed edits, play is running, the workspace is read-only, or the
write-through failed. A shadow entry whose bytes are gone is dropped
as promoted, so the path comes back with nothing written for it.

**Parameters**

- `path` `string` — Shadowed VFS path to promote (one of vfs.playShadowPaths()).

**Returns** `string` — The path the call settled — it is no longer shadowed.

```lua
engine.paused = true
local promoted = vfs.promotePlayShadow("/zero/source/cover.jpg")
engine.paused = false
```

## typed/builtin//modules/api/engine/vfs/vfs/read {#typed-builtin-modules-api-engine-vfs-vfs-read}

```lua
vfs.read(path: string, opts: VfsOpts?) -> string?
```

Read a file from the virtual filesystem. Binary-safe.
Returns file contents as a string, or nil if the file is not
known. Relative paths resolve under `opts.root` (default
`/source/`). When called from a coroutine and the bytes
aren't locally cached, transparently yields the coroutine
while the lazy fetch runs.

## typed/builtin//modules/api/engine/vfs/vfs/readAsync {#typed-builtin-modules-api-engine-vfs-vfs-readasync}

```lua
vfs.readAsync(path: string, opts: VfsOpts?) -> string
```

Asynchronous binary-safe read. Returns a promise ID that
resolves to the file contents. Useful for reading render
textures from the main thread without blocking.

**Parameters**

- `path` `string` — VFS path.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/" }`.

**Returns** `string` — Promise ID — pass to `task.await()`.

```lua
local data = task.await(vfs.readAsync("/zero/runtime/screenshots/last.png"))
```

## typed/builtin//modules/api/engine/vfs/vfs/reload {#typed-builtin-modules-api-engine-vfs-vfs-reload}

```lua
vfs.reload(modulePath: string?) -> boolean
```

Clear entries from the `require()` cache so the next
`require(name)` re-runs the module's source. Pass a single
module identity to drop only that entry; call with no
arguments to drop every cached module.

**Parameters**

- `modulePath` `string` _(optional)_ — Module identity to reload (omit to reload all).

**Returns** `boolean` — For a single identity, whether a module was cached under that name and has now been dropped — `false` says the name matched nothing. The no-arg form returns true.

```lua
vfs.reload("@mylib/utils.helpers")
```

## typed/builtin//modules/api/engine/vfs/vfs/remove {#typed-builtin-modules-api-engine-vfs-vfs-remove}

```lua
vfs.remove(path: string, opts: VfsOpts?) -> (boolean, string?)
```

Remove a file. Refuses to remove directories unless
`opts.recursive = true`. Refuses protected system roots. While
play is running, authored `/source` content that predates play is
refused and the refusal RAISES with the reason; content this play
session created is removable, a folder included.

**Parameters**

- `path` `string` — VFS path to remove.
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/", recursive = false }`.

**Returns** `(boolean, string?)` — True on success; (false, errmsg) when the path itself refuses the removal. A removal the running play session refuses raises with the whole reason instead, so a `pcall` around the call reads it — and the lock answers ahead of whether the path is there, so a locked `/source` path raises whether or not it holds anything.

```lua
vfs.remove("/zero/source/scratch.luau")
```

## typed/builtin//modules/api/engine/vfs/vfs/revertPlayShadow {#typed-builtin-modules-api-engine-vfs-vfs-revertplayshadow}

```lua
vfs.revertPlayShadow(path: string) -> string
```

Revert a single play-shadow edit: restore the pre-play copy captured
at the first play-mode write (the last edit-mode state, unstaged edits
included) into the live slot — or remove the file when it did not exist
at that moment — then unmark the path. Hot-reload picks the original
back up, so the running session actually reverts. Takes ONE file path,
and needs the write lock released, so run it inside a pause you take
and hand back. A revert that cannot happen raises with the reason, on
the same terms as vfs.promotePlayShadow.

**Parameters**

- `path` `string` — Shadowed VFS path to revert (one of vfs.playShadowPaths()).

**Returns** `string` — The path that is now reverted and no longer shadowed.

```lua
engine.paused = true
local reverted = vfs.revertPlayShadow("/zero/source/Foo.component/init.luau")
engine.paused = false
```

## typed/builtin//modules/api/engine/vfs/vfs/unmarkPlayShadow {#typed-builtin-modules-api-engine-vfs-vfs-unmarkplayshadow}

```lua
vfs.unmarkPlayShadow(path: string) -> boolean
```

Forget a single play-shadow path after it has been promoted (saved
over source) or discarded. Tracking only — never touches the bytes.

**Parameters**

- `path` `string` — VFS path to unmark.

**Returns** `boolean` — Always true.

```lua
vfs.unmarkPlayShadow("/zero/source/Foo.component/init.luau")
```

## typed/builtin//modules/api/engine/vfs/vfs/unwatch {#typed-builtin-modules-api-engine-vfs-vfs-unwatch}

```lua
vfs.unwatch(watcherId: number) -> boolean
```

Remove a previously registered VFS watcher.

**Parameters**

- `watcherId` `number` — Watcher id returned by `vfs.watch`.

**Returns** `boolean` — True if the watcher was found and removed.

```lua
vfs.unwatch(id)
```

## typed/builtin//modules/api/engine/vfs/vfs/watch {#typed-builtin-modules-api-engine-vfs-vfs-watch}

```lua
vfs.watch(path: string, callback: (string, string) -> ()) -> number
```

Register a callback that fires when a VFS path is written or
removed. Two match modes: exact, or folder/prefix (key ends with
`/`, and fires for any descendant). The callback runs in the VM
that registered it. Returns a watcher id for `vfs.unwatch`.

**Parameters**

- `path` `string` — Exact path, or folder path ending in `/`.
- `callback` `(string, string) -> ()` — `(mutated_path, kind) -> ()`, kind `"write"` or `"remove"`.

**Returns** `number` — Watcher id.

```lua
local id = vfs.watch("/zero/source/", function(path, kind) print(kind, path) end)
```

## typed/builtin//modules/api/engine/vfs/vfs/write {#typed-builtin-modules-api-engine-vfs-vfs-write}

```lua
vfs.write(path: string, content: string, opts: VfsOpts?) -> (boolean, string?)
```

Write content to a file. Binary-safe. Overwrites existing
files by default — pass `opts.overwrite = false` to refuse to
clobber. While play is running a `/source` write lands on the play
shadow: it succeeds and reads back, live in the session with disk
source untouched, and is discarded on a guarded play-exit unless
accepted. Scratch under `/source/tmp/` writes through untouched.
Pass `opts.durable = true` to say these bytes ARE the source: the
write reaches canonical `/source` with play still running and the
session still in play, hot-reloading the modules and components that
read it, so the edit is observed running in the same play session
with nothing left to promote. A durable write RAISES with the reason
when the bytes cannot become canonical source.

**Parameters**

- `path` `string` — VFS path to write to.
- `content` `string` — File content (binary-safe).
- `opts` `VfsOpts` _(optional)_ — `{ root = "/source/", overwrite = true, quiet = false, durable = false }`.

**Returns** `(boolean, string?)` — True on success; on failure returns false + error message. A durable write raises instead of returning false.

```lua
vfs.write("/zero/source/notes.md", body)
vfs.write("/zero/source/game/Vent.component/init.luau", src, { durable = true })
```
