opentide

Configuration models

Pydantic models of each .opentide/configurations/ file, their JSON Schema export, and the per-file issue helper editors use.

Every file under .opentide/configurations/ has one Pydantic model in opentide.models.configuration. The CLI validates the merged document (bundled file, then your workspace file, then the parent instance) against that model when it reads the file, and the JSON Schema of the file is model_json_schema() of the same model. There is no second, hand-written schema to drift from the CLI.

FileModelSchema file
paths.toml, legacy global.tomlPathsConfigurationpaths.schema.json
deployment.tomlDeploymentConfigurationdeployment.schema.json
schema.tomlSchemaConfigurationschema.schema.json
visibility.tomlVisibilityConfig (visibility::1.0)visibility.schema.json
documentation.tomlDocumentationConfigurationdocumentation.schema.json
sharing.tomlSharingConfiguration, items MispIntegration (sharing::misp::1.0)sharing.schema.json
platforms/<id>.tomlSentinelConfiguration, SplunkConfiguration, …platforms.<id>.schema.json
any other platforms/*.tomlPlatformConfigurationplatforms.schema.json

resources.toml and lint.toml have no model here: lint.toml is checked by opentide lint's own loader.

Unknown keys are rejected everywhere except the retired [vocabulary] table of schema.toml and a top-level sharing.toml array that names a connector opentide does not know ([[opencti]]), which is skipped with an unknown_connector warning.

A $NAME placeholder is a valid value for any tenant setup key; the models check the file as written, and the variable is read when the tenant loads. An error never repeats the offending value, since setup tables hold credentials.

from opentide.models.configuration import configuration_model_for

model = configuration_model_for(".opentide/configurations/platforms/sentinel.toml")
model.model_validate({"platform": {"enabled": True}})

Exporting the JSON Schemas

from pathlib import Path

from opentide.generation.configuration_schemas import (
    export_configuration_schemas,
    generate_configuration_schema,
)

schema = generate_configuration_schema("deployment.toml")
export_configuration_schemas(Path("catalogs/configuration"))

The same export runs from the command line:

python -m opentide.generation.configuration_schemas catalogs/configuration

It writes one schema per file kind and configurations.router.json, which maps each file name (deployment.toml, platforms/sentinel.toml, platforms/*.toml, sharing.toml, …) to its schema and says how layers merge: deep (scalars replace, tables merge, arrays replace) or, for sharing.toml, by-name. Field descriptions are the hover text; enums (strategy, tenant strategies, documentation flavor, max_tlp, object families) are JSON Schema enums. These schemas are instance-independent; the enriched visibility.1.0.schema.json that opentide generate schemas writes into .opentide/schemas/ is unchanged.

Checking one file

configuration_file_issues returns ValidationIssue records (code, field_path, severity, message) for one file, the same shape validate reports for objects. A client file is a partial override, so it is merged onto the layers before it (the bundled file by default) and required keys apply to that merge, never to the file alone.

from opentide.validation.configuration import configuration_file_issues, configuration_text_issues

issues = configuration_file_issues("deployment.toml", {"promotion": {"promotion_target": "LIVE"}})
# [ValidationIssue(code="promotion_target_unknown", field_path=("promotion", "promotion_target"), ...)]

issues = configuration_text_issues("sharing.toml", buffer_text, status_names={"PRODUCTION"})
CodeSeverityRaised for
missing_fielderrorA required key is missing from the merged document
unknown_keyerrorA key the model does not declare
schema_validationerrorA value of the wrong type or outside its enum
promotion_target_unknownerrorpromotion.promotion_target is not a [[statuses]] name
asset_unknownwarningA visibility log source or detector names an undeclared asset
identifier_mismatcherror[platform] identifier differs from the file name stem
config_parseerrorThe text is not valid TOML
not_a_configuration_filewarningThe path is not a file opentide reads, or has no model (resources.toml, lint.toml)

A [[responders]] entry that does not match its model is a warning: the loader keeps an entry that has a name and skips one that has none. A platform file is checked with the model of the platform the loader files it under ([platform] identifier, then [tide] identifier, then the file name). A platform plugin's file is open: only [platform], [[modifiers]] and the tenant name, strategies, manual and ssl are checked. A path under configurations/sharing/ returns sharing_directory, and one under .opentide/schemas/ is not a configuration file.

sharing.toml field paths point into the checked file: ("misp", "<index>", "<key>"), with the block name in context.block. A finding on a block the file does not hold (it comes from an earlier layer) has an empty path, and context.layer / context.index say where it is. sharing.toml returns the sharing-config codes of the sharing spec: missing_field, duplicate_name, name_invalid, unknown_key, max_tlp_unknown, organisation_uuid_invalid, object_type_unknown, sharing_directory, rule_status_unknown (errors), and api_key_literal, unknown_connector (warnings). api_key must be ${NAME} with an uppercase name; a literal key is a warning, and the warning names the block, never the key.

ArgumentDefaultPurpose
earlier_layersthe bundled fileLayers the file overrides; pass the workspace file when checking a parent instance
later_layersnoneLayers that override the file
status_namesthe workspace's statuses, read from an absolute path; else the bundled onesMerged deployment.toml status names, for sharing.toml rule_statuses
sharing_directoryFalseReport sharing_directory when configurations/sharing/ exists
partialFalseSkip required-key checks, for a layer checked without the layers it overrides

Source

src/opentide/models/configuration/, src/opentide/generation/configuration_schemas.py, src/opentide/validation/configuration.py

On this page