opentide

Workspace layout

Defines the standard detection-repository directory layout scaffolded by `opentide setup` and used for object storage, generated artifacts, documentation mirrors, and client configuration overrides.

Requirements

  • A healthy opentide workspace MUST contain objects/ with subdirectories for each core object family.
  • Generated artifacts MUST live under .opentide/ — not committed as hand-edited sources.
  • .opentide/states/deployment/ is generated deployment state and MUST be committed. .opentide/states/sharing.jsonl is a regenerable cache and SHOULD be gitignored. Implementations MUST NOT gitignore the whole .opentide/states/ directory (deployment-state.md).
  • Client configuration overrides MUST live only under .opentide/configurations/.
  • Object YAML files MUST reside in the paths defined by merged paths.toml configuration.
  • An object instance MUST be a YAML file placed directly in its family directory (objects/rules/rule.yaml, not objects/rules/team/rule.yaml). A file in a subdirectory of a family directory is not an object instance: implementations MUST NOT deploy or share it, and SHOULD warn that it is outside the catalogue. Nested folders under a family directory are otherwise outside this specification.
  • Object YAML filenames SHOULD use dash-case slugs (lowercase, hyphens) ideally aligned with the object name field via the same slugify() rules the opentide engine uses for documentation paths. The name field itself MAY be richer (title case, punctuation, spaces). On slug collision, append a short UUID suffix. This is a soft convention for catalogue hygiene, not a schema validation requirement.
  • opentide generate schemas MUST emit schema artifacts to .opentide/schemas/.
  • opentide generate templates MUST emit templates to .opentide/templates/.

Definition

Core object families

FamilyConfig keyDefault path
Threatpaths.objects.threatobjects/threats/
Objectivepaths.objects.objectiveobjects/objectives/
Rulepaths.objects.ruleobjects/rules/

Registered objects list: threat, objective, rule.

.opentide/ layout

PathPurposeClient may override config?
.opentide/configurations/TOML overrides (paths, deployment, schema, platforms)Yes
.opentide/configurations/platforms/Per-platform TOMLYes
.opentide/configurations/sharing.tomlEvery sharing integration block ([[misp]])Yes
.opentide/states/sharing.jsonlCurrent sharing synchronization ledger. One JSON object per line. Regenerable cache; SHOULD be gitignored.No
.opentide/states/deployment/Deployment state: one <uuid>.json shard (deployment.state::1.0) per rule holding the Defender for Endpoint, SentinelOne, and CrowdStrike rule ids per tenant. Authoritative; MUST be committed.No
.opentide/schemas/Generated JSON SchemaNo
.opentide/templates/Generated YAML templatesNo
.opentide/exports/Generated exportsNo
.opentide/inflight/CI preview shards for open PR/MR object changesNo

Documentation mirror paths

KeyDefault path
paths.docs.rootdocs/
paths.docs.rulesdocs/rules/
paths.docs.objectivesdocs/objectives/
paths.docs.threatsdocs/threats/

Generated schema artifacts

FamilyArtifact filename
threatthreat.1.0.schema.json
objectiveobjective.1.0.schema.json
objectiveobjective.1.1.schema.json
rulerule.1.0.schema.json
visibilityvisibility.1.0.schema.json
routeropentide.schema.json
inflight shardinflight.shard.1.0.schema.json

Inflight shard schema source of truth: schemas/inflight.shard.1.0.schema.json. Deployment state shard schema source of truth: schemas/deployment.state.1.0.schema.json. Generated copies MAY be emitted to .opentide/schemas/ by opentide generate schemas when supported.

visibility.1.0.schema.json is not an object family — there are no visibility objects under objects/. It is the schema used to validate the client visibility.toml configuration, emitted here alongside the object schemas. opentide.schema.json is the IDE router that dispatches object YAML to the correct object schema by metadata.schema.

Generated template artifacts

FamilyTemplate filename
threatthreat.1.0.template.yaml
objectiveobjective.1.0.template.yaml
objectiveobjective.1.1.template.yaml
rulerule.1.0.template.yaml

Export artifacts

KeyFilename
artifacts.exports.revisionsrevisions.export.json
artifacts.exports.objectsobjects.export.json
artifacts.exports.attack_layerattack-navigator.json

Other paths

KeyDefault
paths.opentide.root.opentide/
paths.opentide.schemas.opentide/schemas/
paths.opentide.templates.opentide/templates/
paths.exports.root.opentide/exports/
paths.objects.snippet_file.vscode/model-templates.code-snippets

Example workspace tree

detection-repo/
├── objects/
│   ├── threats/
│   ├── objectives/
│   └── rules/
├── docs/
│   ├── rules/
│   ├── threats/
│   └── objectives/
├── .opentide/
│   ├── configurations/
│   │   └── platforms/
│   ├── schemas/
│   ├── templates/
│   ├── exports/
│   ├── inflight/
│   └── states/
│       ├── deployment/
│       │   └── <uuid>.json
│       └── sharing.jsonl
├── .vscode/
├── .github/workflows/
├── README.md
└── .gitignore

Inflight preview shards

CI writes one JSON file per changed object UUID under .opentide/inflight/<uuid>.json on pull requests and merge requests. Shards use schema inflight.shard::1.0 and wrap the parsed object document plus PR/MR metadata:

FieldTypeDescription
schemastringAlways inflight.shard::1.0
written_atstring (ISO-8601)When the shard was written
objectobjectFull parsed threat, objective, or rule document
changeobjectPR/MR provenance (platform, number, url, title, head_ref, base_ref, head_sha, source_path, recorded_at)

Consumers overlay shards onto the committed registry when the shard object metadata.version is greater than the committed version, or equal with a wrapped shard (preview wins on ties). After merge to the default branch, opentide generate inflight prune removes shards superseded by committed YAML.

CI metadata sources

PlatformDetected whenPrimary env vars
GitHub ActionsGITHUB_ACTIONS setGITHUB_EVENT_PATH (pull_request payload), GITHUB_HEAD_REF, GITHUB_BASE_REF, GITHUB_SHA
GitLab CICI set (and not GitHub/Azure)CI_MERGE_REQUEST_IID, CI_PROJECT_URL, CI_MERGE_REQUEST_TITLE, CI_MERGE_REQUEST_SOURCE_BRANCH_NAME, CI_MERGE_REQUEST_TARGET_BRANCH_NAME, CI_COMMIT_SHA
Azure PipelinesTF_BUILD setSYSTEM_PULLREQUEST_PULLREQUESTNUMBER or SYSTEM_PULLREQUEST_PULLREQUESTID, SYSTEM_PULLREQUEST_PULLREQUESTURI, SYSTEM_PULLREQUEST_PULLREQUESTTITLE, SYSTEM_PULLREQUEST_SOURCEBRANCH, SYSTEM_PULLREQUEST_TARGETBRANCH, BUILD_SOURCEVERSION
Localnone of the aboveINFLIGHT_CHANGE_JSON optional override

change.source_path is the object YAML path relative to the workspace root (OPENTIDE_REPO_ROOT).

CI jobs

Job idTriggerCommand
inflight_shardspull request / merge requestopentide generate inflight
inflight_prunepush to default branchopentide generate inflight prune
sharepush to the default branch only, and only when setup selected the sharing stageopentide share push --changed
deploy_productionpush to the default branchopentide deploy --strategy RELEASE, then the deployment-state write-back whenever the deploy started (below)

share is the production release. Setup defaults the stage off (--no-sharing; the wizard checkbox is unchecked). opentide setup MUST NOT emit it, or any other opentide share command, on a pull request or merge request. The trigger, the setup choice, and the diff are specified in sharing.md.

Production deployment state

The production deploy job (opentide deploy --strategy RELEASE on a push to the default branch) is the only generated job that commits deployment state. opentide setup and opentide setup ci MUST generate it to meet deployment-state.md CI requirements 1–7:

  1. Queue production jobs without dropping runs (no GitHub concurrency group, GitLab resource_group, Azure the strongest available lock, documented).
  2. Before the dry run and the deploy, overlay .opentide/states/deployment/ from the default-branch tip.
  3. Run the write-back whenever the deploy started, including after a failure, a cancellation, or a timeout.
  4. Stage shards independently of rule files; commit rule files only when opentide validate --strict passes on exactly those files, read NUL-delimited.
  5. On a rebase conflict, replay this run's state delta per platform and tenant (opentide deploy replay-state); a concurrent change to the same binding is deployment_state_conflict and fails the job.
  6. On every terminal write-back failure, print the state diff and push the commit to refs/opentide/deployment-state/<commit> before failing.
  7. Log every recorded id as external_id_recorded at WARNING.

Staging, pull-request, and merge-request deploy jobs MUST NOT commit deployment state. A create on Defender for Endpoint, SentinelOne, or CrowdStrike in those jobs is refused with deployment_state_unpersisted before the vendor call (deployment-state.md).

Relationships

Defaults & overrides

Bundled paths.toml ships in the opentide package. Clients MAY override path values via .opentide/configurations/paths.toml. See configuration.md.

Examples

Object fixtures assume the default layout: rules under objects/rules/, etc.

History

VersionDateNotes
1.52026-10-20The production deploy job runs opentide deploy --strategy RELEASE; opentide 0.17.0 removed --plan.
1.42026-10-19objective::1.1 schema and template artifacts beside objective::1.0 (RFC 0011).
1.32026-10-19Object instances are the YAML files placed directly in each family directory. A file in a subfolder is not an instance and is neither deployed nor shared (specifications#16, opentide#312).
1.22026-10-16The production deploy job has no GitHub Actions concurrency group (specifications#43).
1.12026-10-16.opentide/states/deployment/ holds committed deployment-state shards; .opentide/states/ is never ignored whole and setup ignores sharing.jsonl only. The production deploy job serialises, overlays the default branch's state, and writes it back, and staging jobs cannot create on state-backed platforms (RFC 0010).
1.02026-09-28Optional CI stage share runs opentide share push --changed on a push to the default branch.
1.02026-09-28Sharing uses one client file, sharing.toml, and one generated ledger, .opentide/states/sharing.jsonl.
1.02026-06-25Initial spec from opentide paths.toml

On this page