Row-level security
Every user sees exactly their own data, enforced by Postgres itself.
You describe the kinds of users and what each may read, create, update and delete. Appbricx turns that into Postgres row-level security, checks every write against your business rules, and proves it for every role before a release goes out. You write no policies.
| role | member | booking | session | instructor |
|---|---|---|---|---|
| member | own | own | all | none |
| instructor | all | all | all | own |
| signed out | none | none | all | all |
- Permissions match the plan for 2 roles × 4 tables
- Signed-out visitors can't read member, booking
- Per role, per table
- Read, create, update, delete
- Own, all or none
- Ownership follows references
- Business rules
- Server checks before every write
- Proven
- A role by table matrix in every verify
How access is decided
Name the personas (member, instructor, front desk). From the personas and pages, Appbricx derives sensible defaults for every role on every table.
Override where you need to: read, update and delete as own, all or none, create as true or false. owner_refs says which references make a row someone's own.
Add business rules for what may be in a row: a check that runs on the server before each create or update and refuses with your message.
{ "name": "booking",
"owner_refs": ["member_id"],
"actor_ref": "member_id",
"access": { "member": { "read": "own", "create": true,
"update": "own", "delete": "none" } },
"rules": [{ "id": "seats-left", "on": "create",
"message": "That class is full. Try another time.",
"check": "/* one read-only SELECT, run as the writer */" }]
}
// what verify reports
✓ Permissions match the plan for 2 roles × 4 tables
✓ Signed-out visitors can't read the 2 private tables
Access control without writing policies
Postgres enforces it
Each role's permissions become row-level security policies, so a bug in a screen or a script can't read another user's rows.
Ownership that follows refs
A booking is a member's own when member_id points at their row. Ownership is transitive: a message is yours when its match is.
Nobody writes as someone else
actor_ref fills the owner field with the writer's own row on every write, whatever the screen sends.
Business rules
Up to six checks per table run before each write. They fail closed: a rule that throws or times out refuses the write.
Signed-out visitors
Verify lists which tables anonymous visitors can read, so a table meant to be private never ships public by accident.
Teams and invites
A staff role can invite people by email with a role, and you choose whether sign-up is open, invite-only or closed.
Ledgerly keeps every member's money private
Sign in to Ledgerly as Maya, then as Daniel: none of Maya's accounts, budgets or transactions are visible to him. Three server rules also refuse a zero budget, a bill on day 32 and a transaction dated in the future.

Works with
Row-level security: common questions
Do I need to write row-level security policies?
No. You say what each kind of user may do with each table, and Appbricx creates the Postgres row-level security for you. Verify then tests every role against every table.
What is the difference between access and a business rule?
Access decides who may touch which rows. A business rule decides what may be in them, such as refusing a booking for a full class. Workflows handle what should happen after a write.
Can a script get around the rules?
No. Rules and access are enforced by the data API and Postgres, so they apply to every caller: screens, scripts and outside tools alike.
What does a user see when a rule refuses a write?
The request returns HTTP 422 with your message, and app.create in a screen rejects with it so you can show it.