Guides · Concepts
Access presets
Every table carries one preset that says who may list, read, create, update and delete its rows. Presets replace row-level security policies: there are eight of them, they cannot be misconfigured, and the API applies them to every request.
The eight presets#
| Preset (wire id) | Dashboard label | In one sentence | List / read | Create | Update | Delete |
|---|---|---|---|---|---|---|
prive (default) | private | Each user only sees their own rows. | owner | users | owner | owner |
partage | shared | Everyone reads everything; each user edits only their own rows. | users | users | owner | owner |
collaboratif | collaborative | Everyone reads and edits everything. | users | users | users | users |
espaces | spaces | Rows belong to a space; only its members can access them. | space | space | space | space |
espace_modere | moderated spaces | Members read and create; only the author or the space admin can edit. | space | space | owner or space admin | owner or space admin |
lecture_publique | public read | Readable without an account. | anon | users | owner | owner |
journal | journal | Append-only — no end user can edit or delete, ever. Your sk_ / ak_ keys still can. | owner | users | nobody | nobody |
formulaire | form | Anyone can submit; submissions are readable only with your sk_ / ak_ key. | nobody | anon (rate-limited) | nobody | nobody |
The cells:
| Value | Who satisfies it | What it does |
|---|---|---|
owner | the author of the row | filters — owner_id = me |
users | anyone signed in to the app | imposes nothing beyond being signed in |
anon | everyone, session or not | imposes nothing |
space | the members of the row's space | filters — space_id in my spaces |
space admin | the admin of the row's space | filters, restricted to the role |
nobody | no end user, ever | refuses |
A nobody on list or read answers 401 to an anonymous caller and the neutral 404 to a signed-in one; on update or delete, a row the caller can see answers 403.
Rules that apply to every preset#
- Refusal by default. Anything not explicitly allowed is refused. A table with no declared preset is
prive. owner_idis set by the API, never read from the client. A body carryingowner_idoridis refused.- Spaces membership is checked before any query; outside the space,
404. - An anonymous caller on a non-public preset gets
401with the hint "session required". - Your own keys ignore the matrix. An
sk_(your server) and anak_(your agent) read and write everything, on every preset. Rows they create haveowner_id: nulland are invisible to end users onpriveandjournal. - Files follow the same matrix: a
privatefile behaves like a row onprive, apublicfile like a row onpartage. journalis append-only for the users of the app, not for you. No end user can rewrite the past, not even a row's author; your keys are not bound by the matrix. It is not a tamper-proof ledger.formulaireis a mailbox. Anyone deposits, nobody reads from the app; submissions are read and deleted with your keys — from your server or the dashboard. It is the one preset with server-side abuse limits: 10 creations per minute per IP (429) and 10 000 rows per table (402), for anonymous and signed-in callers alike.
Changing a preset#
The preset is the only thing standing between your users' data and each other, so changing it is the most consequential change in the product. It always confirms in two steps: the first call answers 428 with a summary — who gains or loses reading, who gains or loses writing, how many rows become invisible — and a token valid five minutes; the second call, with the token, applies. In the dashboard this is the Definition tab of a table; from an agent, the set_table_preset tool.
Three more rules:
- Opening a table that already holds rows to the public requires a signed-in human. When the change opens reading to
users(everyone signed in) oranon(everyone) and the table is not empty, an agent is refused with401and no token: the rows were deposited under a promise, and only a human can revoke it, from the dashboard. An empty table needs no human — there is nothing to expose. Closing a preset, moving to a spaces preset, or widening writes never need a human. - Switching a non-empty table to
espacesorespace_modereis refused (409): existing rows have no space. Create the table on a spaces preset from the start, or empty it first. Switching between the two spaces presets always passes; switching out of one resets the rows'space_id. - A new table can take any preset: the
create_tabletool declares the preset at creation and confirms the switch itself, because a table that was just created is empty.
Every preset change is written in the request log at level warn.
A limit is not a rule#
The publishable key is public: anyone can send any request with it, ignoring your interface. A "one check-in per day" enforced by your front end is a display convention, not a rule. Server-side rules belong in an edge function; the only preset with a server-side limit of its own is formulaire.
