Corbie

Plugins

fs

This plugin reads and writes files and directories.

  fs.read(path, { as })              the file's contents
  fs.write(path, { text | bytes | json | blob | secret, append, mode })
                                     writes the file; returns the number of bytes written
  fs.list(path, { deep })            the entries, by name: [{ name, kind, size, modified, mode }]
  fs.stat(path)                      one entry
  fs.exists(path)                    true if the path exists
  fs.remove(path, { deep })          removes a file, a link or an empty directory; returns nothing
  fs.mkdir(path)                     makes the directory and those above it; returns nothing
  fs.move(path, { to })              moves or renames a file, a link or a directory; returns nothing

A path with no leading / is in the job's scratch directory (Scripts).

Options:

as"text", "bytes", "json" or "blob"; default "text" secreta handle to a secret (secrets); the file's mode is then 0600 appendadds to the end of the file; default false modethe mode of a new file; default 0o644 deeplists the directories below too, or removes a directory with its contents; default false

An entry's fields:

kind"file", "directory" or "link" modifieda time in UTC, as "2026-10-13T20:02:07Z" modethe permission bits, as a number

A file read or written as a blob may be any size (blobs).

Errors:

not_foundthe path does not exist not_permittedthe operating system refuses the runner bad_inputa file over 64 MiB read in a form other than "blob"; a file read as "json" that is not JSON failedanything else, such as removing a directory that is not empty

Needs these permissions, at the path's real location after links are followed:

fs.read:pathread, list, stat, exists fs.write:pathwrite, mkdir, remove, and move for both paths secret:NAME@fs.write:patha secret written to the file
  • fs.remove and fs.move act on the name: a link is removed or moved, not what it points to.
  • A directory removed or moved needs fs.write:path for every entry inside, at the old path and the new. If one is refused, nothing is removed or moved.
  • fs.list omits the entries that the job may not read.

This script writes a blob to a file and reads the file as a blob.

allow: [fs.write:in.csv, fs.read:in.csv]

const table = blobs.make("item,amount\ntea,12\n", { type: "text/csv" })
fs.write("in.csv", { blob: table })                     // gives 19
return fs.read("in.csv", { as: "blob" }).size           // gives 19

This script makes the other calls.

allow: [fs.read:/srv/reports/**, fs.read:out/**, fs.write:out/**]

const text = fs.read("/srv/reports/latest.md")
fs.write("out/summary.txt", { text: text.slice(0, 200) })    // gives 52
fs.exists("out/summary.txt")                                 // gives true
fs.stat("out/summary.txt").size                              // gives 52
fs.mkdir("out/2026")
fs.move("out/summary.txt", { to: "out/2026/q3.txt" })
fs.list("out", { deep: true }).map((entry) => entry.name)    // gives ["2026", "2026/q3.txt"]
fs.remove("out/2026", { deep: true })

This script writes a secret to a file.

allow: [fs.write:/srv/app/key, "secret:APP_KEY@fs.write:/srv/app/key"]

fs.write("/srv/app/key", { secret: secrets.get("APP_KEY") })