Skip to main content

Storage

Use storage APIs to persist data safely.

  • Storage: server-authoritative data.
  • LocalStorage: client-local cache/preferences.
  • default built-in server backend: SQLite at runtime/mtadm.db.
  • account/auth data and Storage data share the same SQLite database file.
  • built-in moderation bans are persisted in this database via the dedicated bans table.

Prerequisite:

  • Storage runtime module must be enabled for Storage.* / LocalStorage.* availability.

Error Handling Pattern​

This API uses the "Error or Nil" pattern.

  • If a function succeeds, err is nil.
  • If a function fails, err is a string code/message.

Function List​

FunctionDescriptionScope
Storage.GetRead one server-authoritative record by bucket/key.S
Storage.SetCreate or replace one server-authoritative record.S
Storage.DeleteDelete one server-authoritative record.S
Storage.UpdateAtomic read-modify-write on one record.S
Storage.QueryQuery records with bounded filters and paging.S
Storage.IncrementAtomic numeric increment/decrement.S
LocalStorage.GetRead one local client value.C
LocalStorage.SetWrite one local client value.C
LocalStorage.DeleteDelete one local client value.C
LocalStorage.ListList local client values in a namespace.C

Static Service: Storage​

Storage is authoritative and server-only.

FunctionDescriptionScope
Storage.GetRead one record by bucket/key.S
Storage.SetCreate or replace one record.S
Storage.DeleteDelete one record by bucket/key.S
Storage.UpdateAtomic read-modify-write with a mutator callback.S
Storage.QueryQuery records with bounded filter and paging options.S
Storage.IncrementAtomic numeric increment/decrement.S

Static Service: LocalStorage​

LocalStorage is client-local and non-authoritative.

FunctionDescriptionScope
LocalStorage.GetRead one local value.C
LocalStorage.SetWrite one local value.C
LocalStorage.DeleteDelete one local value.C
LocalStorage.ListList keys/values in a namespace with optional paging.C

Event List​

EventDescriptionScope
Built-in eventsNone in Storage/LocalStorage v1.S
storage:record_updatedRecommended custom event after write/update/increment.S
storage:record_deletedRecommended custom event after delete.S
storage:revision_conflictRecommended custom event for optimistic concurrency conflicts.S

Scope Model​

  • Storage.* is server-only and authoritative.
  • LocalStorage.* is client-only and non-authoritative.
  • Client-to-server persistence flows should use server APIs (for example Command/Event) that call Storage.* on server.

Node Relationship​

  • Storage is persistence for durable data (accounts, bans, economy, progression, config).
  • Node domains (NodeSim, NodeSharedVisual, NodePlayers, NodeUI) are runtime state graphs.
  • Do not treat node runtime state as durable persistence by default.
  • If a resource needs persistence for node-backed gameplay data, persist resource-owned records in Storage and rebuild node state on load/start.

Events​

Storage and LocalStorage do not expose built-in hook callbacks in v1.

Use custom domain events from your storage workflows.

storage:record_updated (Custom)​

Recommended payload:

  • bucket (string)
  • key (string)
  • revision (number)
  • updatedBy (string): recommended system, player:<playerId>, account:<accountId>, or client:<clientId>

Example:

TriggerEvent("storage:record_updated", {
bucket = bucket,
key = key,
revision = meta and meta.revision,
updatedBy = meta and meta.updatedBy
})

storage:record_deleted (Custom)​

Recommended payload:

  • bucket (string)
  • key (string)
  • updatedBy (string): recommended system, player:<playerId>, account:<accountId>, or client:<clientId>

Example:

TriggerEvent("storage:record_deleted", {
bucket = bucket,
key = key,
updatedBy = "system"
})

storage:revision_conflict (Custom)​

Recommended payload:

  • bucket (string)
  • key (string)
  • expectedRevision (number)
  • actualRevision (number)

Example:

TriggerEvent("storage:revision_conflict", {
bucket = "economy_balances",
key = key,
expectedRevision = expectedRevision,
actualRevision = currentRevision
})

Storage Functions​

Storage.Get​

S Server Only

local value, meta, err = Storage.Get(bucket, key)

Parameters:

  • bucket (string): logical bucket/namespace.
  • key (string): record key.

Returns:

  • value (table | number | string | boolean | nil)
  • meta (table | nil): record metadata.
  • err (nil | string)

Storage.Set​

S Server Only

local meta, err = Storage.Set(bucket, key, value, options)

Parameters:

  • bucket (string)
  • key (string)
  • value (table | number | string | boolean)
  • options (table, optional): optional fields listed below.
  • expectedRevision (number, optional)
  • ttlSeconds (number, optional)
  • auditReason (string, optional)

Returns:

  • meta (table | nil)
  • err (nil | string)

Storage.Delete​

S Server Only

local meta, err = Storage.Delete(bucket, key, options)

Parameters:

  • bucket (string)
  • key (string)
  • options (table, optional): optional fields listed below.
  • expectedRevision (number, optional)
  • auditReason (string, optional)

Returns:

  • meta (table | nil)
  • err (nil | string)

Storage.Update​

S Server Only

local value, meta, err = Storage.Update(bucket, key, mutatorFn, options)

Parameters:

  • bucket (string)
  • key (string)
  • mutatorFn (function): nextValue = mutatorFn(currentValue)
  • options (table, optional): optional fields listed below.
  • expectedRevision (number, optional)
  • ttlSeconds (number, optional)
  • auditReason (string, optional)

Returns:

  • value (table | number | string | boolean | nil)
  • meta (table | nil)
  • err (nil | string)

Storage.Query​

S Server Only

local rows, page, err = Storage.Query(bucket, filter, options)

Parameters:

  • bucket (string)
  • filter (table): constrained filter object.
  • options (table, optional): optional fields listed below.
  • limit (number, optional)
  • cursor (string, optional)
  • orderBy (string, optional)
  • order (asc | desc, optional)

Returns:

  • rows (table | nil): array of { key, value, meta }.
  • page (table | nil): paging info (nextCursor, count).
  • err (nil | string)

Storage.Increment​

S Server Only

local value, meta, err = Storage.Increment(bucket, key, delta, options)

Parameters:

  • bucket (string)
  • key (string)
  • delta (number): positive or negative.
  • options (table, optional): optional fields listed below.
  • expectedRevision (number, optional)
  • auditReason (string, optional)

Returns:

  • value (number | nil): updated numeric value.
  • meta (table | nil)
  • err (nil | string)

LocalStorage Functions​

LocalStorage.Get​

C Client Only

local value, err = LocalStorage.Get(namespace, key)

Parameters:

  • namespace (string): local namespace (for example ui.chat).
  • key (string)

Returns:

  • value (table | number | string | boolean | nil)
  • err (nil | string)

LocalStorage.Set​

C Client Only

local err = LocalStorage.Set(namespace, key, value)

Parameters:

  • namespace (string)
  • key (string)
  • value (table | number | string | boolean)

Returns:

  • err (nil | string)

LocalStorage.Delete​

C Client Only

local err = LocalStorage.Delete(namespace, key)

Parameters:

  • namespace (string)
  • key (string)

Returns:

  • err (nil | string)

LocalStorage.List​

C Client Only

local rows, err = LocalStorage.List(namespace, options)

Parameters:

  • namespace (string)
  • options (table, optional): optional fields listed below.
  • limit (number, optional)
  • cursor (string, optional)

Returns:

  • rows (table | nil): array of { key, value }.
  • err (nil | string)

Metadata Shape​

Storage metadata fields:

  • version (number)
  • revision (number)
  • createdAt (number, ms epoch)
  • updatedAt (number, ms epoch)
  • updatedBy (string)

Security and Trust Rules​

  • Storage is authoritative. ACL enforcement happens server-side.
  • LocalStorage is untrusted. Never use it for bans, permissions, economy, or ownership.
  • Client scripts cannot bypass ACL by writing local data.
  • Use one permission system: ACL (acl.json) for authorization rules; DB stores identity/data only.

Identity Strategy​

Use persistent identity keys for gameplay data:

  • preferred: accountId (logged-in identity)
  • fallback: clientId (when account is not available yet)
  • avoid: session playerId for persistent records

Recommended key prefixes:

  • account.<accountId>.*
  • client.<clientId>.* for low-trust telemetry/preferences only

Suggested Key Design​

  • player.profile:<accountId> (or client:<clientId> for guest-only flows)
  • player.ban:<accountId>
  • player.ban_client:<clientId>
  • chat.mute:<accountId>
  • economy.balance:<accountId>:<currency>
  • resource.<resourceName>:<key>

Error Codes​

  • ERR_INVALID_BUCKET
  • ERR_INVALID_KEY
  • ERR_INVALID_VALUE
  • ERR_NOT_FOUND
  • ERR_REVISION_CONFLICT
  • ERR_PERMISSION_DENIED
  • ERR_QUOTA_EXCEEDED
  • ERR_RATE_LIMIT
  • ERR_SERIALIZATION
  • ERR_STORAGE_UNAVAILABLE
  • ERR_TIMEOUT

Quick Examples​

Server record set/get:

local accountId = "a_abc123"
local clientId = "c_9e2d1a7f"

local meta, err = Storage.Set("player_bans", "player.ban:" .. accountId, {
accountId = accountId,
clientId = clientId,
isActive = true,
reason = "cheat_detected"
}, { auditReason = "manual moderation action" })

if err then
return Logger.Error("ban set failed: " .. err)
end

local value, meta2, getErr = Storage.Get("player_bans", "player.ban:" .. accountId)
if getErr then
return Logger.Error("ban get failed: " .. getErr)
end

Client local preference:

local err = LocalStorage.Set("ui.chat", "activeTab", "global")
if err then
return print("local save failed: " .. err)
end

local tab, getErr = LocalStorage.Get("ui.chat", "activeTab")
if not getErr then
print("active chat tab: " .. tostring(tab))
end

Economy example (server owner custom currency):

local accountId = "a_9e2d1a7f"
local key = "economy.balance:" .. accountId .. ":cash"
local value, meta, err = Storage.Increment("economy_balances", key, 250, {
auditReason = "job payout"
})

Optimistic update with revision check:

local current, meta, getErr = Storage.Get("economy_balances", key)
if not getErr then
local nextValue, nextMeta, updateErr = Storage.Update("economy_balances", key, function(v)
local row = v or { balance = 0 }
row.balance = (row.balance or 0) + 100
return row
end, {
expectedRevision = meta.revision,
auditReason = "daily reward"
})
end

Client local namespace listing:

local rows, err = LocalStorage.List("ui.chat", { limit = 50 })
if not err then
for _, row in ipairs(rows) do
print(row.key .. "=" .. tostring(row.value))
end
end