Preference
Included from Basic tier
A preference is one per-user named preference document: GUI state — a dashboard layout, chosen filters, a refresh
cadence — stored server-side so it follows the user across browsers,
machines and reinstalls instead of living in per-browser localStorage. The
document body is opaque JSON: the server validates that it parses as JSON
and enforces the bounds below, nothing more.
The surface is API v2 only (PreferencesService — see
opschain docs api-v2), and strictly scoped to the authenticated
caller's own rows: no request names a user, and no caller — superuser
included — can read or write another user's documents. Different users may
hold same-named documents; each name resolves to the caller's own row.
PostgreSQL-backed: under the embedded SQLite store every call answers the
typed ERROR_REASON_STORE_POSTGRES_REQUIRED, so GUI clients keep a
localStorage fallback.
Writes are compare-and-set: PutPreference with expected_version 0 (or
absent) creates the document at version 1, a positive expected_version
replaces exactly that stored version (incrementing it), and anything else
answers the typed ERROR_REASON_VERSION_MISMATCH with the current and
supplied versions. Successful writes record api:preferences:create /
api:preferences:update audit events carrying the document's name and the
supplied expected_version only — the body never reaches the event lane.
Bounds
| Bound | Value | Breach |
|---|---|---|
| Document size | 64 KiB of JSON text | invalid_argument + ERROR_REASON_PREFERENCE_TOO_LARGE (metadata: limit_bytes, size_bytes) |
| Documents per user | 100 names | failed_precondition + ERROR_REASON_PREFERENCE_LIMIT_EXCEEDED (metadata: limit); overwriting an existing name always stays possible |
| Name length | 255 characters | invalid_argument field violation |
Fields
| Field | Type | Meaning |
|---|---|---|
name | string | The document's name, unique per user (e.g. dashboard). |
document_json | string | The stored document, JSON text. Never present on listing rows (ListPreferences serves PreferenceInfo: name, version, updated_at only). |
version | int | The compare-and-set counter, 1 on create, +1 per successful put. |
created_at | timestamp | When the document was created. |
updated_at | timestamp | When the document was last written. |
There is no delete yet: the v2 write contract carries no delete verbs, and a
preferences delete lands with that v2-wide decision rather than ahead of it.
Storage is user_preference_documents (migration 00042), one row per
(user_id, name) cascading with the owning user; the adopted v1
user_preferences ui_support table is a different, unversioned document and
is unchanged by this surface (its GET/PATCH /api/ui_support/preferences
routes are deprecated in the v1 OpenAPI document, no
caller — but keep serving).