Skip to content

Filesystem

Read, write, and manage files within sandboxed filesystem volumes.

For filesystem configuration, see Filesystem.

local fs = require("fs")

Get a filesystem volume by registry ID:

local vol, err = fs.get("app:storage")
if err then
return nil, err
end
local content = vol:readfile("/config.json")
ParameterTypeDescription
namestringVolume registry ID

Returns: FS, error

Volumes don't require explicit release. They're managed at the system level and become unavailable if the filesystem is detached from the registry.

Read entire file contents:

local vol = fs.get("app:config")
local data, err = vol:readfile("/settings.json")
if err then
return nil, err
end
local config = json.decode(data)

For large files, use streaming with open():

local file = vol:open("/data/large.csv", "r")
while true do
local chunk = file:read(65536)
if not chunk or #chunk == 0 then break end
process(chunk)
end
file:close()

Write data to a file:

local vol = fs.get("app:data")
-- Overwrite (default)
vol:writefile("/config.json", json.encode(config))
-- Append
vol:writefile("/logs/app.log", message .. "\n", "a")
-- Exclusive write (fails if exists)
local ok, err = vol:writefile("/lock.pid", tostring(pid), "wx")
ModeDescription
"w"Overwrite (default)
"a"Append
"wx"Exclusive write (fails if file exists)

For streaming writes:

local file = vol:open("/output/report.txt", "w")
file:write("Header\n")
file:write("Data: " .. value .. "\n")
file:sync()
file:close()
local vol = fs.get("app:data")
-- Check existence
if vol:exists("/cache/results.json") then
return vol:readfile("/cache/results.json")
end
-- Check if directory
if vol:isdir(path) then
process_directory(path)
end
-- Get file info
local info = vol:stat("/documents/report.pdf")
print(info.size, info.modified, info.type)

Stat fields: name, size, mode, modified, is_dir, type

local vol = fs.get("app:data")
-- Create directory
vol:mkdir("/uploads/" .. user_id)
-- List directory contents
for entry in vol:readdir("/documents") do
print(entry.name, entry.type)
end
-- Remove file or empty directory
vol:remove("/temp/file.txt")

Entry fields: name, type (“file” or “directory”)

When using vol:open() for streaming:

MethodDescription
read(size?)Read bytes (default: 4096)
write(data)Write string data
seek(whence, offset)Set position (“set”, “cur”, “end”)
stat()Get file info (same fields as vol:stat)
sync()Flush to storage
close()Release file handle
scanner(split?)Create line/word scanner

Always call close() when done with a file handle.

For line-by-line processing:

local file = vol:open("/data/users.csv", "r")
local scanner = file:scanner("lines")
scanner:scan() -- skip header
while scanner:scan() do
local line = scanner:text()
process(line)
end
file:close()

Split modes: "lines" (default), "words", "bytes", "runes"

fs.type.FILE -- "file"
fs.type.DIR -- "directory"
fs.seek.SET -- from start
fs.seek.CUR -- from current
fs.seek.END -- from end
MethodReturnsDescription
readfile(path) / read_file(path)string, errorRead entire file
writefile(path, data, mode?) / write_file(path, data, mode?)boolean, errorWrite file
exists(path)boolean, errorCheck if path exists
stat(path)table, errorGet file info
isdir(path)boolean, errorCheck if directory
mkdir(path)boolean, errorCreate directory
remove(path)boolean, errorRemove file/empty dir
readdir(path)iterator, stateList directory (use in generic for loop)
open(path, mode)File, errorOpen file handle
chdir(path)boolean, errorChange working dir
pwd()string, errorGet working dir

Filesystem access is subject to security policy evaluation.

ActionResourceDescription
fs.getVolume IDAcquire filesystem volume
ConditionKindRetryable
Empty patherrors.INVALIDno
Invalid modeerrors.INVALIDno
File is closederrors.INVALIDno
Path not founderrors.NOT_FOUNDno
Path already existserrors.ALREADY_EXISTSno
Permission deniederrors.PERMISSION_DENIEDno

See Error Handling for working with errors.