The database refuses cross-tenant reads. Always.
Tenancy is enforced primarily at the application layer (every Prisma query goes through withSellerScope). Postgres Row-Level Security on seller_id is defense in depth — even if a code path forgets to filter, the database refuses cross-tenant rows.
- Application layer: withSellerScope wraps every Prisma transaction; sets SET LOCAL ROLE robnu_tenant + app.current_seller_id.
- Database layer: Postgres RLS policies on every seller-scoped table filter by seller_id.
- Helper functions: app_current_seller_id() returns the current request's seller; app_is_admin_scope() controls admin-bypass.
What you get.
withSellerScope is the only path
There is no alternative way to read seller data. A new code path that bypasses withSellerScope hits the database under the wrong role and gets zero rows back.
Defense, not redundancy
Even if the application layer fails, the database is the gate. Schema-per-seller would have been impossibly complex at 5K sellers; RLS gets the same isolation guarantee with one connection pool.
Admin bypass is explicit
SuperAdmin reads use a separate role with explicit ImpersonationSession scoping. Every admin read is logged with the session ID, not just the user.
Tables under RLS.
- Order, OrderItem — all order-scoped reads filtered.
- Shipment, ShipmentItem, ShipmentEvent — same.
- OrderReturn, OrderReturnItem, OrderReturnEvent, ReturnAwb — same.
- MarketplacePayout, PayoutLineItem — same.
- ReconciliationBatch, ReconciliationItem, Adjustment — same.
- return_claims, return_claim_events, return_claim_evidences, return_scan_events — same.
- ai_conversations, ai_messages, ai_tool_calls, ai_predictions, ai_document_extractions, ai_usage_ledger — same.
- Catalog tables (CatalogProduct, CatalogListing, ListingAlias, Warehouse, Inventory*) — same.
Practical answers.
Negligible when policies are simple equality filters and seller_id is well-indexed. Robnu seeds seller_id indexes alongside the primary key on every seller-scoped table.
Yes — they're SQL in the prisma migration directory. The full ADR is core/decisions/2026-04-26-postgres-rls-defense-in-depth.md in robnu-docs.
5K migrations and 5K connection pools at 5K sellers. Operationally infeasible. RLS gets the same isolation guarantee with one pool.
Try it inside your own dashboard.
Free during early access. No card. Forever free under 25 orders/day.
