The Disconnected Edge: How We Solved In-Flight Data Sync at 35,000 Feet

Most application architectures assume the network is available often enough to repair mistakes: fetch fresh configuration, retry an API call, consult a central database, or stream a missing asset from a CDN.
An aircraft edge system does not get that safety net. For long stretches, the onboard platform must operate as if the backend does not exist. When a connection appears, it may be short, expensive, slow, or interrupted halfway through a transfer.
That changed how we designed an in-flight entertainment platform. The hard part was not serving movies, games, catalogs, and passenger experiences locally. The hard part was moving the right data between a central backend and intermittently connected aircraft without leaving either side in an ambiguous state.
This is the offline-first model we used: make the aircraft independently useful, treat synchronization as a deliberate protocol rather than a background convenience, and make every partial failure recoverable. Specific identifiers and thresholds here are illustrative; the design principles are the point.
The operating environment: disconnected by default
Each aircraft carried an embedded edge system responsible for the passenger experience: media, applications, digital publications, catalogs, configuration, and operational telemetry. It had local storage and local services, but no guarantee of a usable path to the internet.
That creates constraints that are easy to underestimate from a cloud-first mindset:
- Connectivity windows are intermittent and may end without warning.
- Bandwidth can be scarce, variable, and costly.
- Devices must keep serving known-good content while updates are incomplete.
- A reboot, power event, or failed transfer cannot corrupt the active passenger experience.
- Central systems must distinguish “not yet uploaded” from “lost forever.”
The backend remained authoritative for centrally managed content, configuration, and policy. But the aircraft had to be operationally autonomous. Offline mode was not a degraded fallback; it was the normal mode.
Separate the data lanes before designing sync
“Sync everything” is not a protocol. Different kinds of data have different correctness rules, sizes, and priorities. We treated them as separate lanes:
- Content: large immutable media and application assets. Correctness means every byte matches a known version.
- Configuration: smaller, product-sensitive settings. Correctness means a complete, compatible version is activated atomically.
- Telemetry and analytics: append-only events generated onboard. Correctness means no silent loss and no harmful double counting.
- Operational commands: centrally issued intent, such as a requested content set. Correctness means explicit acknowledgement and an auditable lifecycle.
Once the lanes were separated, we could give each one a suitable delivery, retry, and conflict model instead of forcing all data through one generic “sync” abstraction. It also gave the scheduler useful priorities: a small compatible configuration update can be more valuable than the next chunk of a large optional asset.
Make sync a persisted state machine
A sync worker that only lives in memory is fragile. It forgets why it stopped after a reboot, cannot distinguish a paused transfer from a failed one, and makes recovery dependent on log archaeology. We modeled synchronization as a small persisted state machine.
idle → discovering → planning → transferring → verifying
↑ ↓
└──── paused / retry_wait ← activating ← ready
↓
quarantined
The exact labels are less important than the invariant: every transition is durable and restartable. The device records the active release, requested release, manifest version, compatibility result, artifact/chunk progress, retry schedule, event-upload cursor, and the reason for any terminal failure. On startup, the worker reads that state and resumes conservatively rather than guessing from partial files.
{
"activeRelease": "release-2026-05",
"desiredRelease": "release-2026-06",
"syncState": "transferring",
"manifestHash": "...",
"verifiedBytes": 734003200,
"eventCursor": 817291,
"nextRetryAt": "2026-06-13T08:15:00Z"
}
Persisted state also creates a useful boundary between mechanism and policy. The transfer mechanism knows whether a chunk is verified. Policy decides whether to retry now, defer a nonessential asset, quarantine a release, or ask an operator to investigate.
Versioned manifests made content deterministic
For content and application bundles, we avoided asking the device to infer what changed from a directory listing. The backend produced an immutable, versioned manifest describing the desired release: asset identifiers, sizes, hashes, dependencies, compatibility requirements, and configuration version.
{
"releaseId": "ife-2026.06.13",
"minPlatformVersion": "4.8.0",
"schemaVersion": 3,
"assets": [
{
"path": "movies/example.mp4",
"sha256": "…",
"bytes": 1789423412
}
],
"configVersion": "cfg-2026-06-13-02"
}
An edge device first fetched the small manifest, checked signature and compatibility, compared it with local state, and then downloaded only missing or changed artifacts. Every artifact was verified before it could be considered ready. A successful HTTP response was not proof of correctness; the expected hash was.
Large files were transferred in chunks with persisted progress. A connection loss simply paused work at the latest verified boundary. On the next window, the device resumed instead of starting again.
for each required asset:
read verified chunk offsets from local state
request missing ranges
verify each completed chunk
verify final asset hash
mark asset ready only after verification
Compatibility is part of correctness. A manifest can demand a platform version, a schema version, or a migration path that the device does not support. In that case the worker must reject the release explicitly and report why; silently applying a newer structure to an older runtime creates harder failures later.
Never activate a half-synced release
Downloading a release and serving it are separate operations. New artifacts were staged outside the active content set. Only when the entire manifest was present, verified, compatible, and accompanied by a valid configuration did the device switch the active pointer in one durable operation.
BEGIN TRANSACTION;
assert release is complete and verified;
assert configuration is compatible;
set active_release = :releaseId;
record activated_at = :timestamp;
COMMIT;
This is the key recovery property: a failed or interrupted sync leaves the previous known-good release active. The next sync can resume staging. It never turns an incomplete directory into the passenger experience.
Rollback followed the same model. Retaining a previous verified release made recovery a pointer change rather than an emergency re-download. The device reported both its desired release and active release so the backend could distinguish “download in progress” from “activation failed.”
An end-to-end sync window
A typical connection window followed a deliberately boring sequence. First, the device authenticated and sent a compact status summary: software version, active release, desired-release status, storage pressure, event cursor, and the outcome of the prior attempt. The backend replied with policy and the latest eligible manifest.
Next, the device planned work. It verified that the release was compatible and that there was enough staging capacity. It then prioritized small metadata, critical configuration, and pending commands before large content artifacts. Meanwhile, telemetry upload ran in bounded batches so that a large backlog could not starve a critical update, and a big download could not starve telemetry indefinitely.
During transfer, every request had a deadline. Completed ranges were recorded only after verification. If the connection disappeared, the worker retained its exact state and backed off until the next viable attempt. If all assets passed verification, activation was a separate, short transaction. Finally, the device sent an acknowledgement containing the active release and any rejected or quarantined items.
That acknowledgement closed the loop. The backend could not infer success from having served a manifest. A release was operationally complete only after the device reported it active.
Upload events as an idempotent append-only stream
Telemetry and passenger analytics are fundamentally different from content. They are produced locally while disconnected, then uploaded later. The edge system persisted events before attempting delivery and assigned each event a stable identity, such as a device id plus a monotonic sequence number or a generated UUID.
{
"deviceId": "aircraft-edge-42",
"sequence": 817292,
"eventId": "01J…",
"type": "content_started",
"occurredAt": "2026-06-13T08:12:24Z",
"payload": { "contentId": "movie-123" }
}
The backend accepted batches idempotently. If a connection failed after the server accepted a batch but before the aircraft received acknowledgement, retrying the same events was safe because the backend could recognize identities it had already processed.
Server acknowledgements advanced a durable cursor only after accepted events were recorded. The edge node retained data until that acknowledgement was committed, then compacted acknowledged records according to retention policy. This produces at-least-once transport with effectively-once accounting when the consumer deduplicates by event identity.
Not every failure deserves an infinite retry. A temporarily unavailable endpoint may be retried with backoff. A malformed event, unsupported schema, or permanently rejected payload should move to a quarantine or dead-letter record with a reason, preserving evidence without blocking the entire queue. Operators can inspect, repair, discard, or replay it through an explicit process.
Conflicts need a taxonomy, not a universal CRDT
Offline systems do create conflicts, but “use CRDTs” is not a complete answer. A conflict policy should follow ownership and business semantics.
- Server-authoritative data: release manifests, pricing policy, and centrally managed configuration should have a single authority. The device applies a compatible version; it does not merge edits.
- Append-only facts: telemetry is normally merged by deduplication, ordering metadata, and domain-specific aggregation-not by overwriting records.
- Locally authored mutable state: if multiple offline writers can independently edit the same logical object, a CRDT may be appropriate when its merge semantics match the product. Counters, sets, and collaborative metadata are possible examples.
- Irreconcilable changes: some domains require explicit rejection or human review. Last-write-wins is a policy choice, not a conflict-resolution strategy.
CRDTs are valuable because they can guarantee convergence under particular operations and merge rules. They do not create correct business semantics automatically. For most centrally controlled data, immutable versions plus server authority were easier to reason about and audit.
Bandwidth, storage, and rollout policy
A device did not attempt every task whenever a network interface looked available. It assessed the connection, applied policy, and worked through a priority queue. Small manifests and critical configuration came first; event uploads and large optional artifacts were scheduled according to remaining budget and product priority.
Transfers used bounded concurrency, request deadlines, exponential backoff with jitter, and persisted state. Retrying blindly can consume an entire connectivity window, so retries were capped and re-evaluated when conditions changed. Each request carried enough identity to make retries observable and safe.
Storage required policy too. The active release and a known-good rollback release were protected from normal eviction. Incomplete staging content could be removed safely when it no longer matched the desired manifest. Optional, verified assets could be evicted only according to explicit rules, never based on a generic filesystem cleanup that might remove an activation dependency.
Rollouts were also progressive. A new release should first be eligible for a limited ring of devices, then expand only when acknowledgements, validation results, and operational signals remain healthy. A spike in compatibility failures, verification errors, activation failures, or abnormal backlog growth should pause expansion. The design needs a clear rollback path before a release is ever offered to a wider fleet.
Security, credentials, and privacy at the edge
An offline device still needs a trust model. Transport encryption protects an available connection, but it does not prove that a downloaded artifact is an approved release. Manifests and artifacts need integrity verification; depending on the threat model, signed manifests provide stronger provenance than hashes fetched from the same channel as the content.
Devices should authenticate with distinct, scoped, revocable identities. Credential rotation needs a tolerable overlap period: the device validates a replacement credential, records it durably, and retains the old one only long enough to avoid being stranded during an interrupted rotation. Revocation and expiry must be visible in device state and backend operations.
Data minimization matters as much as transport security. Sync only data the aircraft needs to operate, collect only telemetry necessary for product and operational use, and define retention boundaries for local queues and backend ingestion. Avoid logging access tokens, passenger-sensitive data, or complete event bodies when metadata will do. Backend authorization should scope every request to the device and fleet it is permitted to access.
Operations: measure partial progress
Intermittent systems need better observability than a simple “online” metric. We tracked release state, manifest version, bytes remaining, verified chunks, last successful contact, event backlog age, upload acknowledgement cursor, retry counts, active release, failed validation reasons, and storage pressure.
Those signals made it possible to answer useful operational questions: which devices are running an old release, which are stuck on a corrupt artifact, which have an increasing analytics backlog, and whether a rollout problem is global or isolated to a connection path.
The health objective is not constant connectivity. It is eventual convergence without harming the active experience: a device should remain useful on a known-good release, make measurable progress when a connection exists, and surface failures that need intervention. Recovery paths were deliberate: stale transfer leases could be reclaimed, incomplete staging directories could be cleaned safely, old verified releases could be rolled back to, and event batches could be replayed without double counting.
Test the failure modes, not only the protocol
The system needed tests for conditions that are rare in a development environment but routine at the edge:
- Connection loss during manifest retrieval, range download, upload, and acknowledgement.
- Process restart or power loss before and after an active-release pointer change.
- Duplicate event batches, reordered responses, and stale acknowledgements.
- Corrupted chunks, incompatible manifests, expired credentials, and full disks.
- Clock drift, long offline periods, and recovery after a device misses multiple releases.
- Stale locks, interrupted credential rotation, and release rollback during an active sync.
Fault injection was more useful than happy-path tests alone. We simulated slow links, short connections, partial range responses, duplicate uploads, backend timeouts, and storage exhaustion. The invariant under test was simple: after any interruption, the device must either continue serving the last verified release or recover to a well-defined state without inventing completion.
What changed in our design process
Building for aircraft made us stop treating the network as a dependable dependency. The device had to remain useful with a stale but verified local state. The backend had to accept delayed, duplicate, and partial communication without losing its understanding of the fleet.
That led to a few durable rules:
- Make offline operation a first-class product requirement.
- Persist sync state; never rely on a process remembering where it was.
- Use immutable manifests and hashes for large artifacts.
- Stage, verify, and atomically activate releases; retain a known-good rollback target.
- Persist outbound events and make server ingestion idempotent.
- Define conflict policy by ownership and domain semantics; use CRDTs only where their merge model fits.
- Prioritize work intentionally across bandwidth and storage constraints.
- Measure partial progress, backlog age, and recovery-not only availability.
Conclusion
Offline-first synchronization is not a smaller version of cloud sync. It is a distributed-systems problem where partitions are normal and recovery must be designed before the failure happens.
For an in-flight edge platform, immutable releases, resumable verified transfer, atomic activation, idempotent event delivery, explicit conflict rules, and clear operational state made the system dependable even when connectivity was not. Those patterns apply far beyond aircraft: ships, retail stores, factories, field devices, and any product that must keep working when the network disappears.

