Policies
Policies define what tables a developer can access and what operations they can perform. DAAM uses a default-deny model — tables not mentioned in any policy are inaccessible. Policies belong to databases and are assigned to users and groups. The control plane is the source of truth; agents enforce policies locally.
Overview
Policies are database-centric. Each policy belongs to a specific database and defines two things:
- Table grants — which tables the user can access and what operations (SELECT, INSERT, UPDATE, DELETE) are allowed on each.
- Masking rules — which columns have their values masked on read, and what masking preset is applied (e.g. email, phone, redact).
Users and groups are then assigned to policies. A single policy can be assigned to any number of users and groups. A user or group can be assigned to multiple policies on the same database — the grants and masking rules from all applicable policies are merged into a single effective policy.
If a user has no policy assignments on a database, they have no access to it at all. There is no implicit read access — every permission must be explicitly granted.
Creating a Policy
Admins create policies from the database detail page in the console:
- Navigate to the database you want to create a policy for.
- Click Create Policy.
- Enter a policy name (e.g. "read-only", "analyst", "full-access"). Names must be unique within the database.
- Add table grants — select tables and choose which operations to allow.
- Add masking rules — select columns and choose a masking preset.
- Save the policy.
A newly created policy has no assignments. It serves as a template ready to be assigned to users or groups.
The table and column dropdowns in the policy editor are populated from the agent's schema introspection. The agent automatically discovers your database's tables and columns and reports them to the control plane.
analyst · prod-users (us-east-1)
Version 3 · Updated2026-03-10 14:32:00 UTC
Assignments (3)
| Type | Name | Assigned | |
|---|---|---|---|
| User | [email protected] | 2026-02-20 11:00:00 UTC | |
| User | [email protected] | 2026-02-22 15:30:00 UTC | |
| Group | Data Team | 2026-03-01 09:00:00 UTC |
The preview above includes row filters on the users and orders tables. Row filters restrict which rows a user can see based on column conditions and user variables. For details on operators, user variables, and how filters combine across policies, see the Row Filters documentation
Table Grants
Each table grant specifies a schema, table, and one or more permissions. The preview above shows a live policy editor with sample data — try the preset buttons to see how permissions change across all tables.
| Permission | Description |
|---|---|
SELECT | Read rows from the table |
INSERT | Create new rows |
UPDATE | Modify existing rows |
DELETE | Remove rows |
DDL operations (CREATE, ALTER, DROP), TRUNCATE, and superuser access are never granted. These operations are blocked regardless of policy configuration.
Permission Presets
The policy editor offers presets for common permission patterns:
| Preset | Grants |
|---|---|
| Read Only | SELECT |
| Append Only | SELECT, INSERT |
| Read/Write | SELECT, INSERT, UPDATE, DELETE |
Presets are a UI convenience — they expand to individual table grants. You can customize the permissions on any table after applying a preset.
Masking Rules
Masking rules define how sensitive column values are transformed when read via SELECT. Masking only affects read results — writes to masked columns are unaffected. Each rule specifies a column match pattern and a masking preset.
Available Presets
| Preset | Example Output |
|---|---|
email | j***@e***.com |
phone | ***-***-1234 |
ssn | ***-**-6789 |
credit_card | ****-****-****-1111 |
name | A*** J*** |
redact | [REDACTED] |
null | NULL |
Match Patterns
Masking rules use three-part match patterns: schema.table.column. You can use wildcards to apply rules broadly:
public.users.email— masks the email column on public.userspublic.users.*— masks all columns on public.users*.*.email— masks any column named email across all schemas and tables
Masking and grants are orthogonal. A user with SELECT on a table sees masked values for any masked columns. A user without SELECT cannot query the table at all. Writes to masked columns are allowed — masking only transforms read results.
Assigning Users and Groups
After creating a policy, assign users or groups to it. Assigned users receive the policy's grants and masking rules on the database.
Direct User Assignment
On the policy detail page, type at least 2 characters into the search box to search org members by email. Select a user from the results and save the assignment. The user's effective policy is recomputed and pushed to the agent immediately.
Group Assignment
You can also assign groups to policies. All members of the group (including members of nested child groups) automatically receive the policy's grants. This is the recommended approach for teams — when someone joins or leaves a group, their database access updates automatically.
The search box supports both user emails and group names. Only users and groups belonging to the current organization appear in results.
A user assigned to the same policy both directly and via a group does not receive double grants. The policy's grants and masking rules are included once in the effective policy regardless of how many assignment paths exist.
Effective Policy Resolution
A user's effective policy on a database is the merged result of all policies they can reach through direct assignments and group memberships. The agent never sees individual policies — it receives a single flat effective policy per user.
How Resolution Works
Your effective access is the combination of all policies assigned to you — directly and through groups. Grants from all applicable policies are merged: if any policy grants a permission, you have it. When multiple policies define masking for the same column, the strictest rule wins.
Grant Merging (Union)
All table grants from all applicable policies are combined. If any policy grants a permission on a table, the effective policy includes that grant. For example:
| Source | Table | Grants |
|---|---|---|
| "read-only" (direct) | public.orders | SELECT |
| "read-write" (via group) | public.orders | SELECT, INSERT |
| "read-write" (via group) | public.products | SELECT |
Effective result: public.orders gets SELECT and INSERT (union), public.products gets SELECT.
Masking Merging (Most Restrictive Wins)
When multiple policies define masking rules for the same column, the most restrictive preset wins. For example, if one policy shows a column unmasked and another redacts it, the column is redacted. The null and redact presets are considered the most restrictive since they reveal the least information.
If a masking rule exists in one policy but not another, the rule is included in the effective policy. The absence of a masking rule in one policy does not cancel a rule from another.
Example: one policy masks public.customers.email with the "email" preset and another masks it with "redact". The effective policy uses "redact" because it is more restrictive (rank 2 vs rank 4).
Live Enforcement
Policy changes take effect immediately. When you edit a policy, assign a user, or change group memberships, the control plane automatically:
- Recomputes the effective policy for all affected users on the database.
- If the effective policy changed, pushes the updated version to the agent.
- The agent applies permission changes to active database sessions in real time.
If a user loses all access (e.g. their only policy is deleted or they are removed from a group), the agent terminates their active connections.
What Triggers Recomputation
| Event | Recomputed For |
|---|---|
| Policy created, updated, or deleted | Every user assigned to that policy (directly or via group) |
| User assigned to or unassigned from a policy | That user on the policy's database |
| Group assigned to or unassigned from a policy | Every member of that group on the policy's database |
| User added to or removed from a group | That user on every database where the group has a policy |
| Access request approved, activated, expired, or revoked | The requester on the target database |
The effective policy version changes only when the resolved grants or masking rules actually differ, even if a source policy was edited without changing the effective outcome.
Schema Introspection
The policy editor's table and column dropdowns are populated from the agent's automatic schema discovery. When an agent connects to its upstream database, it introspects the schema and reports the available tables and columns to the control plane.
This means the policy editor always reflects your actual database schema. If you add a new table to your database, the agent detects it and makes it available for policy configuration without any manual steps.
You can trigger a schema refresh from the database detail page in the console if you need the agent to re-introspect immediately.
Deleting a Policy
When you delete a policy, all its assignments, table grants, and masking rules are removed. The control plane then:
- Recomputes the effective policy for all users who were assigned (directly or via groups).
- For users who still have access through other policies: pushes the updated effective policy.
- For users who no longer have any access: sends a policy removal to the agent, which terminates their active connections.
Deleting a policy is immediate and cannot be undone. If you want to temporarily disable access, consider removing user assignments instead of deleting the policy.
Validation
Policies are validated on save. Policy names must be unique within the database, at least one table grant is required, and masking rule patterns must follow the schema.table.column format. The console displays validation errors inline when saving.