opentide

Validation

opentide validates detection content through a structured pipeline: ID uniqueness, UUID format, Pydantic schema validation, vocabulary conformance, deprecated field warnings, and cross-object reference checks. Optional checks include CVE scanning and per-platform query validation.

Requirements

  • opentide validate MUST run default checks: id-uniqueness, uuid-format, schema.
  • Schema validation MUST load each object via metadata.schema and validate against the registered Pydantic model.
  • Vocabulary validation MUST walk metaschema tide.vocab annotations for each object type.
  • UUID values MUST be UUID version 4.
  • Object UUIDs MUST be unique across the workspace registry.
  • Cross-object references MUST resolve to existing objects when checked.
  • Threat chaining relationships MUST reference valid targets when checked.
  • Query validation MUST only be offered for platforms in the query-validation allowlist (see platforms/index.md).
  • Platforms without query validators MUST return supported: false — not fake success.
  • The schema check MUST report external_id_in_rule for rule_id, rule_id_bundle, or rule_id::<tenant> at the root of any platform block and MUST suppress the redundant generic unknown-key diagnostic for that same field.
  • Full-workspace validation MUST validate every deployment-state shard under .opentide/states/deployment/, including orphan shards whose object_uuid is not in the registry. A missing shard is not an error.

Definition

Check types (ValidateCheck)

CheckIdentifierDefaultDescription
ID uniquenessid-uniquenessyesNo duplicate metadata.uuid across YAML files
UUID formatuuid-formatyesEvery UUID must be valid UUIDv4
SchemaschemayesPydantic model validation + vocab + cross-ref
CVEcvenoCVE reference integrity (optional flag)
Sharing configsharing-confignosharing.toml integration blocks: required keys, unique names, known keys, max_tlp, object_types, and organisation_uuid. On for opentide share push.

Schema check sub-steps

When schema is enabled, for each in-scope object the implementation MUST, in order:

  1. Load and model-validate — resolve the object's metadata.schema and validate it against the registered model.
  2. Vocabulary — check controlled-vocabulary fields against the indexed metaschema vocabulary annotations.
  3. Deprecation — flag deprecated fields as warnings.
  4. References — resolve references links to existing objects.
  5. Chaining — resolve chaining relationships (threat vectors only).

The model-validation step reports a legacy external-id key as external_id_in_rule rather than as an unknown key (platforms/index.md). The key is checked at the root of every platform block only; a key of the same name nested inside alert, query, exclusions, or another field is not a legacy key.

Steps 4 and 5 resolve cross-object links; they run whenever the schema check runs against a full workspace scope where the referenced objects are present.

Validation scope

ModeBehavior
fullAll objects in the workspace index
narrowFilter by --file, --uuid, and/or --type

When --file, --uuid, or --type filters match no objects, validation MUST emit a scope_no_match error rather than silently passing — a filter that selects nothing is treated as a mistake, not a success. With several --file targets, validation MUST fail when any one of them matches no object, even if the others do. The production write-back relies on this to commit only rule files that were actually validated (deployment-state.md).

Deployment state

In full scope, validation MUST read every *.json file under .opentide/states/deployment/ and report the deployment-state codes: deployment_state_shape, deployment_state_platform, and deployment_state_uuid_mismatch (including two files that canonicalise to one uuid). Orphan shards are validated like any other. A rule with no shard is not an error. narrow scope MAY check only the shards of the selected rules plus any shard file passed directly.

CodeSeverityWhen
external_id_in_ruleerrorA legacy external-id key at the root of a platform block
deployment_state_shapeerrorA shard is unreadable or violates deployment.state::1.0
deployment_state_platformerrorA shard names a platform that is not state-backed
deployment_state_uuid_mismatcherrorA shard's filename and object_uuid disagree or are not lowercase canonical

Platform advisories

A platform spec MAY list advisories: conditions that validation SHOULD report as a warning without rejecting the rule. Each has a stable code, and the CLI and editors MUST report a finding under that code. Codes are unique across platform specs: a later spec MUST NOT reuse a code for a different condition, and picks a new one instead. An advisory never sets VALIDATION_ERROR_RAISED.

CodeSeverityWhen
lookback_zerowarningElastic: scheduling.lookback is zero
lookback_longwarningElastic: scheduling.lookback exceeds 1h
threshold_lookbackwarningElastic: a threshold rule's lookback exceeds its interval
esql_metadata_idwarningElastic: a non-aggregating ES|QL query (no STATS step) lacks METADATA _id
eql_maxspan_windowwarningElastic: an EQL maxspan exceeds interval + lookback
eql_maxspan_lookbackwarningElastic: an EQL maxspan exceeds lookback but not interval + lookback
risk_bandwarningElastic: an explicit risk score is outside its severity's band
technique_not_enterprisewarningElastic: the rule names a technique that is not Enterprise ATT&CK, which deploy skips

The conditions and their reasons are in elastic-1.0.

Query validation

Command: opentide validate query --platform <id> --live

PlatformSupported
sentinelyes
defender_for_endpointyes
splunkyes
sentinel_oneyes
carbon_black_cloudyes
elasticyes, live only: Kibana rule preview and the Elasticsearch checks (elastic-1.0)
crowdstrikeno
harfanglabno

Requires platform credentials and enabled configuration. Validates deployed or planned rule queries against the live platform API. Query validation is a tenant check on every platform in the query-validation allowlist above: without --live the command MUST refuse and name --live, and MUST NOT report a pass. crowdstrike and harfanglab report supported: false instead (platforms). No platform has an offline syntax check; Elastic's query language (KQL, Lucene, EQL, or ES|QL) comes from each rule's block: its type, and language for the types that take one.

Specifications-repo fixture checker

This repository’s CI (scripts/object_fixtures.py) is not opentide validate. It proves object-spec field types against fixtures without a Pydantic runtime. Codes below are checker codes; they are distinct from the ValidateCheck identifiers (uuid-format, schema, …).

FixtureChecker code
fixtures/invalid/threat-impact-as-string.yamlfield_not_list
fixtures/invalid/threat-leverage-semicolon.yamlpacked_vocab_string
fixtures/invalid/threat-impact-semicolon.yamlpacked_vocab_string
fixtures/invalid/threat-leverage-semicolon-list-item.yamlpacked_vocab_string
fixtures/invalid/threat-impact-empty.yamlempty_vocab_list
fixtures/invalid/threat-leverage-empty.yamlempty_vocab_list
fixtures/invalid/threat-leverage-high.yamlunknown_vocab_value
fixtures/invalid/threat-actors-string-list.yamlactors_not_objects
fixtures/invalid/threat-actors-not-list.yamlactors_not_objects
fixtures/invalid/threat-actor-missing-name.yamlactor_missing_name
fixtures/invalid/threat-actor-unscoped.yamlactor_unscoped
fixtures/invalid/threat-actor-wrong-stage.yamlactor_unknown
fixtures/invalid/threat-missing-body.yamlmissing_threat_body
fixtures/invalid/rule-bad-uuid.yamlinvalid_uuid
fixtures/invalid/rule-unknown-schema.yamlunknown_schema
fixtures/invalid/rule-missing-metadata.yamlmissing_metadata
fixtures/invalid/rule-reviewed-bad-date.yamlinvalid_reviewed
fixtures/invalid/rule-1.0-reviewed.yamlreviewed_not_in_schema
fixtures/invalid/objective-no-signals.yamlempty_signals
fixtures/invalid/objective-signal-entry-mixed.yamlsignal_entry_mixed
fixtures/invalid/objective-signal-bad-uuid.yamlinvalid_uuid
fixtures/invalid/objective-1.0-signal-binding.yamlbinding_requires_1_1
fixtures/invalid/rule-elastic-esql-index.yamlesql_source (also language and suppression_shape)
fixtures/invalid/rule-search-language.yamlsearch_language
fixtures/sharing/invalid/unknown-distribution.tomlunknown_key
fixtures/sharing/invalid/legacy-targets-table.tomlunknown_key
fixtures/sharing/invalid/duplicate-name.tomlduplicate_name
fixtures/sharing/invalid/missing-url.tomlmissing_field
fixtures/sharing/invalid/bad-name.tomlname_invalid
fixtures/sharing/invalid/bad-max-tlp.tomlmax_tlp_unknown
fixtures/sharing/invalid/missing-max-tlp.tomlmissing_field
fixtures/sharing/invalid/bad-organisation-uuid.tomlorganisation_uuid_invalid
fixtures/sharing/invalid/bad-object-type.tomlobject_type_unknown
fixtures/deployment/invalid/bad-schema.jsondeployment_state_shape
fixtures/deployment/invalid/uuid-mismatch.jsondeployment_state_uuid_mismatch
fixtures/deployment/invalid/noncanonical-uuid.jsondeployment_state_uuid_mismatch
fixtures/deployment/invalid/numeric-id.jsondeployment_state_shape
fixtures/deployment/invalid/leading-zero-defender-id.jsondeployment_state_shape
fixtures/deployment/invalid/empty-id.jsondeployment_state_shape
fixtures/deployment/invalid/whitespace-tenant.jsondeployment_state_shape
fixtures/deployment/invalid/unsupported-platform.jsondeployment_state_platform
fixtures/deployment/invalid/rule-with-rule-id.yamlexternal_id_in_rule
fixtures/deployment/invalid/rule-with-rule-id-tenant.yamlexternal_id_in_rule
fixtures/deployment/invalid/rule-with-rule-id-bundle.yamlexternal_id_in_rule

Deployment fixtures (scripts/deployment_state_fixtures.py) are checked against schemas/deployment.state.1.0.schema.json plus the filename rules the schema cannot see. Each invalid shard fixture is checked under the shard filename the checker assigns to it (a uuid that differs from object_uuid for uuid-mismatch.json, the uppercase spelling for noncanonical-uuid.json, the valid name otherwise). The valid shard must be byte-identical to writer output, and no rule fixture in fixtures/valid/, fixtures/platforms/, or fixtures/cross-object/ may carry a legacy external-id key.

Sharing fixtures (scripts/sharing_fixtures.py) also prove the merge rule: override.toml merged by name onto sharing.toml MUST equal merged.toml.

On a rule's configurations.elastic block the checker applies three of the Elastic constraint codes, the ones the invalid Elastic fixture exercises: language, esql_source, and suppression_shape. They are the platform's own codes, so the checker and opentide validate report the same name. The other Elastic codes, and every other platform block, are left to opentide.

On a rule's response.procedure.searches[] the checker reports search_language when language is set with a system other than elastic, or with a value other than kuery, lucene, eql, or esql (rule-1.1).

A packed semicolon string that happens to start with a valid token MUST still fail packed_vocab_string. Accepting only the first token is lossy and non-compliant.

Issue severities

SeverityExamples
errorSchema failure, duplicate UUID, invalid UUID, broken cross-ref, external_id_in_rule, invalid deployment-state shard
warningDeprecated field usage, a platform advisory

Environment signals

VariableSet when
VALIDATION_ERROR_RAISEDOne or more errors
VALIDATION_WARNING_RAISEDOne or more warnings

Parallel execution

Validation MAY parallelize per-object work and ID scans; worker count is resolved from object count and optional --workers flag.

Relationships

Defaults & overrides

Validation uses merged configuration for vocabulary and status enums. No separate validation config file. Scope defaults to full workspace.

Examples

FixtureExpected result
fixtures/valid/rule-1.0.yamlPasses uuid-format + schema (opentide validate)
fixtures/valid/threat-1.0.yamlPasses threat::1.0 lists (impact, leverage) and list[ThreatActor]
fixtures/invalid/rule-bad-uuid.yamlFails uuid-format (opentide validate); checker code invalid_uuid
fixtures/invalid/rule-unknown-schema.yamlFails schema (opentide validate); checker code unknown_schema
fixtures/cross-object/rule-references-objective.yamlPasses when sibling objective fixture present
fixtures/deployment/valid/00000000-0000-4000-8003-000000000010.jsonValid deployment.state::1.0 shard in writer form
fixtures/deployment/invalid/rule-with-rule-id-tenant.yamlFails schema with external_id_in_rule

History

VersionDateNotes
1.42026-10-23Fixture checker code search_language for response.procedure.searches[].language (specifications#58).
1.42026-10-23Platform advisories: the eight Elastic advisory codes are warnings (elastic-1.0, specifications#57).
1.32026-10-19elastic joins query validation, live only through Kibana rule preview and the Elasticsearch checks (elastic-1.0). The command is validate query --live on every platform in the query-validation allowlist; without --live it refuses. The fixture checker applies three Elastic constraint codes (language, esql_source, suppression_shape) to configurations.elastic blocks.
1.22026-10-19objective::1.1 signal binding codes (RFC 0011). The fixture checker proves signal_entry_mixed, invalid_uuid on reuse, and binding_requires_1_1. unresolved_signal, self_signal, duplicate_signal, duplicate_signal_uuid, and signal_tlp are opentide validate against the workspace library.
1.12026-10-16external_id_in_rule replaces the generic unknown-key error for legacy external-id keys; full-workspace validation checks every deployment-state shard, orphans included; every --file target must match an object; deployment fixture checker codes (RFC 0010).
1.02026-09-30Fixture checker codes invalid_reviewed and reviewed_not_in_schema (RFC 0008).
1.02026-09-28sharing-config checker codes for [[misp]] integration blocks.
1.02026-09-16Document specifications-repo fixture checker codes for threat::1.0 list/ThreatActor encoding (#11, #12).
1.02026-06-25Initial spec from opentide validation/session.py

On this page