Persistence
A plugin often needs to remember things between requests: a counter, a user’s to-do list, a cached token. You don’t bring your own database for this. The Cat ships two ready key-value stores you reach from the front door, one shared and one per-user:
from cat import store, user
await store.save("theme", {"color": "dark"}) # shared by everyoneawait user.save("todos", [{"text": "buy milk"}]) # private to the callerBoth live in the Cat’s SQLite database (under data/), so a backup of the project folder carries them along.
flowchart TD
P["Your plugin code"] --> S["store<br/>global key-value"]
P --> U["user<br/>per-user key-value"]
S --> DB[("data/ SQLite")]
U --> DB
The global store
Section titled “The global store”store is one key-value space shared across the whole installation. Every agent, endpoint and directive sees the same values. Reach it as the ambient store:
from cat import store
await store.save("theme", {"color": "dark"})theme = await store.load("theme") # {"color": "dark"}, or None if unsetawait store.load("theme", {}) # pass a default for the unset caseawait store.exists("theme") # True / Falseawait store.delete("theme") # True if something was removedsave is a full replacement, not a merge: to change a collection, load it, edit it, save it back.
counts = await store.load("counts", {})counts["hits"] = counts.get("hits", 0) + 1await store.save("counts", counts)The per-user store
Section titled “The per-user store”user is the same key-value API, but every key is scoped to the person behind the current request. Two users never see each other’s data, and you never pass a user id around, the ambient user already knows who is calling:
from cat import user
todos = await user.load("todos", [])todos.append({"text": "buy milk", "done": False})await user.save("todos", todos) # stored under this user onlyawait user.delete("todos")Because it resolves the caller, user is only available where there is a request: inside tools, endpoints, directives and the request hooks. It raises in the bootstrap hooks, which run with no request in flight. See User Management for who that user is.
What you can store
Section titled “What you can store”Any JSON-serializable value: numbers, strings, booleans, lists, dicts. Types round-trip, an int comes back an int, not "9". The two usual JSON coercions apply: tuples come back as lists, and non-string dict keys come back as strings.
await store.save("count", 9)await store.load("count") # 9 (int, not "9")Which persistence do I want?
Section titled “Which persistence do I want?”The two stores are for runtime state your code reads and writes. The Cat has two other persistence flavors for different jobs:
| Need | Use |
|---|---|
| Shared state across all users | store |
| State private to each user | user |
| Configuration an admin edits in the UI | Plugin Settings |