Scrydon
Analytics

Classification & masking

Per-column data classification, mask strategies, row filters, and the Rego-backed policy engine that enforces them.

Every managed table carries a data classification at the table level and at the column level. Combined with mask strategies and row filters, this gives you fine-grained control over which user — or which workflow — sees which data.

Three layers of governance

LayerWhat it doesGranularity
Table classificationMarks the entire table at a sensitivity level — public, internal, confidential, restricted.Per table
Column classification + mask strategyMarks individual columns and decides whether they're shown, redacted, hashed, or denied per role.Per column per role
Row filterA Rego predicate that restricts which rows a caller can see (e.g. "only rows where org_unit matches the caller's department").Per row per caller

All three are evaluated together at read time. There is no path to the data that bypasses them.

Table classification

Choose at table creation; can be changed by a workspace admin. The classifications map to who can use the table at all:

ClassificationWho can read the table
publicAny workspace member
internalWorkspace members with the data:read-internal grant (default for members)
confidentialWorkspace admins and above
restrictedWorkspace owners and org owners only

Higher classifications also affect default mask strategies on the columns — see below.

Column classification & mask strategies

Each column can declare a classification of its own. When set, the column's effective sensitivity is the max of its own classification and the table's. A column-level restricted column inside an internal table is still treated as restricted.

For each (column, role) pair, you can declare a mask strategy:

StrategyWhat the role sees
clearThe actual value
redactA fixed mask string ([REDACTED])
hashA deterministic hash — useful for joining masked data across queries
nullA NULL value as if the column didn't exist for them
denyThe query fails entirely with a permission error

Common patterns:

Column typeWorkspace memberWorkspace adminWorkspace owner
PII (name, email)redactclearclear
Salary / financialsnullnullclear
Internal IDsclearclearclear
External secretsdenydenyclear

Row filters

A row filter is a Rego predicate that decides whether a row is visible to a caller. Filters are written per role and per table. Example concepts:

  • Tenant scoperow.org_unit == caller.org_unit
  • Region scoperow.region in caller.allowed_regions
  • Project scoperow.project_id in caller.project_grants

The filter runs as part of the policy decision at read time. Rows that fail the filter are excluded from the result set; the caller has no signal that excluded rows existed at all.

The policy decision point

Classifications, masks, and filters are compiled into a per-tenant Rego bundle by the platform. Every read against a managed table evaluates that bundle. The bundle is rebuilt when:

  • A classification is changed.
  • A mask strategy is added or modified.
  • A row filter is added or modified.
  • A role definition changes.

Rebuilds are fast and apply on the next read; there is no waiting period.

OPA is the default policy decision point. A built-in fallback engine is also supported for deployments that prefer not to run OPA as a sidecar. Both consume the same bundle.

Audit

Every read against a managed table emits an RESOURCE_ACCESS event in the audit log with:

  • The actor and their resolved role.
  • The table ID and the columns requested.
  • The row count returned (not the rows themselves).
  • The mask strategies and row filters that applied.

For compliance audits, this answers "who saw what column when" without exposing the data itself.

On this page

On this page