OpenCTI model adapter¶
Corrobore maps the OpenCTI 7.260722.0 graph model through the
opencti-adapter crate. The adapter is intentionally separate from
graph-core: OpenCTI-specific schemas, identifiers, access metadata, and
compatibility rules can evolve without adding CTI policy to the generic graph
engine.
The source lock, mapping contract, and golden fixtures live together:
compatibility/opencti/7.260722.0/source-lock.jsonpins the upstream source.compatibility/opencti/7.260722.0/model-mapping.jsondescribes mapping version1.0in a machine-readable form.compatibility/opencti/7.260722.0/model-fixtures.jsoncovers every supported object and relationship family, access context, and nested extension.compatibility/opencti/7.260722.0/parity-corpus.jsonremains a regression corpus and is also required to round-trip without loss.
Canonical graph representation¶
Objects become graph nodes with the OpenCtiObject base label, a family label,
and a sanitized entity-type label. Relationships retain their original
direction, semantic type, source identifier, and target identifier.
Every canonical graph record stores:
| Property | Meaning |
|---|---|
opencti.raw |
The complete typed JSON record, including unknown fields and nested extensions |
opencti.mapping_version |
Independent adapter mapping version |
opencti.family |
Pinned object or relationship family |
opencti.canonical_id |
Stable OpenCTI identity used by the projection |
opencti.identifiers |
Typed identifier keys |
opencti.references |
Reference-bearing fields |
opencti.access |
Access-policy inputs, without authorization decisions |
opencti.timestamps |
Validated semantic timestamps |
opencti.provenance |
External references, source references, and migration metadata |
opencti.field.* |
Typed copies of top-level fields for graph consumers |
The lossless opencti.raw value is authoritative for reconstruction. Typed
copies are projections and must not be used to discard fields from the raw
record.
Families and forward compatibility¶
The adapter distinguishes STIX domain objects, cyber observables, meta objects, OpenCTI internal objects, STIX core relationships, reference relationships, sightings, and internal relationships.
An unknown object or relationship type is preserved as a generic typed OpenCTI
record. It is never silently converted to another semantic type such as
Identity. A malformed record with no usable type, identifier, relationship
type, or endpoint is rejected with an explicit mapping error.
Identifier projection¶
Lookups are namespaced by identifier kind:
- internal ID;
- standard ID;
- current or historical STIX ID;
- external-reference ID;
- alias;
- deduplication ID.
One active record may own a typed identifier. Create, update, merge, delete, replay, and rebuild operations are atomic. Updates require the exact current revision. Deletes tombstone the record and release its identifiers. Merges move identifiers to the survivor and tombstone sources. Replaying an identical transaction is idempotent; reusing its transaction ID with different content is an explicit conflict.
Mapping migrations¶
The mapping version is independent of both the Corrobore storage schema and the OpenCTI source release. A migration rebuilds identifier projections from canonical records under the target mapping version. Conflicts are deterministic and abort the rebuild instead of selecting a winner implicitly.
Access metadata is descriptive adapter output. Authorization and tenant-policy decisions remain outside this crate and outside the scope of the mapping.
Snapshot and mutation synchronization¶
OpenCtiSynchronizer applies a consistent snapshot followed by an ordered
catch-up stream. Every mutation carries a stable replay identity and monotonic
source sequence. A durable checkpoint records the snapshot phase, high-water
mark, last contiguous acknowledgement, retry queue depth, bounded replay
fingerprints, and bounded dead-letter diagnostics.
The supported mutation classes are object upsert/delete, relationship upsert/delete, and access-policy replacement for either record kind. Upserts replace the complete canonical payload so fields removed upstream do not remain as stale graph properties. Relationship replacements also move adjacency entries when endpoints change.
The HTTP runtime commits each accepted source batch as one canonical WAL transaction, then fsyncs and atomically renames the synchronization checkpoint. If the response or checkpoint write is lost after the graph commit, the same batch can be replayed without duplicating canonical records.
Parity validation compares active records, lossless properties, identifier projections, relationship endpoints and types, access-policy inputs, and derived-projection freshness. Shadow reads remain disabled until every dimension matches.