Technical whitepaper v1.0 · one entry point, every connection
Defence supply chains run on data that lives in other people's systems. This paper describes the architecture we use to accept that data from any source, in any format, and deliver it in the shape each consumer requires, with a complete, provable record of what arrived, what happened to it, and what was sent back.
Integration & enterprise architects
1.0
August 2026
35 min read
A defence manufacturer with forty suppliers does not have a data problem. It has forty data problems, each with its own file format, its own transfer mechanism, its own idea of what a part number looks like, and its own failure modes. The conventional answer, build an integration per partner, works for the first five and becomes the dominant engineering cost somewhere around the fifteenth.
The Common Interface Framework is our answer to that curve. It is a single entry and exit point through which every transmission passes, whatever its source, format or transport. This paper sets out how it works, why it is shaped the way it is, and which decisions we would defend under scrutiny.
Integrations built one at a time are not wrong. Each is a reasonable local decision. The difficulty is that the reasonable local decisions do not compose, and the cost surfaces as five questions that nobody can answer quickly:
Every one of these is answerable inside a single integration, given enough time in its logs. None is answerable across integrations, because there is no shared vocabulary in which to ask. The organisation ends up with a set of pipelines that each work and a portfolio that cannot be operated.
There is a second cost, quieter and larger. When each integration owns its own retry logic, its own error handling and its own notion of “done”, those behaviours drift. One pipeline retries transient failures; another does not. One quarantines a bad record; another drops it. The organisation's actual data-handling policy becomes the union of whatever each integration's author happened to implement, and nobody can state what it is.
Point-to-point integration does not fail at scale. It succeeds, individually, while the portfolio becomes unoperable.
The framework rests on one choice: define the unit of work at the level of the interface object, not the partner system.
A purchase order is a purchase order whether it arrives from an ERP over an API, from a PLM system as a nightly export, or as a spreadsheet a supplier uploaded through a portal. The business meaning is identical. What differs is the encoding and the transport, and those are precisely the concerns that can be isolated behind an adapter.
So there is one definition, PURCHASE_ORDER_IN, describing what should happen to an inbound purchase order: which phases it passes through, how often it is processed, what validation applies, how failures are handled, who is told. Each partner supplies an adapter that turns their particular bytes into the canonical shape. Everything downstream of that adapter is shared.
Each individual transmission becomes an event, one row, one identifier, one history. That identifier is the spine of the entire design. It appears in log lines, audit records, error reports, reconciliation rows, lineage entries and the raw record store. An operator holding it can reach every artefact connected to a transmission without knowing which subsystem produced any of them.
Most integration platforms can tell you that a job ran. Far fewer can tell you what a specific transmission from a specific supplier on a specific night contained, which of its rows were rejected and why, what was written as a result, and who was notified, as a single query, months later, without reading a log file.
That capability is not a reporting feature bolted on afterwards. It is a consequence of choosing the right unit of work at the start, and it cannot be retrofitted onto a design that chose differently.
Six entities, split along one line: definitions are configuration; events are history.
| ENTITY | HOLDS |
|---|---|
| Interface | The catalogue entry for one integration: code, direction, partner system, business domain, criticality, owner, notification group |
| Event definition | What should happen and how often: cadence, priority, first and last phase, log level, retention, alarm threshold |
| Phase | One configurable unit of work: adapter, execution mode, timeout, retry limit, wait between attempts, routing on success, warning and error |
| Configuration | Environment-scoped keys, field mappings and per-partner overrides |
| Event | One transmission: identifier, tenant, direction, current phase, status, timings, retry counts, record count, messages |
| Payload | The bytes and what they are: type, format, storage reference, size, checksum, classification, retention expiry |
The separation earns its keep the first time a definition changes. An event processed under last month's phase sequence retains the sequence it actually ran; it does not silently acquire a phase that did not exist when it executed. Definitions are versioned, not mutated. Anyone who has tried to explain a historical result under a configuration that has since been edited in place will recognise why.
A phase is one unit of work with a narrow contract. It receives the event identifier, returns a result code and a message, and is responsible for recording its own completion, log output and status. The framework records the start.
Phases are configuration, not code paths. An interface needing no enrichment disables phase 3. One needing two validation passes adds a row. The first and last phase are properties of the definition. This matters because the alternative, a fixed pipeline with feature flags, accumulates conditionals that nobody dares remove.
Outbound uses the same event model in reverse. The symmetry is deliberate: one monitoring screen, one retry policy language, one audit shape, one reporting spine for both directions.
Re-sending an event with the same identifier must never duplicate records on the partner's side. Every retry and replay mechanism in this design rests on that property, and the framework cannot supply it, only the adapter knows what “the same” means to that partner. We treat it as part of the adapter contract and test it explicitly.
The adapter pattern separates the façade that satisfies the framework from the logic that does the work. Three kinds:
| KIND | RESPONSIBILITY | VARIES BY |
|---|---|---|
| Format | Bytes to records and back | Encoding |
| Business | Domain logic for one interface object | Object type |
| Transport | Moving payloads across the boundary | Partner |
The separation pays for itself in three places. Business logic can be built and tested outside the framework, then wrapped. Several partners sending the same object share one business adapter behind different format and transport adapters. And an adapter can be replaced, a partner migrating from file transfer to an API, without the definition, the phases, the monitoring or the reporting changing at all.
Prefer to see the framework running instead of reading about it? Thirty minutes, on screen, nothing to prepare.
The synchronous processor runs the whole phase chain inline and returns the outcome to the caller, for interactive paths where somebody is waiting: a portal upload, a resubmission from the console, a partner API that expects a result rather than an acknowledgement.
The asynchronous processor handles everything else, and processes one phase per cycle.
One phase per cycle sets a latency floor. An event with six phases on a five-minute cadence takes up to thirty minutes to complete even when nothing fails.
This is why cadence is configured per interface and never globally. An interface that needs lower latency gets a shorter cycle or the synchronous processor. Designs that hide this arithmetic behind a single global scheduler setting produce a class of “the data is late” incident that nobody can explain.
Events are selected per business domain, so a backlog in one cannot starve another, ordered by priority and then by age. Priority bands run from critical to bulk; cadence tiers from one minute to one hour. Both are properties of the definition.
Concurrency is capped per domain and per partner. When a threshold is crossed, bulk work is deferred first. A per-partner circuit breaker holds events rather than repeatedly calling an endpoint that is already failing, which protects the partner as much as us, and is the difference between a degraded integration and an incident on someone else's system.
Failure handling is framework behaviour rather than per-integration code. That is the single largest source of consistency in the design, and the reason the portfolio has one operational policy instead of forty.
Retrying a transport timeout is sensible. Retrying a payload that failed schema validation is not, it will fail identically, and the only effect is to delay the notification to the person who can fix it. The framework classifies failures and retries only the classes where repetition can succeed:
| CLASS | RETRIED | BECAUSE |
|---|---|---|
| Transport | yes | The endpoint may recover |
| Capacity | yes | Backpressure is temporary |
| Reference data | yes | The missing record may arrive |
| Format | no | The bytes will not change |
| Validation | no | The rule will not change |
| Credentials | no | Repetition risks lockout; alert immediately |
Thresholds are evaluated on error rate over a window, consecutive failures on one interface, queue depth, age of the oldest waiting event, service-level breach, and on the absence of an expected transmission. Severity is banded, with a minimum severity configurable per interface so a low-criticality feed does not page anyone at night.
That distinction is what makes the record worth keeping.
An event leaves a halted or quarantined state only through an action attributable to a person: a resubmission, a release from review, a forced closure. Each is recorded with the identity that performed it. A framework that quietly self-heals produces a clean dashboard and an audit trail that proves nothing.
Two properties hold across the whole path. Nothing is overwritten, raw records are append-only, and reprocessing supersedes rather than replaces. Everything carries a lineage identifier, so any curated record can be traced back through validation and parsing to the exact bytes that produced it.
The curated layer is deliberately small: six domain areas, product design, planning, procurement, manufacturing, logistics and finance. There is no seventh. Dashboards, briefs, scores and posture views are computed compositions over those six, not additional stores. Constraining this is what keeps lineage tractable; every additional durable store is another thing that can silently disagree.
Reporting divides cleanly into four questions with different audiences, different cadences and, importantly, different failure domains.
Error report, throughput and latency by interface, queue depth and backlog age, retry and forced-closure activity, service-level attainment, alarm history. The error report is complete rather than incremental, every open error, not only those since the last run. A report showing only new failures lets a persistent one fall quietly out of view, which is exactly the failure mode reporting exists to prevent.
Insights, executive reporting, dashboards and generated visualisations over the six domain areas. Report definitions live on the server; callers select a report and supply parameters. No caller submits query text, a constraint that costs some flexibility and buys a query surface that can be reasoned about, authorised and audited.
Promotion pass and fail by interface, rejected records by reason, field mapping coverage, completeness and conformance, duplicate detection, freshness by domain area, plus lineage traversal in both directions. Mapping coverage is the one people underestimate: it answers “which fields is this partner sending that we are silently ignoring?”, which is usually where the next data-quality surprise is already waiting.
Sent, acknowledged and promoted counts per interface per cycle; value and quantity totals; a break report with ageing; duplicate detection; and detection of transmissions that never arrived.
A transmission that never arrives raises no error, because no processing was attempted. Every monitoring approach based on observing failures is blind to it.
Detecting it requires an expectation to compare reality against, which is why cadence belongs to the interface definition rather than to a scheduler's configuration. The system knows a nightly feed is nightly, and can therefore notice a night when nothing came.
Operational reporting draws on the event store and the audit record, not on the curated layer. This is intentional. The reports needed to diagnose an outage must not share a failure domain with the thing that is out.
Fourteen screens in five groups, organised on one principle: configuration is separated from operation, and both from administration. The person watching today's traffic and the person changing how an interface behaves are usually different people with different authority, and conflating their screens is how production configuration gets changed at three in the morning by someone trying to clear a backlog.
Two screens carry most of the daily load. The event monitor is the searchable list, filters and saved views, because an operator watching one partner should not rebuild the same filter every morning. The event detail view shows the phase timeline with timings and results, the payloads at each stage (request, response, log, output), and the actions: retry a phase, retry from a phase, re-queue, force close. Every action is authority-gated and recorded.
The lineage explorer answers the question that arrives by email rather than by alert: where did this number come from? Enter a curated record, get back the validation result, the raw record, the payload and the originating transmission. Enter an event identifier, get everything it produced.
The framework handles commercially sensitive and, in some deployments, classified information belonging to organisations that are competitors of one another. The controls below are properties of the architecture rather than configuration applied afterwards.
Tenancy is the control most often defeated in multi-tenant systems, because a single trusted header anywhere reduces everything downstream to decoration.
Tenant context is resolved once, at the boundary, from the verified identity of the caller, never from a request header, parameter or payload field, and nothing downstream may override it. People authenticate with hardware-backed credentials; partner systems authenticate with per-partner certificates, so a compromised credential for one partner cannot act as another.
Classification is assigned at ingress and inherited by everything derived from the data: raw record, curated record, report, export. The highest label wins on aggregation. A record whose label is missing is treated as the most sensitive and access is refused rather than granted, the safe direction to fail.
The audit record is append-only and hash-chained, so modification is detectable. The acting identity comes from the authenticated principal, never from a field in the request. And if the audit sink cannot be written, the action is refused rather than performed unrecorded, an action that cannot be recorded did not happen.
Data is encrypted in transit on every hop, including internal traffic, and at rest across the payload vault, the event store and both data layers, with field-level protection for the most sensitive attributes and keys held in hardware. Payload retention is set per payload type with destruction by key destruction, payloads are raw, pre-validation partner content and they accumulate, which makes retention a control rather than a housekeeping setting.
Organisations arriving at this design usually have working integrations already. The migration that succeeds is incremental: stand the framework alongside the existing paths, move one interface at a time, and retire each old path only once its replacement has run clean for a defined period. Both can write to the same data layers, so they coexist without conflict.
One thing cannot coexist. Once promotion into the curated layer belongs to a phase, any other route into that layer must close. Two paths writing the same data under different validation is precisely the condition the framework exists to eliminate, and leaving the old one open forfeits the lineage guarantee for everything.
If only the first stages are ever built, build these: an immutable payload store and an event identifier. Everything else in this paper, validation, reporting, reconciliation, lineage, is answerable later against payloads that were kept. None of it is answerable against payloads that were not.
A new partner is a definition and an adapter. Not a new pipeline.
This paper describes architecture, not a deployment. Specific obligations for a given programme, classification handling, accreditation, national security requirements, are determined by the relevant authority and the contract, and are addressed separately for each engagement.
It is a single entry and exit point through which every transmission between Skansar and an external system passes, whatever its source, format or transport. Parsing, validation, retry, throttling, notification and audit are shared by every integration instead of being rebuilt per partner.
A point-to-point integration works for the first five partners and becomes the dominant engineering cost somewhere around the fifteenth. Each one carries its own format, transport, identifier conventions and failure modes, so operational knowledge lives in code rather than in configuration, and nobody can answer basic questions across the estate.
The event. Every transmission, inbound or outbound, becomes an event with an identity, a definition that describes what should happen, a stored raw payload and a recorded outcome. Definitions describe intent, events record fact, and changing a definition never rewrites history.
Retry is bounded and typed: transient conditions are retried automatically on a backoff, and conditions that cannot be fixed by repetition are quarantined for a human instead. Movement out of quarantine is deliberate and recorded, so no failure disappears quietly and no failure is retried forever.
No. Raw payloads are stored immutably and curated domain data is promoted from them through validation, so every curated figure can be traced to the payload it came from. Identity is established at the boundary, tenancy is enforced on every read and write, and the record of what arrived and what was sent back is tamper-evident.
TECHNICAL WHITEPAPER V1.0 · PUBLISHED AUGUST 2026
This paper describes the framework as built, and marks the parts that are specified rather than shipped where they appear. For the layers that sit above the interface, meaning, relationships, context and decision memory, see the Skansar AI Architecture paper. For the regulatory side of the same problem, see the EDIP and the 35% rule guide.
Thirty minutes: how data reaches you today, Skansar on screen against data shaped like yours, then closing remarks and next steps. Nothing to prepare and no data needed for the call.