Skip to main content

Policy YAML reference

A Metatate policy is a single YAML (or JSON) document validated against the DataPolicy schema. Validation is strict: unknown fields are rejected at every level, with one escape hatch — an optional extensions map at each level for your own extension data. The YAML parser uses the safe default schema (no date coercion, no executable tags), and blank values in governance fields are hard errors, so a policy can never silently govern nothing.

Top-level structureDirect link to Top-level structure

apiVersion: metatate.io/v1
kind: DataPolicy
metadata: { ... }
spec: { ... }
extensions: { ... } # optional
FieldRequiredValue
apiVersionyesMust be exactly metatate.io/v1.
kindyesMust be exactly DataPolicy.
metadatayesIdentity and business context.
specyesScope, classification, and rule sections.
extensionsnoScenario mapping, display metadata, and free extension keys.

metadataDirect link to metadata

FieldRequiredValue
nameyes1–64 character lowercase slug (letters, digits, internal dashes).
versionyesSemver MAJOR.MINOR.PATCH (prerelease and build metadata allowed).
descriptionnoWhat the policy does.
intentnoThe authoring intent behind the policy.
sourceTypenoData provenance note (for example crm_sync); emits a lineage instruction.
businessContextnoObject with optional purpose, domain, owner, steward; emits an advisory business-context instruction.
tagsnoFree-form string labels on the policy.

spec basicsDirect link to spec basics

FieldRequiredValue
enforcementModeyesenforce, monitor, or advisory. In monitor and advisory modes, enforceable rules are served as require_review instead of their active decision.
priorityyesInteger 0–999. 800–999 ranks critical, 500–799 high, 300–499 medium, 101–299 low; 100 and below falls back to the classification sensitivity.
selectoryesTarget scope (below).
classificationyessensitivity (public, internal, confidential, restricted), category (must start with pii, phi, pci, financial, intellectual_property, operational, or public — exact, or extended like pii.contact), optional subcategory.
validitynoThe window this version is in force (below). Omit it for a version that is always in force.

spec.validityDirect link to spec.validity

A policy version can declare when it is in force. Both fields are optional, so you may set a start, an end, or both:

spec:
validity:
effectiveFrom: '2026-08-01T00:00:00Z'
effectiveUntil: '2026-09-01T00:00:00Z'

The rules, in full:

  • RFC 3339 date-time with an offsetZ or ±HH:MM. A bare date (2026-08-01) or a bare local date-time with no offset is rejected. Offsets are normalized to UTC, so 2026-08-01T02:00:00+02:00 is stored as 2026-08-01T00:00:00.000Z.
  • Real calendar instants only. 2026-02-30T00:00:00Z is rejected rather than silently rolling over to March 2.
  • effectiveFrom must be strictly before effectiveUntil. Equal bounds are rejected — an empty window is never what anyone meant.
  • The window is version-level. Every rule extracted from the version carries it; there are no per-rule windows.
  • Omitting it means always in force. Both bounds serve as null, and a policy with no validity block checksums exactly as it did before the field existed.

At serving time, a rule participates only while effective_from <= evaluated_at < effective_until — the start is inclusive, the end is exclusive, and a null bound is unbounded on that side. When rules exist for an asset and scenario but none is in force, decision-bearing tools answer not_enough_published_state with reason not_currently_effective rather than inventing an answer. See Typed answer states.

Two things are deliberately out of scope in this version: time-of-day windows and calendars, and per-rule windows. There is also no structured field for validity in the brief editor today — author it in the YAML source.

spec.selectorDirect link to spec.selector

The selector names what the policy governs, across seven first-class target dimensions. At least one dimension must be non-empty — an unscoped policy is rejected. Matching is exact against normalized catalog identifiers; wildcard characters (*, ?, [, ]) are rejected because they would silently match nothing.

DimensionValueNotes
databases, schemasIdentifier namesHierarchical filters over the other dimensions.
tablesTable namesTable-level targets.
columnsColumn namesColumn-level targets within the table universe (or estate-wide if no tables are named).
tagsTag namesEach also emits a governance-tag instruction.
collectionsCollection idsTargets a named group of tables by id; at most 64 entries, each up to 256 characters.
taxonomyTypesClassification type keysTargets every column classified with the type (for example pii.contact.email); same bounds as collections.

Composition: explicitly named tables plus the active member tables of listed collections form the table universe, filtered by databases and schemas. taxonomyTypes refine to column level — intersected with the table universe when one exists, otherwise matching classified columns estate-wide. When both columns and taxonomyTypes are present, the column targets are their union. Collection and taxonomy targets are resolved at deployment time against the current catalog, so classify-once governance follows your estate as it grows.

Rule sectionsDirect link to Rule sections

All rule sections are optional; each present section must carry real content.

spec.accessControlmasking (type starting with none, partial, full, hash, tokenize, encrypt, redact, dynamic, conditional, or custom; optional config object and exemptRoles; type none emits nothing), plus allowedRoles and deniedRoles lists that become per-role allow/deny instructions.

spec.rowFilter — optional description, plus required non-empty rules. Each rule has a required non-blank expression (the row predicate), optional roles (must name at least one role when present; omit it for a table-wide predicate), and optional description. Serves as a conditional decision carrying the obligation.

spec.accessWindows — purpose-bound agent data access. Requires actorRoles (non-empty exact role tokens), timeColumn, and non-empty rules. Each rule has purposeKey, type (rolling or as_of), and lookbackDays (positive integer). Only one selector dimension may be used, and column selectors are rejected: a window governs table reads and names its time column separately.

spec:
accessWindows:
actorRoles: [agent, analyst]
timeColumn: created_at
rules:
- purposeKey: research.general
type: rolling
lookbackDays: 90
- purposeKey: commercial.general
type: rolling
lookbackDays: 30

At decision time, the token's bound role and the exact purpose_key select a rule. Rolling windows use the server's evaluated_at instant. as_of rules require data_access_context.as_of. Missing or unmatched context fails closed with access_window_context_required; SQL validation must prove a predicate no wider than the returned lower bound.

spec.usagepermittedUses and/or prohibitedUses; at least one list must be non-empty. Permitted uses serve allow, prohibited uses serve deny.

The vocabulary is open — any non-blank string validates, forever — but what you write here decides how purpose_key is evaluated at serving time, so prefer registry vocabulary:

spec:
usage:
permittedUses: [analytics.reporting, compliance.audit]
prohibitedUses: [marketing, ai.third_party_export]

A category token (marketing) matches every purpose in that family. A key (ai.third_party_export) matches only itself. A legacy free string that normalizes onto a registry key (Model Trainingai.training) still works. Anything else is unmapped, and an unmapped entry can never prove coverage — a permitted rule carrying one answers require_review for every purpose, and it can never neutralize a prohibition either.

The policy editor shows this while you type: entries are chip-coded by how they resolved, unmapped and normalizable strings get an advisory warning, and a normalizable one offers a one-click swap to the canonical key. The warnings never block saving, review, or deployment — they are advice, not validation. Note that they appear in the structured brief editor, not the raw YAML tab.

Read Purpose keys before writing these lists. In particular: on an asset with permitted-uses rules, a call that states no purpose fails closed to review — so switching a list to registry vocabulary changes what purpose-blind callers get.

spec.aiGovernanceallowTraining, allowInference (booleans that serve allow/deny for AI training and inference), requiresAnonymization (serves conditional), and free-form restrictions.

spec.retention — optional period, trigger, action free strings; serves retain.

spec.compliance — required non-empty regulations list of named regimes (open vocabulary, for example GDPR); served as compliance context.

spec.transferGovernancedefaultEffect (allow, deny, conditional) and rules, each with optional effect, operations, destinationSystems, destinationJurisdictions, consumerJurisdictions, requiresApproval, requiresAnonymization, requiredRole. A satisfiable approval or anonymization path serves conditional; an outright deny serves deny. Extra keys pass through.

extensionsDirect link to extensions

FieldPurpose
scenarioMapMaps a stable instruction key to { scenarioKey, source }, overriding an instruction's default canonical scenario. scenarioKey must be canonical (see Scenarios); source is defaulted, ai_suggested, or user_confirmed.
scopeRefsStructured display/provenance references for tables, collections, taxonomy types, and roles. Authoring metadata only — spec.selector stays authoritative.
instructionDisplayDisplay-only title/description overrides per instruction key. Never deployable.
any other keyUnknown extension keys pass through unchanged (for example displayName).

Worked exampleDirect link to Worked example

This is a complete, real policy from the Customer 360 sample estate, exactly as the demo workspace authors and publishes it:

# Customer 360 estate spec — customer data use guardrails.
# Real Metatate Cloud policy DSL: this exact document is what the demo
# workspace authors, approves, and publishes; the served instruction rows
# are derived from it by the real governance engine.
apiVersion: metatate.io/v1
kind: DataPolicy
metadata:
name: customer-use-guardrails
version: 1.0.0
sourceType: crm_sync
description: >-
Customer data supports analytics, reporting, and support workflows;
marketing, advertising, personalization, and model training are
prohibited. AI training is blocked and inference requires anonymization.
tags: [pii, privacy_sensitive, ai_training_blocked]
businessContext:
domain: Customer Data
owner: Revenue Operations
steward: privacy-review@example.com
purpose: >-
Customer master data used for approved reporting, analytics, support,
and controlled operational exports.
spec:
enforcementMode: enforce
priority: 550
selector:
databases: [master, product]
schemas: [public]
tables: [customers, support_tickets, product_usage_events]
tags: [privacy_sensitive]
classification:
sensitivity: confidential
category: pii
usage:
# `analytics` and `marketing` are registry CATEGORY tokens — they match
# every purpose in those families. `advertising` and `personalization`
# normalize onto registry keys. `reporting`, `support`, and `ml_training`
# are unmapped legacy strings.
permittedUses: [analytics, reporting, support]
prohibitedUses: [marketing, advertising, personalization, ml_training]
aiGovernance:
allowTraining: false
allowInference: true
requiresAnonymization: true
extensions:
displayName: Customer use guardrails
scenarioMap:
"usage_guidance:spec.usage.prohibitedUses:prohibited":
scenarioKey: purpose.prohibited_use
source: user_confirmed

Walking through it: the selector scopes three tables in master.public plus the privacy_sensitive tag, at priority 550 (high) in enforce mode. The usage section produces an allow instruction for analytics, reporting, and support, and a deny instruction for the prohibited uses — which the scenarioMap pins to the canonical purpose.prohibited_use scenario as a user-confirmed choice. At serving time a call stating purpose_key: analytics.reporting matches the analytics category token and keeps the allow; the same call with no purpose answers review_required. The aiGovernance section splits into three instructions: AI training denied, inference allowed, and a conditional anonymization obligation. Classification, sourceType, and businessContext serve as context alongside the decisions.

From YAML to deployable instructionsDirect link to From YAML to deployable instructions

Saving a valid document creates a canonical policy version; approving it makes it ready. Metatate then deterministically extracts the deployable instructions above — each with a scenario, decision, priority, enforcement mode, reason, and provenance — and a deployment publication makes them live. Ready is not live: nothing here answers a query until it is published. See Policies in the app and the governance model.