External Normalizer / Adapter Boundary Contract
This page defines the boundary between external gateways or adapters and
nats-sinks. It is written for defence and mission-support readers, but the
contract is generic enough for business systems, media pipelines, observability
feeds, logistics adapters, and enterprise integration services.
nats-sinks is not a tactical protocol stack, media decoder, C2 service,
authorization gateway, sanitizer, policy authority, or semantic validator.
Those responsibilities stay with systems that are approved for the source
domain. nats-sinks consumes already-authorized, already-normalized events
from NATS JetStream and persists them with at-least-once, commit-then-ACK
delivery semantics.
Boundary Model
flowchart LR
Source[Source system or sensor] --> Adapter[External gateway / adapter / decoder]
Adapter --> Normalize[Normalize, authorize, redact, validate]
Normalize --> JS[NATS JetStream]
JS --> Runner[nats-sinks core runner]
Runner --> Policy[Generic validation and policy]
Policy --> Sink[Configured sink]
Sink --> Store[Durable store]
Store --> Ack[ACK after durable commit]
The external component owns:
- protocol parsing, media decoding, and domain-specific validation;
- authorization, sanitization, redaction, and release decisions;
- schema translation from source-specific data into a deployment-approved event shape;
- classification and security-label assignment according to the deployment policy;
- payload-reference creation when raw or large artifacts should not be embedded in the JetStream message;
- semantic interpretation of tactical, media, C2, logistics, or enterprise data.
nats-sinks owns:
- JetStream consumption through the configured durable consumer;
- normalized event envelope validation;
- generic mission metadata validation when configured;
- generic security-label, classification, priority, and label preservation;
- optional payload encryption, policy checks, and custody metadata;
- DLQ handling for permanent validation failures;
- destination writes through configured sinks;
- ACK only after durable success for all required sink work.
Adapter Families
The same boundary applies across adapter types:
| Adapter family | External responsibility | nats-sinks responsibility |
|---|---|---|
| Enterprise data gateway | Map business transactions into stable events. | Persist event payload, metadata, lineage, and audit context. |
| Link 16 / TADIL-J normalizer | Interpret authorized J-series-derived data outside nats-sinks. | Persist the normalized event and metadata. |
| VMF / K-series normalizer | Interpret authorized VMF-derived data outside nats-sinks. | Persist the normalized event and metadata. |
| MIP4-IES / C2 object adapter | Perform MIP-specific exchange, validation, and object mapping. | Preserve object custody, version, correlation, and relationship metadata. |
| NFFI / FFTS / FFI adapter | Parse, authorize, and redact tracking data externally. | Preserve tracking-event custody and data-quality metadata. |
| STANAG 4609 / KLV / MISB decoder | Demux media, decode approved metadata, and create artifact references. | Persist media custody records and decoded metadata placeholders. |
| Observability or telemetry adapter | Normalize operational signals and apply sharing policy. | Persist or route approved event records without changing delivery semantics. |
For the companion standards overview, see the Interoperability Standards Matrix.
Normalized Event Requirements
Every adapter should publish a bounded, deployment-approved event envelope. The exact profile is deployment-specific, but a durable event should normally include:
| Field | Purpose |
|---|---|
event_id |
Stable event identifier generated outside nats-sinks. |
source_system |
System that produced or owns the source event. |
source_gateway |
Gateway, decoder, or adapter that normalized the event. |
schema_profile |
Named schema or metadata profile used by the adapter. |
schema_version |
Version of that schema profile. |
payload or normalized_payload |
Approved normalized event body. |
raw_payload_reference |
Optional reference to an external artifact or source export. |
payload_sha256 |
Digest of the normalized payload or referenced artifact. |
event_time |
Time the source event claims or represents. |
receive_time |
Time the adapter or gateway received the event. |
classification |
Deployment-approved classification or sensitivity label. |
labels |
Searchable, low-cardinality labels approved for storage. |
security_labels |
Optional data-centric security label profile. |
mission_metadata |
Optional generic mission or operational metadata. |
data_quality |
Optional quality, confidence, freshness, or completeness metadata. |
correlation_id |
Identifier that links related events or transactions. |
idempotency_key |
Stable duplicate-detection key or fields used to derive one. |
custody |
Optional custody fields or hash references supplied by the adapter. |
Use the generic Mission Metadata, Security Label Profile, Idempotency, Tamper-Evident Custody, and Dead Letter Queues guidance when designing a profile.
Idempotency And ACK Behavior
Adapters should publish stable identifiers. nats-sinks assumes at-least-once
delivery, so redelivery is normal. A duplicate event should result in an
idempotent update or no-op rather than a second mission, media, or business
record.
Good idempotency sources include:
event_idplussource_system;- source stream sequence plus gateway identifier;
- artifact ID plus artifact hash;
- object ID plus object version;
- track ID plus event time and sequence;
- transaction ID plus source system.
Do not use mutable fields such as priority, classification, labels, or free text as the only idempotency key.
nats-sinks ACKs JetStream only after the required durable sink work succeeds.
If validation fails permanently, the configured DLQ behavior must complete
before ACK. If sink commit status is uncertain, prefer redelivery over silent
loss.
Synthetic Examples
The following examples are illustrative and unclassified. They are not protocol parsers, message catalogues, tactical procedures, or controlled interface specifications.
Generic Business Event
{
"event_id": "business-event-0001",
"source_system": "order-service",
"source_gateway": "enterprise-adapter",
"schema_profile": "nats-sinks.business-event.v1",
"schema_version": "1.0",
"event_time": "2026-05-31T10:00:00Z",
"receive_time": "2026-05-31T10:00:02Z",
"correlation_id": "order-correlation-0001",
"classification": "internal",
"labels": ["synthetic", "business"],
"payload_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"normalized_payload": {
"event_type": "order_created",
"order_reference": "ORDER-EXAMPLE-0001",
"status": "created"
},
"idempotency_key": "order-service:business-event-0001"
}
Generic Media Artifact Event
{
"event_id": "media-artifact-0001",
"source_system": "media-pipeline",
"source_gateway": "media-normalizer",
"schema_profile": "nats-sinks.artifact-reference.v1",
"schema_version": "1.0",
"event_time": "2026-05-31T10:01:00Z",
"receive_time": "2026-05-31T10:01:03Z",
"classification": "internal",
"labels": ["synthetic", "media"],
"raw_payload_reference": {
"artifact_id": "artifact-0001",
"uri": "object://example-bucket/synthetic/artifact-0001.bin",
"media_type": "application/octet-stream",
"sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
},
"data_quality": {
"quality_source": "media-normalizer",
"confidence": 0.98
},
"idempotency_key": "artifact-0001"
}
Synthetic Link 16-Derived Event
{
"event_id": "link16-derived-0001",
"source_system": "authorized-gateway-placeholder",
"source_gateway": "link16-normalizer-placeholder",
"schema_profile": "nats-sinks.normalized-link-event.v1",
"schema_version": "1.0",
"message_family": "TADIL-J",
"message_name": "Jx.x",
"event_time": "2026-05-31T10:02:00Z",
"receive_time": "2026-05-31T10:02:02Z",
"correlation_id": "synthetic-track-correlation-0001",
"classification": "placeholder",
"labels": ["synthetic", "link16-derived"],
"payload_sha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"normalized_payload": {
"synthetic": true,
"track_reference": "TRACK-EXAMPLE-0001",
"status": "observed"
},
"mission_metadata": {
"profile": "synthetic-mission-event",
"phase": "observe"
},
"idempotency_key": "authorized-gateway-placeholder:link16-derived-0001"
}
Synthetic VMF-Derived Event
{
"event_id": "vmf-derived-0001",
"source_system": "vmf-source-placeholder",
"source_gateway": "vmf-normalizer-placeholder",
"schema_profile": "nats-sinks.normalized-vmf-event.v1",
"schema_version": "1.0",
"message_family": "VMF",
"message_name": "K-series-placeholder",
"event_time": "2026-05-31T10:03:00Z",
"receive_time": "2026-05-31T10:03:02Z",
"classification": "placeholder",
"labels": ["synthetic", "vmf-derived"],
"payload_sha256": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"normalized_payload": {
"synthetic": true,
"sender_reference": "SENDER-EXAMPLE",
"recipient_reference": "RECIPIENT-EXAMPLE",
"status": "normalized"
},
"idempotency_key": "vmf-source-placeholder:vmf-derived-0001"
}
Synthetic MIP4-IES-Derived C2 Object Event
{
"event_id": "c2-object-0001",
"source_system": "c2-adapter-placeholder",
"source_gateway": "mip-adapter-placeholder",
"schema_profile": "nats-sinks.object-relationship.v1",
"schema_version": "1.0",
"event_time": "2026-05-31T10:04:00Z",
"receive_time": "2026-05-31T10:04:02Z",
"classification": "placeholder",
"labels": ["synthetic", "c2-object"],
"normalized_payload": {
"object_id": "OBJ-EXAMPLE-0001",
"object_type": "generic-object",
"object_operation": "upsert",
"object_version": "1"
},
"data_quality": {
"quality_source": "mip-adapter-placeholder",
"completeness": 1.0
},
"idempotency_key": "OBJ-EXAMPLE-0001:1"
}
Synthetic FFTS / FFI Tracking Event
{
"event_id": "tracking-event-0001",
"source_system": "tracking-source-placeholder",
"source_gateway": "ffts-adapter-placeholder",
"schema_profile": "nats-sinks.geo-temporal-track-fix.v1",
"schema_version": "1.0",
"event_time": "2026-05-31T10:05:00Z",
"receive_time": "2026-05-31T10:05:02Z",
"classification": "placeholder",
"labels": ["synthetic", "tracking"],
"normalized_payload": {
"track_id": "TRACK-EXAMPLE-0002",
"position_reference": "redacted-position-reference",
"position_quality": "reduced",
"synthetic": true
},
"data_quality": {
"quality_source": "ffts-adapter-placeholder",
"timeliness": 0.95
},
"idempotency_key": "TRACK-EXAMPLE-0002:2026-05-31T10:05:00Z"
}
Synthetic FMV / KLV-Derived Event
{
"event_id": "fmv-klv-event-0001",
"source_system": "media-source-placeholder",
"source_gateway": "klv-decoder-placeholder",
"schema_profile": "nats-sinks.fmv-custody-event.v1",
"schema_version": "1.0",
"event_time": "2026-05-31T10:06:00Z",
"receive_time": "2026-05-31T10:06:03Z",
"classification": "placeholder",
"labels": ["synthetic", "fmv", "custody"],
"raw_payload_reference": {
"artifact_id": "fmv-segment-0001",
"uri": "object://example-bucket/synthetic/fmv-segment-0001.ts",
"media_type": "video/mp2t",
"sha256": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
},
"normalized_payload": {
"synthetic": true,
"segment_id": "SEGMENT-EXAMPLE-0001",
"presentation_timestamp": "2026-05-31T10:06:00Z",
"decoded_metadata_reference": "klv-placeholder-0001"
},
"idempotency_key": "fmv-segment-0001"
}
Subject Naming Guidance
Subject design should remain boring and reviewable. Prefer stable, bounded subject families such as:
enterprise.orders.createdmission.synthetic.link.normalizedmission.synthetic.vmf.normalizedmission.synthetic.c2.objectmission.synthetic.tracking.fixmission.synthetic.media.custody
Avoid putting sensitive values, raw identifiers, coordinates, unit names, target references, or free-text mission details in subject tokens. Put approved context in metadata fields where validation, redaction, and storage policy are clear.
Failure Handling
Adapters should distinguish temporary source-side delays from permanent
normalization failures before publishing to JetStream. Once an event reaches
JetStream, nats-sinks applies the configured validation and sink behavior:
- malformed normalized event metadata should fail closed;
- permanent validation failures should go to the configured DLQ before ACK;
- temporary sink failures should result in retry, redelivery, or bounded spool behavior according to the deployment configuration;
- duplicate delivery should be safe through idempotent sink configuration.
Non-Goals And Safety Notes
This contract does not implement or describe Link 16, VMF, MIP4-IES, NFFI, FFTS, FFI, STANAG 4609, SMPTE 336 KLV, MISB, ADatP, APP, or any other protocol. It does not include controlled field catalogues, classified message semantics, operational coordinates, real tracks, keys, frequencies, target data, platform identifiers, unit identifiers, or tactical procedures.
Use this page only for authorized environments and synthetic examples. Actual source mappings must be defined by approved programme teams and reviewed under their own data-handling, releasability, and accreditation processes.