Mission Metadata
Mission metadata is an optional core feature for carrying richer event context beside the NATS payload. It is designed for mission-oriented deployments, but it is not defence-only. The same mechanism can describe industrial telemetry, critical infrastructure events, operational reporting, logistics movements, security monitoring, or any workflow where messages need a small, validated context object that should travel consistently to every sink.
The feature is disabled by default. When enabled, the core runtime resolves a validated JSON object before a sink receives the message. Oracle can store the object in a configurable JSON column, the file sink includes it in the emitted JSON record, and future sinks should preserve the same object where their destination contract supports structured metadata.
flowchart LR
P[Publisher] -->|payload + optional header| JS[JetStream]
JS --> R[JetStreamSinkRunner]
R --> V[Validate mission metadata]
V --> E[NatsEnvelope.mission_metadata]
E --> O[Oracle MISSION_METADATA_JSON]
E --> F[File record mission_metadata]
V -->|invalid| DLQ[DLQ before ACK]
Why It Exists
Fixed columns are useful for stable framework-wide metadata such as
priority, classification, and labels. They become a poor fit when each
deployment wants different context fields: mission identifiers, operation
identifiers, platform IDs, source-system IDs, track IDs, correlation IDs,
confidence values, releasability markers, domain labels, or use-case-specific
phase markers such as F2T2EA.
Mission metadata solves that by providing one generic JSON object:
- the core validates it once,
- every sink sees the same normalized object,
- Oracle stores it as one JSON value instead of many fixed columns,
- file sink records include the same object,
- invalid metadata follows normal permanent-failure handling.
Configuration
Mission metadata is configured at the top level of the JSON config file:
{
"mission_metadata": {
"enabled": true,
"header": "Nats-Sinks-Mission-Metadata",
"max_bytes": 8192,
"allowed_profiles": ["mission-event-v1"],
"profiles": [
{
"name": "mission-event-v1",
"version": 1,
"unknown_fields": "forbid",
"required_fields": [
{"name": "event_id", "type": "string", "max_length": 128},
{"name": "source_system", "type": "string", "max_length": 128}
],
"optional_fields": [
{
"name": "f2t2ea_phase",
"type": "string",
"enum": ["find", "fix", "track", "target", "engage", "assess"]
},
{"name": "origin_domain", "type": "string", "max_length": 128}
]
}
],
"default": {
"profile": "mission-event-v1",
"profile_version": 1,
"event_id": "default-event",
"source_system": "operator-default",
"origin_domain": "operations"
},
"rules": [
{
"subject": "mission.synthetic.>",
"metadata": {
"profile": "mission-event-v1",
"profile_version": 1,
"event_id": "synthetic-default",
"origin_domain": "synthetic-test",
"source_system": "local-harness"
}
}
]
}
}
| Field | Required | Default | Valid values | Description |
|---|---|---|---|---|
enabled |
no | false |
true or false |
Turns mission metadata parsing on. Disabled runners ignore the configured header. |
header |
no | Nats-Sinks-Mission-Metadata |
Non-empty header name without control characters. | Header containing a JSON object supplied by the publisher. |
max_bytes |
no | 8192 |
Integer from 1 to 262144. |
Maximum canonical JSON size accepted for one metadata object. |
allowed_profiles |
no | [] |
List of non-empty strings. | Optional allow-list. When set, metadata must contain a profile value from this list. |
default |
no | null |
JSON object or null. |
Global metadata object used when the header is absent and no subject rule matches. |
rules |
no | [] |
Ordered list of subject rules. | Subject-aware defaults. First matching rule wins when the header is absent. |
Rule fields:
| Field | Required | Default | Valid values | Description |
|---|---|---|---|---|
subject |
yes | none | NATS subject pattern, for example mission.* or mission.>. |
Pattern matched against the message subject. |
metadata |
yes | none | JSON object or null. |
Metadata default for matching subjects. null explicitly clears the global default. |
Publisher Header Example
Publishers can provide mission metadata directly through the configured header:
nats pub mission.synthetic.sensor.track.0001 '{"event_id":"SYN-0001"}' \
-H 'Nats-Sinks-Mission-Metadata: {"profile":"mission-event-v1","profile_version":1,"mission_id":"SYN-MISSION-001","f2t2ea_phase":"track","source_system":"synthetic-sensor"}'
Header values are authoritative. If the header is present and empty, the message receives no mission metadata even when defaults are configured. This allows publishers to explicitly say that no mission context applies.
Validation Rules
Mission metadata is treated as untrusted input:
- the root value must be a JSON object,
- duplicate JSON keys are rejected,
- non-standard JSON constants such as
NaN,Infinity, and-Infinityare rejected at the parser boundary, - object keys must start with a letter and contain only letters, numbers, underscores, dots, colons, or hyphens,
- secret-looking key names such as
password,token,secret,private_key,credential, orkey_materialare rejected, - strings, arrays, objects, nesting depth, integer range, and total serialized size are bounded,
- control characters in keys and strings are rejected,
- non-finite numbers are rejected,
- configured
allowed_profilesfail closed. - known first-party
nats-sinks.*.v1profiles receive additional bounded validation for required fields, hashes, URI schemes, timestamps, enum values, and numeric ranges. - operator-defined profile registry rules receive additional fail-closed validation for required fields, optional fields, JSON types, enum values, max string lengths, nested object limits, and unknown-field behavior.
Malformed mission metadata is a permanent validation failure. If a DLQ is configured, the runner publishes the message to the DLQ and ACKs the original only after DLQ publication succeeds. If DLQ publication fails, the original is not ACKed and remains eligible for redelivery.
sequenceDiagram
participant JS as JetStream
participant R as JetStreamSinkRunner
participant DLQ as DLQ Subject
JS->>R: Deliver message
R->>R: Parse mission metadata header
R->>R: Validation fails
R->>DLQ: Publish failure record
DLQ-->>R: Publish succeeds
R->>JS: ACK original
Operator-Defined Profile Registry
Deployments can define their own mission metadata profiles in
mission_metadata.profiles. A profile applies when the metadata object's
profile field matches the configured profile name. If a version is
configured, the metadata object must also contain a matching profile_version.
Profile registry validation runs after the generic hostile-JSON checks and
after the optional allowed_profiles allow-list. Invalid profile data is a
permanent validation failure. When DLQ is configured, the runner publishes a
sanitized DLQ record before ACKing the original JetStream message.
Non-defence business event profile example:
{
"mission_metadata": {
"enabled": true,
"allowed_profiles": ["business-event-v1"],
"profiles": [
{
"name": "business-event-v1",
"version": 1,
"unknown_fields": "forbid",
"required_fields": [
{"name": "event_id", "type": "string", "max_length": 64},
{
"name": "event_type",
"type": "string",
"enum": ["created", "updated", "deleted"]
}
],
"optional_fields": [
{"name": "source_system", "type": "string", "max_length": 128},
{"name": "amount", "type": "number"},
{"name": "attributes", "type": "object", "max_depth": 1}
]
}
]
}
}
Synthetic mission-support profile example:
{
"mission_metadata": {
"enabled": true,
"allowed_profiles": ["normalized-link-event"],
"profiles": [
{
"name": "normalized-link-event",
"version": 1,
"unknown_fields": "forbid",
"required_fields": [
{"name": "event_id", "type": "string", "max_length": 128},
{"name": "source_gateway", "type": "string", "max_length": 128},
{"name": "message_family", "type": "string", "enum": ["TADIL-J"]},
{"name": "payload_sha256", "type": "string", "max_length": 64}
],
"optional_fields": [
{"name": "correlation_id", "type": "string", "max_length": 128},
{"name": "classification", "type": "string", "max_length": 128},
{"name": "data_quality", "type": "object", "max_depth": 2}
]
},
{
"name": "fmv-custody-event",
"version": 1,
"unknown_fields": "forbid",
"required_fields": [
{"name": "event_id", "type": "string", "max_length": 128},
{"name": "artifact_id", "type": "string", "max_length": 128},
{"name": "artifact_sha256", "type": "string", "max_length": 64}
],
"optional_fields": [
{"name": "presentation_timestamp", "type": "string", "max_length": 64},
{"name": "collection_id", "type": "string", "max_length": 128}
]
}
]
}
}
This registry is intentionally structural. It checks that configured metadata fields are present and bounded, but it does not parse tactical protocols, validate tactical semantics, authorize a release, or interpret domain-specific meaning. External gateways and adapters remain responsible for those decisions before publishing to JetStream.
First-Party Generic Profiles
Mission metadata can remain fully deployment-defined. When the profile value
matches one of the first-party generic profiles below, the core applies stricter
profile-specific validation while preserving the same optional, destination-
neutral storage model.
| Profile | Purpose | Required fields | Typical use |
|---|---|---|---|
nats-sinks.artifact-reference.v1 |
Stores a bounded reference to an external artifact instead of embedding large payloads. | artifact_id, uri, media_type, sha256 |
Documents, images, video segments, model artifacts, synthetic review packages. |
nats-sinks.data-quality.v1 |
Captures bounded data quality indicators. | none beyond profile |
Commercial data pipelines, observability events, synthetic mission-support quality notes. |
nats-sinks.geo-temporal-track-fix.v1 |
Captures custody for time-based track or movement updates without implementing a protocol. | track_id, source_system, event_time |
Fleet telemetry, asset tracking, synthetic friendly-force tracking custody. |
nats-sinks.object-relationship.v1 |
Captures object, relationship, version, and tombstone events. | object_id, object_type, object_operation |
CMDB objects, supply-chain entities, digital twins, synthetic C2 object custody. |
The profiles are generic by design. They do not implement Link 16, VMF, MIP4-IES, NFFI, FFTS, FMV, KLV, or any other domain protocol. External gateways, adapters, and normalizers remain responsible for authorization, sanitization, semantic validation, and domain-specific interpretation before publishing an event to JetStream.
Artifact reference example:
{
"profile": "nats-sinks.artifact-reference.v1",
"artifact_id": "doc-1001",
"uri": "s3://example-bucket/documents/doc-1001.json",
"media_type": "application/json",
"size_bytes": 2048,
"sha256": "0000000000000000000000000000000000000000000000000000000000000000",
"created_at": "2026-05-31T10:15:00Z",
"source": "document-pipeline",
"relationship": "payload-reference",
"retention_category": "audit",
"classification": "internal",
"labels": ["document", "audit"]
}
Data quality example:
{
"profile": "nats-sinks.data-quality.v1",
"accuracy": 0.98,
"completeness": 1.0,
"consistency": 0.99,
"timeliness": 0.94,
"validity": 1.0,
"confidence": 0.97,
"quality_source": "synthetic-quality-gate",
"assessed_at": "2026-05-31T10:17:00Z"
}
Track/fix example:
{
"profile": "nats-sinks.geo-temporal-track-fix.v1",
"track_id": "asset-1001",
"source_system": "fleet-tracker",
"event_time": "2026-05-31T10:18:00Z",
"receive_time": "2026-05-31T10:18:02Z",
"position_kind": "coordinates",
"position_reference": {
"latitude": 52.0,
"longitude": 5.0
},
"position_quality": "gnss",
"speed": 12.5,
"heading": 180.0,
"precision_category": "full",
"redaction_level": "none",
"confidence": 0.91,
"synthetic": true
}
Object/relationship example:
{
"profile": "nats-sinks.object-relationship.v1",
"object_id": "server-1001",
"object_type": "configuration_item",
"object_version": "7",
"object_operation": "upsert",
"source_system": "cmdb",
"schema_profile": "cmdb-object",
"schema_version": "1",
"event_time": "2026-05-31T10:20:00Z",
"data_quality": {
"confidence": 0.9,
"quality_source": "cmdb-sync"
}
}
Runnable JSON fixtures live under the repository
examples/metadata-profiles/
directory.
Oracle Storage
Oracle stores mission metadata in the MISSION_METADATA_JSON column by default.
The column name is configurable through sink.columns.mission_metadata.
Recommended column:
mission_metadata_json json
Example row value:
{
"profile": "mission-event-v1",
"profile_version": 1,
"mission_id": "SYN-MISSION-001",
"operation_id": "SYN-OP-ALPHA",
"f2t2ea_phase": "track",
"source_system": "synthetic-sensor",
"source_confidence": 0.91,
"releasability": ["NATO", "mission-partner"]
}
The generic METADATA_JSON document also includes the same object under its
mission_metadata field so operators can inspect one complete framework
metadata snapshot.
File Sink Output
The file sink writes mission metadata as a top-level field in each output record:
{
"schema": "nats_sinks.file.message.v1",
"subject": "mission.synthetic.sensor.track.0001",
"priority": "immediate",
"classification": "NATO SECRET",
"labels": "synthetic;mission-test;f2t2ea-example",
"labels_list": ["synthetic", "mission-test", "f2t2ea-example"],
"mission_metadata": {
"profile": "mission-event-v1",
"profile_version": 1,
"mission_id": "SYN-MISSION-001",
"f2t2ea_phase": "track"
},
"payload": {
"event_id": "SYN-0001"
}
}
When mission metadata is absent, the file sink writes JSON null in the
top-level mission_metadata field and under metadata.mission_metadata.
Relationship To Priority, Classification, And Labels
Mission metadata does not replace priority, classification, or labels.
Those fields remain small, stable, framework-wide fields with dedicated storage
in Oracle and file sink output. Mission metadata is for richer context that may
vary between deployments or use-case families.
Recommended split:
| Need | Use |
|---|---|
| Routing urgency, queue attention, operator priority | message_metadata.priority |
Information marking such as NATO SECRET or internal |
message_metadata.classification |
| Small searchable tags | message_metadata.labels |
| Mission, operation, platform, sensor, track, confidence, releasability, or phase context | mission_metadata |
Defence Use Cases
The defence examples in this repository use mission metadata for synthetic, metadata-only lifecycle tagging. See F2T2EA Event Phase Tagging for a detailed blueprint.
The feature is not a targeting system, fire-control system, weapons-release mechanism, rules-of-engagement engine, or autonomous decision layer. It is a data custody mechanism for preserving event context with clear validation, storage, and redelivery semantics.