Persistence
A key/value store for data that must outlive a session — player profiles, saved levels, high scores. Available in both roles; the runtime routes to the appropriate backing store. All operations are asynchronous: you pass a callback that the runtime invokes on completion.
Callbacks, briefly
Munos functions aren't first-class, but a builtin's callback slot accepts either an inline function literal or the name of a top-level function. You may declare the callback with any prefix of its full parameter list — trailing parameters you don't need are dropped.
The optional trailing ctx: i32 is an integer you supply at the call site and the runtime passes back to your callback unchanged — a lightweight way to route one shared callback by context (which key, which request).
Reading
db_get(namespace: string, key: string, cb, ctx: i32 = 0)
// cb(success: bool, data: u8[], ctx: i32)db_get("profiles", "player42", got_profile)
function got_profile(success: bool, data: u8[]) {
if !success { return }
// ...decode data...
}Writing and deleting
db_set(namespace: string, key: string, data: u8[], cb?, ctx?)
// cb(success: bool, ctx: i32)
db_del(namespace: string, key: string, cb?, ctx?)
// cb(success: bool, ctx: i32)For db_set and db_del the callback and its trailing ctx are jointly optional — omit both for fire-and-forget:
db_set("profiles", "player42", encoded) // fire-and-forget
db_set("profiles", "player42", encoded, on_saved) // with confirmationListing keys
db_list(namespace: string, prefix: string, cb, ctx: i32 = 0,
start: string = "", limit: i32 = 0, reverse: bool = false)
// cb(success: bool, keys: string[], ctx: i32)Pass an empty prefix to list the whole namespace. Keys come back in ascending byte order — the same order on every backend.
db_list("levels", "world1-", got_levels)
function got_levels(success: bool, keys: string[]) {
for (var i = 0; i < len(keys); i = i + 1) { print(keys[i]) }
}Paging through a large namespace
start, limit, and reverse return an ordered page instead of every matching key at once. The cursor is exclusive: pass the last key you received as the next call's start, and the first page uses start = "".
reverse = false(ascending): keysKwithK > start, up tolimit.reverse = true(descending): keysKwithK < start, up tolimit.start = ""begins at the largest key.limit = 0means unlimited (the whole matching set).
They sit after cb/ctx so the shorter db_list(ns, prefix, cb) call still works unchanged. Name the ones you want if you don't need ctx:
// First page of 50, ascending. Resume by feeding back the last key.
db_list("levels", "world1-", on_page, 0, cursor, 50, false)
// Top 10, highest first — for a leaderboard keyed by a zero-padded score
// so byte order matches numeric order.
db_list("@scores", "", on_top, limit = 10, reverse = true)Encode the order you want into the key (zero-padded numbers, timestamps) and db_list becomes a scalable index you read a page at a time — no need to pull a whole catalog into the script to sort it.
The error event
event db_error(op: string, namespace: string, key: string, msg: string)Fires in addition to the success = false callback path when an operation hit infrastructure trouble (store unreachable, quota overflow). Declaring it is optional — failures are still observable through success = false — but it gives you the failure reason in one place. Available in both roles.
TIP
Data goes in and out as u8[]. Use pack/unpack or base64_decode / string_from_bytes to turn your records into bytes and back.