# ADR 0011: Cross-transport document bridging via TransportManager

**Status:** Accepted
**Date:** 2026-04-29
**Canonical ADR:** [`peat/docs/adr/059-cross-transport-document-bridging.md`](https://github.com/defenseunicorns/peat/blob/main/docs/adr/059-cross-transport-document-bridging.md)

---

## Pointer

The architectural decision lives in the `peat` repo, where it was authored
and reviewed alongside the broader protocol-level ADR set. This file
exists as a peat-mesh-local reference so that `docs/adr/` stays
self-consistent — when a peat-mesh contributor finds `Translator`,
`TransportManager` fan-out, `ChangeEvent.origin`, or
`TranslationContext.collection` in the source and asks "what's the
ADR?", they can find it from this repo's own ADR index.

## Summary

ADR-059 (in the peat repo) lifts per-transport translator patterns
(TAK in ADR-029, BLE in ADR-041) into a generic `Translator` trait
owned by peat-mesh. `TransportManager` (peat-mesh ADR-0004) is
extended from a transport selector into a fan-out orchestrator: it
observes `Node::observe` once per collection and routes each
`ChangeEvent` through every registered translator's `encode_outbound`,
into per-transport bounded mpsc channels.

## In peat-mesh, the ADR ships in slices:

- **Slice 1.a** (PR #87 — merged): `Translator` trait, `TranslationContext`,
  `Node::publish_with_origin`, `ChangeEvent.origin`, `TransportManager`
  observer-driven fan-out, `OutboundSink` + `QosDropPolicy` +
  `TailDropPolicy`, per-transport `TranslatorRegistrationConfig`,
  lifecycle (parent/child cancellation tokens).
- **Slice 1.b.1** (PR #88 — this file lands here): `TranslationContext.collection` +
  `ChangeEvent::Initial.collection` for codec dispatch.
- **Slice 1.b.2** (peat repo): peat-protocol's `BleTranslator`
  implements `Translator`; peat-ffi adds the generic
  `OutboundFrameCallback` JNI + UniFFI surface.
- **Slice 1.c** (peat-atak-plugin repo): plugin's `BleOutboundCallback`
  dispatcher.
- **Slice 1.d** (peat-sim repo): `track_listener` publishes self-presence.
- **Slice 1.5** (peat repo): TAK adapter migrated as the trait-stability gate.
- **Slice 2** (cross-repo): `allowed_transports` field, `Query::AllowedTransport`,
  delete propagation, drain-task reaper, channel coalescing for
  bandwidth-constrained transports.

For the full design — invariants, schema impact, codec contract,
multi-hop loop prevention, QoS-aware drop policy, per-transport
channel sizing rationale, alternatives considered — see the canonical
ADR linked above.

## Addendum: sync-delivery liveness is a per-transport invariant

peat-mesh#226 surfaced that sync-delivery liveness — *a local CRDT change
reaches every reachable peer in bounded (seconds) time, and a half-open/stale
channel is detected and pending state re-delivered on reconnect within
seconds, not tens of seconds* — was enforced only on the iroh/QUIC transport,
and even there was **omitted** on the `with_iroh` sync endpoint (no QUIC
keepalive ⇒ a half-open connection was detected only by the 30 s app-level
`SyncChannel::RECV_TIMEOUT`). The fix applies `create_tactical_transport_config()`
(1 s keepalive / 5 s `max_idle_timeout`) to that endpoint.

This invariant is **transport-agnostic** and binds every transport registered
with the dual-active `TransportManager`: each supplies its own liveness
primitive (QUIC keepalive for iroh; BLE link-supervision + `peer_lifetime` /
`reconnect` for BLE/GATT in peat-btle), and a transport-specific primitive must
**not** be pushed into the transport-agnostic layer. The canonical statement is
proposed as an amendment to peat `docs/adr/063-persistent-sync-streams.md`.

Tracking: peat-mesh#231 (invariant + work breakdown), peat-btle#73 (verify the
BLE/GATT reconnect → pending-state catch-up).
