- Interoperability. ODCS is a Linux Foundation project. Soda, Great Expectations, and dbt speak it. Your contract is not a proprietary blob.
- No lock-in. Click Export, get a directory of YAML. Import it into whichever tool you pick next. No rewrite required.
- Version control. Contracts diff cleanly in Git. Reviewers see which validity rule changed in a PR, not a screenshot of a settings page.
ODCS-native vs ODCS-extended
ODCS covers the core concepts every tool agrees on: schema, column quality rules, freshness SLAs, ownership. We use its sanctionedcustomProperties extension mechanism for the AnomalyArmor-specific concepts ODCS does not have a native slot for.
| Native ODCS (any tool reads these) | Extended (under customProperties.anomalyarmor) |
|---|---|
apiVersion, id, name, version, status, domain, tags | Schema drift monitoring (ODCS has no schema-change concept) |
schema[] with properties[] + physicalType | ML distribution drift (PSI, KS, chi-squared) |
quality[] metric library (nullValues, invalidValues, duplicateValues, rowCount) | Row-count anomaly detection with rolling baselines |
quality[] custom SQL | Alert rules and routing |
slaProperties[] (latency, frequency) | Destinations (Slack, email, PagerDuty, Linear, webhooks) |
team[] | Operating schedules and blackout windows |
customProperties is exactly how Soda and Great Expectations handle the same tension: industry-standard, not a workaround.
ODCS models customProperties as a list of {property, value} entries, not a dict. Our AnomalyArmor extensions all ride inside a single entry whose property is anomalyarmor:
Deviations from ODCS v3.1.0
Small, documented places where our exported YAML does more or less than the spec:schema[].properties[].id(extension). ODCS does not define anidat the column level. We write a stable UUID5 per column so re-import can match columns across renames.odcs-puremode strips it; other ODCS tools read the column bynameand ignoreid.- Unmodeled optional sections. These ODCS sections are recognized but we do not produce or consume them:
servers,roles,price,support,authoritativeDefinitions,contractCreatedTs. They round-trip opaquely if present on import and are never emitted on export.
Portability modes
Two export modes control what gets written to the YAML.extended (default)
Full round-trip fidelity. Includes customProperties.anomalyarmor.<domain> blocks with every AA-specific configuration knob (drift thresholds, monitoring modes, alert routing, etc.). Use this for version control, backup, and re-import into AnomalyArmor.
odcs-pure
Strips every customProperties.anomalyarmor block. The resulting YAML contains only the ODCS-native subset that any ODCS tool can read and act on: schema, quality rules, freshness SLAs, team. You lose the AA-extended features (drift, alert routing, blackouts) but gain maximum portability for the interop case.
Pick odcs-pure when you want to hand the contract to a Soda or Great Expectations customer. Pick extended everywhere else.
CLI
armor and anomalyarmor. They are aliases, use whichever you prefer. Examples below use armor for brevity.
Export: one asset to stdout
yq or redirect to a file.
Export: one asset to a file
Export: whole warehouse as a zip
Export: every contract in your account
Filtering (export or import)
Include or exclude specific config domains:schema, freshness, validity, metrics, row_count, drift_monitors, schema_drift, alert_rules, destinations, blackouts.
Export: ODCS-pure mode
Validate a YAML file (no DB, no auth required beyond the API key)
Plan an import (dry-run diff)
+ is additions, ~ is modifications, - is deletions.
Apply an import
--prune:
--prune deletes configs that are absent from the YAML. Treat this the way you would treat rm -rf, review the plan output first.
Diff two local YAML files
contracts/main.yaml to contracts/pr.yaml before calling plan or apply.
In-product UI
The same primitives ship in the app.- Export. Every asset detail page has an Export as YAML action. Opens a modal with three controls: asset scope (this asset / schema / warehouse / all), feature-domain checkboxes, and the
extended/odcs-puretoggle. Multi-asset scopes download as a zip. - Import. Same page, Import YAML action next to Export. Drag-drop or click-to-browse a
.yamlfile. The modal runs server-side validation, then shows a plan preview (+2 freshness, ~1 validity, -0 drift). Apply button is disabled until the plan is clean. Prune has an explicit confirmation dialog that lists what will be removed. - Bulk import. Asset list supports checkbox selection; the bulk action bar exposes Import YAML which applies one contract to many selected assets (async job, polled in the modal).
REST API
All endpoints authenticated via your API key. Single-asset endpoints are synchronous; bulk endpoints run as async jobs.Export, single asset (synchronous)
application/x-yaml with a Content-Disposition: attachment header.
Export, bulk (asynchronous)
{"job_id": "<uuid>"}. Poll with:
status, progress_percent, and asset_count. When status is completed, download:
application/zip with the laid-out {warehouse}/contract/{table}.yaml archive.
Validate (no DB, no asset context)
{ valid: bool, errors: [{line, yaml_path, code, message}], contract_summary: {...} }.
Plan, single asset (synchronous)
{ valid, errors, diffs: { <domain>: { added, modified, removed } } }. Read-only, no mutations.
Apply, single asset (synchronous, transactional)
{ valid, applied: { <domain>: {added, modified, removed} }, unsupported_domains: [], pruned: bool }. Partial failures roll back the entire contract.
Apply, bulk (asynchronous)
{"job_id": "<uuid>"}. Poll GET /api/v1/contracts/apply-jobs/{job_id} for per-asset status.
Round-trip example
Adapter coverage at a glance
Three adapters ship today. The grid shows which kinds of rules each adapter converts automatically, which convert with caveats, and which you rewrite as custom rules in AnomalyArmor.Coming from Soda?
Soda ships ODCS export as of 2024. Runsoda export --odcs on your existing Soda project and the resulting YAML imports directly into AnomalyArmor:
Coming from dbt or Great Expectations?
Those tools do not emit ODCS directly. We ship adapters that translate their config into ODCS YAML, which then feeds the samecontract apply pipeline:
Limits and current gaps
- No S3 artifact storage for huge bulk exports. Jobs today inline the zip bytes in the status response, which is fine for hundreds of tables. Company-wide exports with thousands of tables should use multiple
--warehousejobs. - Single-asset apply only in
contract apply. Multi-asset apply ships through the bulk-apply REST/UI path, not through the CLI flag surface yet. - Some domains are
unsupportedon import. Theapplyresponse lists any domains whoseimport_is not yet implemented inunsupported_domains. Pair withplanfirst to see whether they would have changed before committing.
Common Questions
What is ODCS and why does AnomalyArmor use it?
ODCS is the Open Data Contract Standard, a Linux Foundation project that defines a vendor-neutral YAML format for data contracts. Using it means your AnomalyArmor config is portable: Soda, Great Expectations, and dbt also speak it, so you keep optionality if you ever switch tools.What is the difference between extended and odcs-pure export modes?
extended (default) includes the full customProperties.anomalyarmor block so the contract round-trips with every AA-specific feature intact (drift, alert routing, blackouts). odcs-pure strips those blocks and emits only the ODCS-native subset (schema, quality, freshness, team). Use odcs-pure when handing the contract to a Soda or Great Expectations user, extended everywhere else.
How do I preview contract changes before applying them?
Runarmor contract plan --asset <uuid> -f contract.yaml. It returns a per-domain diff showing additions, modifications, and deletions without mutating anything. Apply with armor contract apply once the diff looks right. See Plan an import.
Does contract apply delete configs that aren’t in my YAML?
No, not by default. Configs present in the live asset but missing from the YAML are warn-only, so you won’t accidentally wipe rules by applying a partial contract. Pass --prune to explicitly delete them, and treat that flag the way you would rm -rf, always review the plan output first.
Can I put AnomalyArmor contracts in Git?
Yes, that’s one of the main reasons to use them. The YAML diffs cleanly in PRs so reviewers see which validity rule or freshness SLA changed, not a screenshot of a settings page. Export witharmor contract pull, commit the file, and use armor contract diff in CI to compare branches.
Can I validate a contract YAML without applying it?
Yes.armor contract validate -f contract.yaml runs the document against the official ODCS v3.1.0 JSON Schema and exits non-zero on parse or schema errors. Errors print with file path, YAML path, and line number so editors and CI can surface them inline. No DB connection required.