Bootcamp
Bubble privacy rules, explained properly
Photo by Ibrahim Abazid on Unsplash
Constraining a search on the page feels like security. You write Do a search for Contacts where account = Current User's account, the list shows only the right records, and everything looks locked down. But that constraint lives in the front end: the database behind it will still hand over everything it holds, and a visitor with a little web development knowledge can ask it directly.
That is the gap privacy rules close. Without them on a data type, that data is effectively accessible to the public, even if reaching it isn’t trivial.
Privacy rules are conditions that operate at the database level, on the back end, controlling which records, fields and files each user can find, view or modify. They apply to every request your app makes, regardless of what any page shows.
Back end and front end: use both
Privacy rules control access where the data lives; search constraints control what a page requests. Strictly, the back-end rule does the protecting, but we suggest putting the condition in both places: the front-end constraint is an insurance policy in case someone changes a rule later and quietly widens access. A typical pairing: the privacy rule grants visibility when This Contact's deleted is no and the current user is logged in, and the page’s search adds the constraint deleted = no. Same condition, both layers. Verify what actually reaches the browser with the network Inspect technique: read the JSON coming back from each search.
Three levels of control: table, column, row
Privacy rules give you three grains of control, and most real apps use a hybrid of all three.
- Table (data type) level. The whole data type is visible or it isn’t: logged-in users can view everything, everyone else gets nothing.
- Column (field) level. Extend that rule so everyone else can view just the
namefield. The public now sees names and nothing more. - Row level. Compare a field on the record to the current user. In an app where companies see only their own contacts, the rule is: the current user’s company is this contact’s company. Rows that match are visible; rows that don’t may as well not exist.
One behaviour to burn into memory: any rule that grants a permission wins. If one rule gives a user access, they have it, even if every other rule withholds it. When something is unexpectedly visible, work through all the rules on that data type and find the one doing the granting.
Every expression has exactly two perspectives
Privacy rule conditions can only be written from two viewpoints:
- The data type itself — the record the rule is written on and its fields:
This Deal's Contact,This Contact's deleted is no,This Deal's Assignees contains Current User. - The current user — the person using the app:
Current User is logged in,Current User's Active is yes,Current User's Role is Admin.
Every comparison has to work between these two, or within one of them. There is limited ability to reach through to a related table (something like the current user’s company’s contract), but some contexts restrict it, particularly searches. There’s no way around this constraint, and it has a direct implication for data modelling: each data type, and the user, must carry the fields your rules need to compare. If a deal should be visible only to its account, the deal needs an account field.
Expressions don’t have to be short, either. This Deal's account is Current User's account and This Deal's deleted is no and Current User is logged in is one perfectly good compound rule spanning both perspectives.
The four permissions, and why they’re independent
Each rule grants a set of permissions, and each is exclusive: granting one never implies another.
- View fields (all fields, specific fields, or none). Column-based security. Crucially, it does not grant search: it grants the ability to see a record’s fields when you can reach the record, for instance if you already have its ID.
- Find this in searches. Whether this user’s searches on the data type return results at all. On its own it’s oddly hollow: a repeating group shows the right number of rows, a count is correct, but every field comes back blank.
- View attached files. Governs files uploaded in private mode and attached to a row of data; public files are always visible. With permission, Bubble serves the file’s URL with an access token allowing temporary access; without it, the link leads to an unavailable screen.
- Auto-binding. Whether inputs bound directly to a field can write to it without a workflow action, enabled per field.
The debugger is the best tool for seeing all this: inspect an element, click into its dynamic expression, and it will show which fields are restricted by privacy rules and how many search results came back.
Patterns worth knowing
Single-tenant. All data visible to all users, as in an internal tool for your own business. One rule does it: when Current User is logged in, grant everything.
Multi-tenant SaaS. Add an account layer: every data type in the app carries an account field (projects, contacts, deals, tasks) and so does the user, and the rule on each type is: when Current User's account is This Project's account, grant access to the row. That’s the fundamental SaaS pattern: every record knows its account, every user knows theirs, and the rule checks they match.
User roles. Create a user-role option set (manager, owner, team member, customer), add a Role field of that type to the user, set it at creation, then write rules per role: a manager views all fields, finds in searches and auto-binds; a team member views only some fields. Combine roles with accounts and you can build very sophisticated permissions.
Two-sided marketplace. More dynamic: one side creates data the other consumes. An organisation posts a project and can view all its fields, find it in searches, view attached files and auto-bind; a freelancer can find the project but view only some fields. Then add a Successful Freelancer field on the project: when a freelancer wins the job, save their user there, and a rule saying this project’s successful freelancer is the current user unlocks more access than applicants get. Users move through states, and permissions follow. Every distinct permission is one you must test, so think carefully before going heavily dynamic.
Soft delete. Instead of the Delete workflow action, add an is deleted yes/no field defaulting to no, and on “delete” run Make changes to a thing, setting it to yes. The privacy rule grants access only when deleted is no: you keep an audit of deleted records, and users never see them.
Impersonation. Give the user a Currently Impersonating User field of type User, intended for temporary use. When an admin picks someone to impersonate, set the field, and write rules like: when the current user’s currently-impersonating-user is not empty and this project’s owner is that impersonated user, grant access. The admin sees the app from that user’s perspective until the field is cleared. The same “current thing” trick (a current project, a current order) can grant permissions for a time and be cleared when finished.
The logged-out user. Always decide explicitly what logged-out visitors can see. Sometimes they legitimately need limited data: responding to an invite, onboarding before signup, ordering food before creating an account, or public profiles exposed for search engines. Show some data, not everything; where something sensitive is involved, back-end workflows can bridge the gap, because the system running them isn’t a user and can ignore privacy rules.
Document the intention, not every rule
Don’t write essays about every role’s access to every data type. Put data types down the side of a table and roles along the top, including logged out, and write one short sentence per cell describing the intended permission: the developer sees everything, the app admin everything not soft-deleted, the manager some things, the team member less, the logged-out user nothing. Then check the actual privacy rules against the table and confirm they’re fit for purpose.
Worked examples: watch the rules bite
The best way to understand privacy rules is a deliberately tiny sandbox. Build this in a scratch app and test each pattern.
- Create a page called
privacy rule testingand a data type calledFruitwith text fieldsname,sortanddescription. In App data, add three rows: apple / 1 / red, orange / 2 / tasty, banana / 3 / delicious. - Add a repeating group with type of content Fruit and data source
Do a search for Fruits, showing all three fields per cell, plus a text showingCurrent User's emailandCurrent User is logged inso you always know who you are. - Preview logged out. The fruit still shows, which tells you the table is publicly visible. Fine for a blog or FAQ; a problem for anything else.
- Add a rule: when
Current User is logged in, grant everything; disable every permission for Everyone else. Reload logged out: the table is empty, with no front-end constraints involved, and the network inspector shows the search returning nothing. Log in (App data, pick a user, Run as) and the fruit returns. - Column-level. Give Everyone else “view specific fields: name” only. Logged out, you see names alone, and selecting the repeating group warns of hidden fields because of privacy rules; the network response carries only the name and the record’s ID. Worth knowing for efficiency too:
Do a search forhas no way to say “only send me the name field”, so privacy rules are the mechanism that limits what’s transmitted. - Row-level. Add a
Climatedata type (tropical, cool) and climate fields on Fruit (banana tropical; apple and orange cool) and on User. Replace the rule with: whenThis Fruit's Climate is Current User's Climate. A cool-climate user sees apple and orange; switch them to tropical, reload, and only banana remains. Reloading matters: once data reaches the front end, it stays until the page refreshes. The debugger confirms one result is returned, not three filtered to one. - Roles. Create a user-role option set with Farmer and Customer and a role field on the user. Farmers get everything; customers view name only, but can still find in searches. Flip your role in App data and reload each time to see both sides.
- Layering. Add a third rule: when
Current User is logged in, view description. A logged-in customer now sees name and description, but still not sort, because permissions are a union: anything any rule grants, the user gets. - Soft delete. Add a
deletedyes/no field and the rule: whenThis Fruit's deleted is no, grant access. Mark apple deleted in App data and it vanishes on reload; flip the flag back and it returns. No Delete action involved. - Permission combinations. Grant view-fields without search: the repeating group is empty, because no search can return results. Flip it to search without view-fields: the rows load and the count is right, but every field is blank; you have IDs and a count, nothing else.
- Private files. Add a
filefield to Fruit (it stores the file’s location). Add a file uploader with “Make this file private” checked, attached toCurrent cell's Fruit, and a workflow: when the uploader’s value is changed, make changes to the current cell’s Fruit, setting file to the uploader’s value. You need both halves: the attachment stamps the record’s ID onto the file so privacy rules can govern it, while the saved URL tells your app where the file lives. Add a download icon that opensCurrent cell's Fruit's fileas an external URL, greyed out when the field is empty. - Test the negative first. Set the rule so only logged-in users can view attached files. Logged out, the download hits “permission denied / unauthorized”. Always test the case that should fail before the one that should succeed: test the happy path first and your browser may cache the signed link and token, making revoked permission appear to still work. Then log in and download: the URL carries an access key, expiry and signature. Paste it into an incognito window and it’s still denied; possession of the link is not possession of access.
- Auto-binding. Bind an input to the fruit’s name and grant auto-binding on name to logged-in users. Logged out, typing triggers “you do not have permission to modify this” and the value reverts. Logged in, the edit saves instantly.
When a rule misbehaves in a real project, do the same: rebuild the scenario in its most basic form in a test app, find the rule granting or blocking, and carry the lesson back.
This skill in the AI era
Privacy rules are Bubble’s version of an idea that now runs the industry: enforce access in the data layer, not the interface. The same thinking reappears as row-level security policies and API middleware in the stacks AI tools generate, and it’s the first thing we audit when a project arrives in trouble, because generated code is reliably good at features and reliably quiet about authorisation. A builder who can write “who can find, view and modify what” as precise rules can direct Claude Code to enforce it properly from day one. That’s the pattern behind what transfers from Bubble to AI-assisted development, and behind most of our Bubble rescue and migration work.
If this sparked something, let's talk.
No pitch, no pressure — just a conversation about what you're working on.
Let's talkRelated posts
Bootcamp
A simple approach to data modelling
A four-step method for designing a database schema: identify entities, map relationships, choose fields. Taught through the CRM data model built in Bubble.
Bootcamp
Module 5 — Application architecture: pages, states and reusable pieces
Single-page vs multi-page apps, URL parameters, custom states, reusable elements — and a big practical building a main template with an expanding navigation drawer.
Bootcamp
API calls as actions vs data (and the planning checklist)
When to use Bubble API calls as actions versus data sources, a one-to-many auth pattern, and the planning checklist that keeps integrations from failing silently.