Background
Proposed for v1.3. Today restricted_columns masking is static — a single fixed value-mask applied identically to everyone who is restricted. There are no masking types (full, partial last-4, hash, email-preserving, format-preserving) and no conditional reveal (mask for analysts, reveal for the data owner / a privileged group). Per-role dynamic masking with reveal conditions is standard in the category (Satori, Cyral, Immuta "reveal policies", Privacera, Bytebase, Hoop). This enhances the existing masking code rather than replacing it, and is distinct from the planned v2.1 data classification and v2.0 column-permission enforcement — it governs how a visible value is rendered, not whether the column is accessible.
Scope
Named masking policies bound to columns, with a masking strategy and an optional reveal condition evaluated per requester.
- New table
masking_policy (Flyway V56+): id, organization_id, datasource_id, column_ref (schema.table.column), strategy (snake_case enum masking_strategy: FULL/PARTIAL/HASH/EMAIL/FORMAT_PRESERVING), strategy_params JSONB (e.g. visible suffix length), reveal_to (role / group / user list — who sees the unmasked value), enabled, timestamps.
- Enforcement extends the existing result-masking code in the
proxy module (the restricted-column matcher already runs at result-read time): resolve applicable policies for the requester, and for each policied column either reveal (requester in reveal_to) or apply the strategy. Backward-compatible — an unconfigured restricted_columns entry keeps today's behaviour.
- Strategies:
FULL (existing), PARTIAL (keep last N), HASH (stable SHA-256), EMAIL (j***@domain), FORMAT_PRESERVING (preserve length/shape). Masking is applied before serialization and before any result snapshot is stored, so masked values never persist unmasked.
- Audit: record which masking policies applied to a result (policy ids), never the unmasked values.
- Frontend: a "Masking policies" section on
DatasourceSettingsPage — per-column strategy picker, params, reveal-to selector, and a live preview of the masked rendering. Use the existing enum-label helpers (enumOptions) for strategy options (CLAUDE.md i18n rule).
Acceptance criteria
Pointers
Background
Proposed for v1.3. Today
restricted_columnsmasking is static — a single fixed value-mask applied identically to everyone who is restricted. There are no masking types (full, partial last-4, hash, email-preserving, format-preserving) and no conditional reveal (mask for analysts, reveal for the data owner / a privileged group). Per-role dynamic masking with reveal conditions is standard in the category (Satori, Cyral, Immuta "reveal policies", Privacera, Bytebase, Hoop). This enhances the existing masking code rather than replacing it, and is distinct from the planned v2.1 data classification and v2.0 column-permission enforcement — it governs how a visible value is rendered, not whether the column is accessible.Scope
Named masking policies bound to columns, with a masking strategy and an optional reveal condition evaluated per requester.
masking_policy(FlywayV56+):id,organization_id,datasource_id,column_ref(schema.table.column),strategy(snake_case enummasking_strategy:FULL/PARTIAL/HASH/EMAIL/FORMAT_PRESERVING),strategy_params JSONB(e.g. visible suffix length),reveal_to(role / group / user list — who sees the unmasked value),enabled, timestamps.proxymodule (the restricted-column matcher already runs at result-read time): resolve applicable policies for the requester, and for each policied column either reveal (requester inreveal_to) or apply the strategy. Backward-compatible — an unconfiguredrestricted_columnsentry keeps today's behaviour.FULL(existing),PARTIAL(keep last N),HASH(stable SHA-256),EMAIL(j***@domain),FORMAT_PRESERVING(preserve length/shape). Masking is applied before serialization and before any result snapshot is stored, so masked values never persist unmasked.DatasourceSettingsPage— per-column strategy picker, params, reveal-to selector, and a live preview of the masked rendering. Use the existing enum-label helpers (enumOptions) for strategy options (CLAUDE.md i18n rule).Acceptance criteria
V56+(new table +masking_strategyenum).restricted_columnsbackward-compat path.enumOptions/ enum-label helpers; no inline enum strings; i18n parity green.ApplicationModulesTest/ApiPackageDependencyTestgreen; coverage ≥ 90%.PARTIALmask → run SELECT as a non-revealed user → see the masked value; as a revealed user → see the full value.website/updated.Pointers
frontend/src/utils/enumLabels.tsfrontend/src/pages/datasources/(DatasourceSettingsPage)