Access control

Access control

Policies, roles, and column-level redaction.

Access control here is one mechanism used everywhere: a principal holds roles, roles carry policies, and every request is checked against them. The governance model explains the shape; this page is how you work with it.

Policies

A policy grants one action on one resource, keyed resource:action:

tickets:read_list
tickets:create
orders:delete
sdk:query

Table policies appear automatically as tables are created. Custom actions carry their own keys, which is what lets you grant orders:refund without granting orders:update.

An app's policies, each listing how many actions it grants and whether it is active
A policy is a named set of grants. What a principal can do is the union of the policies on its roles — nothing more.

Roles

Bind policies to a role, and roles to principals. Principals never hold policies directly, so "what can this person do" is answered by looking in one place.

Roles are per-app. Members of your organization, your app's end users, and agents are all assigned roles the same way.

An app's roles listed with the policies bound to each
Roles are the only thing principals hold, so revoking a policy from a role revokes it everywhere at once.

Agents are not special

An agent's role is an ordinary role. Its tools are derived from the policies on that role, so granting an agent an ability is the same act as granting a person one — and revoking it takes effect everywhere at once.

Column redaction

Some columns should be readable by the system but never returned to a caller — a hashed secret, a token, a bank detail. Redaction masks them on read, per column, configured on the table.

Mark a column sensitive when you create or edit the table and it is added to the table's redaction list. Columns whose names look like credentials are added automatically, so the safe behaviour is the default — you opt a column out, not in.

It is enforced at both walls: the handler SDK and the REST layer. A redacted column is masked whether it is read inside a handler, listed over CRUD, or echoed back in an update response.

GET /{appUid}/autocrud/vendors
{
  "vendor_code": "MER-114",
  "name": "Meridian Surgical Supply",
  "bank_account_number": "[redacted]",
  "bank_routing_number": "[redacted]"
}

The row still comes back. Only the redacted columns are replaced, so a caller that needs the record does not have to be trusted with the parts of it that it does not need.

This holds on every read surface, including the console's own table editor — an operator with a table open on a shared screen is not exposing what the API refuses to return. You can still replace a redacted value without seeing it: clear the field and type the new one.

Bank account and routing columns in the console's data explorer, every value masked as [redacted]
The same masking in the console's own data explorer. The values are withheld by the server, not hidden by the page, so they are absent from the response and from anything exported out of the grid.

Redaction is read-side masking

It hides values on the way out. It does not prevent writes and it is not encryption. Use it to stop a secret being displayed, not to stop it being changed.

Protected tables

Tables can be marked protected, and individual columns within them, so structural changes are refused rather than merely discouraged. This is what stops a caller with schema access from dropping the table that stores its own permissions.

Next

On this page