Row Level Security
What is Row Level Security?
Row Level Security (RLS) is a powerful security layer that controls exactly which records (rows) each user can see, create, update, or delete in your application — and even which fields (columns) of those records they can see. Instead of relying on page rules and component filters alone to hide data, RLS enforces access at the data layer itself, so users only ever receive the records they are permitted to access.
RLS is the most granular and most secure level of access control in Tadabase. Once a record is excluded by RLS, it cannot be exposed by any component, page rule, custom filter, or even an API call — the user simply never sees it.
When to Use RLS
- Multi-tenant apps — each customer/company should only see their own records
- Customer portals — users should only see records that belong to them
- Sensitive data — health, financial, HR, or compliance-controlled records
- Domain APIs — when you publish data through a custom domain and want each token-authenticated caller to receive only their own records
- Field-level secrets — when most of a record is shareable but a few fields (SSN, salary, internal notes) need to be hidden or masked
How RLS Compares to Other Filtering
Tadabase already provides several ways to limit what a user sees. RLS does not replace them — it sits underneath them as the foundational ceiling of what is possible.
| Layer | Where it runs | Can a user bypass it? |
| Row Level Security | Server-side, before any other query runs | No |
| Page Rules / Layout Security | Server-side, when a page loads | No |
| Component Data Source filters (e.g. "is the Logged In User") | Server-side, scoped to one component | No, but only narrows what RLS already returned |
| Front-end Filter Tabs / Add Filters menu | Client-side, on top of returned records | Users can change them — only narrows what RLS already returned |
Think of it like this: RLS decides the universe of records a user is even allowed to know exist. Component filters and front-end filters then trim that universe down for display. They cannot widen it.
Enabling RLS on a Data Table
RLS is configured per data table from the Row Level Security Settings panel on the table's Security tab. To enable it:
- Open the data table in the Data Builder.
- Click the Security tab.
- Toggle Enable row level security to ON.
- Choose a Default Behaviour — App Level, API Level, or Both — to decide where policies apply.
- Optionally select Bypass Roles (users in these roles ignore RLS entirely).
- Click Save Settings, then add one or more policies under Security Policies.
With RLS enabled and no matching policy for a user, that user sees no records on this table. Always add at least one policy so legitimate users can reach their data, or rely on Bypass Roles for trusted users.
Default Behaviour (Scope)
The Default Behaviour controls where the table's RLS policies fire:
- App Level — RLS applies only to the front-end app (the live published app users browse).
- API Level — RLS applies only to requests made through the Domain API (covered later in this article).
- Both — RLS applies to both the app and the Domain API.
Most internal tables only need App Level. Use API Level or Both when external systems or partners are pulling data through a custom domain.
Bypass Roles
Bypass Roles let specific user roles see everything in the table, ignoring all RLS policies. This is typically used for admin or super-user roles. Add roles to this list with care — anyone in a bypass role becomes effectively unrestricted on this table.
Security Policies
A security policy is a rule that says "this group of users may perform these operations on records that match these conditions, may see these fields, and may trigger these workflows." You can define as many policies as you need on a single table, and Tadabase combines them automatically.
Each policy is built with a six-step wizard. The wizard's titles are exactly what you'll see in the builder.
Step 1 — Who can use this access?
Pick which kind of user this policy targets:
| Type | Targets |
| Specific Users | Only users that match one or more user conditions (e.g. Email is the logged-in user, or Department is HR). |
| Any Logged in Users | Every authenticated user, regardless of role. |
| Anyone (Public) | Visitors who are not logged in. Use this for publicly browsable data. |
| Specific Roles | Users assigned to one or more roles you select under Target Roles. |
Step 2 — Which operations are allowed?
Choose which actions the policy covers. You can pick any combination, or choose Full permission to include all four:
- Read — view records that match this policy.
- Create — add new records to this table.
- Update — edit records the user is allowed to access.
- Delete — permanently remove matching records.
Step 3 — Which records can they access?
Choose between two modes with the button group at the top of the step:
- All — the policy covers every row in the table.
- Add Conditions — define filter conditions just like you would on a component data source. Useful operators include:
- is the logged in user — for fields connected to the Users table
- is connected to logged in user — for shared connections (e.g. user and record share the same Company)
- is connected to any of the logged in user's roles — for role-based relationships
- Standard operators: is, is not, contains, is blank, higher than, lower than, etc.
Step 3 also surfaces one-click record-scope shortcuts:
- All Records — every row in the table.
- Allow Only Me — available only on policies attached to the Users data table. Each user can only access their own user record.
- Connected using "<field>" field in the "<table>" table — appears for every field on this table that connects to the Users table. Picking one limits the policy to records connected to the logged-in user through that field.
- Quick condition templates — extra one-click templates such as Matches logged in user, Does not match logged in user, and Matches any logged in user role. Click a template to pre-fill a starter filter, then refine as needed.
Step 4 — Which fields should be shown or hidden?
RLS can also control which fields within a record this user is allowed to see. The button group at the top of step 4 picks the mode:
- All Fields — no field restrictions; every field stays visible.
- Restrict the following — a blacklist. Pick the fields to hide or mask (e.g. SSN, salary). All other fields stay visible.
- Only allow the following — a whitelist. Pick the fields the user is allowed to see; everything else is hidden or masked.
When you choose Restrict the following or Only allow the following, you also pick an Optional display style for affected fields from four cards:
- Starred — the value is replaced with asterisks (e.g.
*******). - Circle — the value is replaced with dots (e.g.
●●●●●). - Blurred — the value is rendered, but visually blurred in the live app.
- Blank — the field is returned with an empty value.
This is great for fields like Social Security Numbers, where you want users to know the field exists without revealing its value. Pick the fields themselves from the Fields chip list directly below the display cards.
Restrict the following is a hard blacklist — selected fields are blocked for matching users regardless of any other matching policy. On forms, blocked fields are shown as read-only or hidden so users can still submit other fields without seeing the protected ones.
Step 5 — Which workflows can be triggered?
RLS can also gate which workflows the matched users are allowed to trigger. The button group at the top of step 5 picks the mode:
- All Workflows — no restriction; the user can trigger every workflow on this table.
- Restrict the following — selected workflows cannot be triggered by users matched by this policy, regardless of any allow policy.
- Only allow the following — only the selected workflows can be triggered; every other workflow is blocked.
Pick the workflows themselves from the Workflows chip list. If the table has no workflows yet, step 5 prompts you to create one first.
Workflow restrictions only control whether the matched users can start a workflow. Once a workflow is running, its actions execute with workflow-level permissions and may legitimately reach records that the triggering user could not read.
Step 6 — Policy Details
Give the policy a Policy Name (required), an optional Description, and a numeric Priority. Step 6 also shows a plain-English summary under Policy Description so you can confirm what the policy will do before saving.
Policy Priority
Each policy has a numeric Priority. Higher priority policies are applied first when multiple policies match the same user. Use priority to handle exceptions — for example, give a baseline policy a low priority and a stricter, more specific policy a higher priority so it wins.
How Policies Combine
When several policies match the same user, Tadabase combines them:
- The row-level conditions add together — a user sees the union of every matching policy's allowed records.
- Field-level Restrict the following selections are a hard blacklist — once any matching policy hides a field, no other matching policy can re-expose it.
- Workflow Restrict the following selections work the same way — once any matching policy blocks a workflow, no other matching policy can unblock it.
- Only allow the following (for fields or workflows) is treated as a whitelist applied on top of the user's combined record-level access.
Common Policy Recipes
Here are the most common patterns you'll build with the wizard. When the table you're securing is the Users table or is connected to it, Step 3 surfaces shortcuts (such as Allow Only Me and Connected using "<field>" field) that pre-fill these conditions for you.
| Goal | Type | Conditions | Operations / Fields |
| Users see only their own records | Any Logged in Users | Owner field is the logged in user | Full permission |
| Everyone in the same company sees the same records | Any Logged in Users | Company is connected to logged in user | Read |
| Anyone (logged-out) can read public listings | Anyone (Public) | Status is Published | Read only |
| Managers can edit, employees can only read | Specific Roles → Managers | All records | Read + Update |
| Hide salary field from non-HR roles | Any Logged in Users (no HR role) | All records | Read with Restrict the following on the Salary field (Starred or Blank) |
| Let only managers trigger the "Approve Order" workflow | Any Logged in Users (no Manager role) | All records | Read with Restrict the following in step 5 selecting the "Approve Order" workflow |
RLS Overview & Preview
Click Row Level Security Overview in the Data Builder menu to open a single screen that lists every table in your app and shows whether RLS is enabled, whether field-level rules are in play, the policy count, and how many of those policies are currently active. From the overview you can:
- Jump into any table's Security tab from the Manage action.
- Use View Permissions As to simulate what a specific user (or an unauthenticated visitor) would see.
- Spot tables that have RLS enabled but no policies (a common cause of "I can't see any records").
Use View Permissions As before going live with a new policy. It runs the same evaluation as the live app and shows you exactly which records and fields the chosen user would receive — without you needing to log out and log back in as them.
The Security tab and the RLS Overview now refresh automatically across open browser tabs. If you change a policy in one tab, every other tab open on the same app picks up the new settings without a manual reload.
RLS Blocked Logs
Every time a user is denied access by an RLS policy, Tadabase records the denial in Settings → Logs → RLS Logs. Each row shows when the denial happened, which table was involved, which record ID (if any) was being requested, which operation (Read / Create / Update / Delete) was blocked, the reason, the user that was acting, and their IP. Use this view when a builder reports "I should be able to see this and I can't" — the log entry will tell you which policy blocked them and why.
RLS and the Domain API (API User Logins)
RLS is not just for the live app — it is also the foundation of Tadabase's Domain API, which lets external systems and partners log in as a real user and pull data from your app over a custom domain. Every record returned through the Domain API is filtered by the same RLS policies you configured for the app.
How It Works
- The custom domain must have Allow API Access enabled in its domain settings (see Using Custom Domains).
- On the Users data table, open the Security API tab and turn on Allow RLS Permission for API Access. This is the master switch for the Domain API.
- Configure Who can use this API? — Specific Users, Any Logged-in Users, Anyone (Public), or Specific Roles. This decides which of your app users are allowed to authenticate against the Domain API at all.
- For each table you want to expose, enable RLS and set its Default Behaviour scope to API Level or Both.
- External callers POST credentials to
https://your-custom-domain.com/loginand receive a Bearer token. Every subsequent request to/dataor/data/{api_slug}must include that token.
API Users Inherit Their RLS Permissions
This is the key point: when an external system logs in as a user via the Domain API, that token represents a real user in your Users table. Every Domain API call is then evaluated against that user's RLS policies — exactly as if the user had logged into the live app and viewed the same data. There is no separate "API permissions" model to maintain. If you change an RLS policy in the Security tab, both app users and API users immediately see the new behavior.
A single set of RLS policies governs both your front-end app and your Domain API. Configure the rules once, and they automatically apply everywhere.
Typical Domain API Call
Step 1 — log in the user and capture the token:
POST https://your-custom-domain.com/login
Content-Type: application/json
{
"username": "partner@example.com",
"password": "their-password"
}
The successful response carries the user's bearer token plus a small profile block:
{
"type": "success",
"token": "<bearer-token>",
"user": { "id": "...", "name": "...", "roles": [...], "profile_image": "..." }
}
Step 2 — call any exposed table with the Bearer token:
GET https://your-custom-domain.com/data/orders?page=1&limit=25
Authorization: Bearer {token-from-login}
Every /data/{api_slug} response uses the same envelope so callers can paginate predictably:
{
"type": "success",
"items": [ ... ],
"page": 1,
"limit": 25,
"total": 137
}
- Pagination defaults —
page=1,limit=25. The maximum allowedlimitis100; larger values are clamped down. - Errors use the matching envelope
{ "type": "error", "msg": "<reason>" }with an HTTP status that reflects the failure (401 not authenticated, 403 not authorized, 404 unknown table, 412 plan blocked, etc.).
The items array only ever contains the records this specific user is allowed to see — anything filtered out by a policy simply will not appear in the result. Hidden or masked fields are also enforced automatically.
Field-level Blurred values are stripped from Domain API responses rather than blurred — there is no UI to render the blur effect on a raw API call. Starred, Circle, and Blank modes still mask the value as configured.
Token Lifetime
Domain API tokens expire automatically once they go idle. By default a token is revoked after 30 minutes with no activity; the next call returns 401 Authentication required and the partner must call /login again to obtain a fresh token. Tokens can also be revoked explicitly by POSTing to /logout with the bearer token.
Domain API vs the REST API (App Key)
Tadabase has two separate API surfaces, and they treat RLS differently:
| API | Authentication | RLS applies? |
| REST API (api.tadabase.io) | App ID + App Key + App Secret (server-to-server) | No — full administrative access by design |
| Domain API (your custom domain) | User Bearer token (issued by /login) |
Yes — every call is filtered by the user's RLS policies |
Use the REST API for trusted server-to-server work where you intentionally want unrestricted access (imports, scheduled jobs, integrations you control). Use the Domain API whenever an external system or partner is acting as a user and should only see what that user is allowed to see.
When RLS Is Bypassed
RLS is intentionally bypassed in a handful of situations so you can still administer your app:
- Builder mode — when you are inside the Tadabase builder editing your app, you see all records (this is so you can configure components and view data).
- Bypass Roles — users assigned to a role on the table's Bypass Roles list see everything.
- REST API — calls authenticated with App ID/Key/Secret are server-side and unrestricted by design.
- Background jobs that run as the system — scheduled tasks and queue-driven automations run with system-level access unless they have been told to act as a specific user.
Everything else — including the live published app, the Domain API, CSV imports started from the builder (which now respect the importing user's RLS policies), and View Permissions As simulations — runs through RLS.
RLS Rollout Checklist
- ☐ Decide whether the table should be App Level, API Level, or Both
- ☐ Add at least one policy so legitimate users can still see records
- ☐ Pick Bypass Roles for admins who need full access
- ☐ Use View Permissions As to simulate each role and at least one real user
- ☐ Confirm that field-level rules in step 4 hide or mask sensitive fields correctly
- ☐ Confirm that step 5 only allows the workflows you want each role to trigger
- ☐ If exposing the table over the Domain API, also enable Allow RLS Permission for API Access on the Users table and configure Who can use this API?
- ☐ Test from a fresh browser session as a non-bypass user before announcing the change
- ☐ Check Settings → Logs → RLS Logs after the first day of real usage to catch any unintended denials
We'd love to hear your feedback.