Perch
Lua APIStorage

Storage

perch.storage: a small key-value store per mod that survives restarts. Always granted.

1 min readUpdated Sep 9, 2026

Each mod owns one JSON document on disk, under %APPDATA%\perch\perch\mod-storage\<id>.json, for state it needs back after a restart: a notepad’s text, a counter, the last known reading. Choices the user should see and change belong in settings instead.

No scope. The whole document is capped at 1 MiB.

perch.storage.get

perch.storage.get(key: string): any

The stored value, decoded (a string, number, boolean or table), or nil when the key was never set.

perch.storage.set

perch.storage.set(key: string, value: any): ()

Writes the file immediately. Tables follow the serialization rules: an empty table is stored as []. If the document would exceed 1 MiB, the key is removed again and nothing is raised.

perch.storage.list

perch.storage.list(): { string }

Every key this mod has stored, in no particular order.

perch.storage.remove

perch.storage.remove(key: string): ()

Deletes one key. Removing a key that was never set does nothing.

perch.storage.clear

perch.storage.clear(): ()

Drops everything this mod stored. It touches only this mod’s document, never the user’s settings.

Example

The notepad: a ui.field whose edits are stored as they arrive and restored at start.

--!strict
type fields_t = { text: field_ref_t<string> }

local notes: string = perch.storage.get("text") or ""

local pad = widget.new { id = "notepad", label = "Notepad", fields = { text = field.string("") } }
pad:place(surface.page, function(f: fields_t): node_t
  return ui.field { value = f.text, multiline = true, placeholder = "Write something down...",
    on_change = function(text: string, ctx: input_context_t)
      notes = text
      perch.storage.set("text", notes)
    end }
end)
pad:register()

perch.on_start:connect(function()
  pad:present({ text = notes })
end)
esc
Type to search
navigate open