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
| Field | Required | Value |
|---|---|---|
apiVersion | yes | Must be exactly metatate.io/v1. |
kind | yes | Must be exactly DataPolicy. |
metadata | yes | Identity and business context. |
spec | yes | Scope, classification, and rule sections. |
extensions | no | Scenario mapping, display metadata, and free extension keys. |
metadataDirect link to metadata
| Field | Required | Value |
|---|---|---|
name | yes | 1–64 character lowercase slug (letters, digits, internal dashes). |
version | yes | Semver MAJOR.MINOR.PATCH (prerelease and build metadata allowed). |
description | no | What the policy does. |
intent | no | The authoring intent behind the policy. |
sourceType | no | Data provenance note (for example crm_sync); emits a lineage instruction. |
businessContext | no | Object with optional purpose, domain, owner, steward; emits an advisory business-context instruction. |
tags | no | Free-form string labels on the policy. |
spec basicsDirect link to spec basics
| Field | Required | Value |
|---|---|---|
enforcementMode | yes | enforce, monitor, or advisory. In monitor and advisory modes, enforceable rules are served as require_review instead of their active decision. |
priority | yes | Integer 0–999. 800–999 ranks critical, 500–799 high, 300–499 medium, 101–299 low; 100 and below falls back to the classification sensitivity. |
selector | yes | Target scope (below). |
classification | yes | sensitivity (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. |
validity | no | The 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 offset —
Zor±HH:MM. A bare date (2026-08-01) or a bare local date-time with no offset is rejected. Offsets are normalized to UTC, so2026-08-01T02:00:00+02:00is stored as2026-08-01T00:00:00.000Z. - Real calendar instants only.
2026-02-30T00:00:00Zis rejected rather than silently rolling over to March 2. effectiveFrommust be strictly beforeeffectiveUntil. 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 novalidityblock 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.
| Dimension | Value | Notes |
|---|---|---|
databases, schemas | Identifier names | Hierarchical filters over the other dimensions. |
tables | Table names | Table-level targets. |
columns | Column names | Column-level targets within the table universe (or estate-wide if no tables are named). |
tags | Tag names | Each also emits a governance-tag instruction. |
collections | Collection ids | Targets a named group of tables by id; at most 64 entries, each up to 256 characters. |
taxonomyTypes | Classification type keys | Targets 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.accessControl — masking (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.usage — permittedUses 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 Training → ai.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.aiGovernance — allowTraining, 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.transferGovernance — defaultEffect (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
| Field | Purpose |
|---|---|
scenarioMap | Maps 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. |
scopeRefs | Structured display/provenance references for tables, collections, taxonomy types, and roles. Authoring metadata only — spec.selector stays authoritative. |
instructionDisplay | Display-only title/description overrides per instruction key. Never deployable. |
| any other key | Unknown 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.