Plan doc for gh #353. Picks `iroh` (the `uniffi` FFI pkg) over
`aioquic`/`quiche` bc node-id addressing + hole-punching + relay
fallback is the whole point; `aioquic` stays documented as the
fallback since ~90% of the adapters here are reusable against a
sans-io core.
Deats,
- the layering: iroh `Endpoint` per actor, `Connection` per peer
(pooled via `trionics.maybe_open_context()`, not a hand-rolled
cache), one bi-stream per `Channel`. 4-byte prefix framing
stays so `MsgpackTransport` is untouched.
- `_uniffi_trio.py`: uniffi only uses `asyncio` as the executor
for its rust-future poll loop, so a ~40-line
`TrioToken.run_sync_soon()` bridge replaces it. Spells out the
real hazards — strong ref on the `ctypes` trampoline, poll-code
propagation, and a *bounded* shielded cancel-drain so a wedged
rust future can't make an actor un-cancellable.
- `IrohAddress` w/ ALPN as the `.bindspace`, the `(str, str)`
unwrapped form's collision w/ the UDS match-case, and why
`get_root()` needs a persisted secret key -> a lazy
`default_lo_addrs()` + a pure-getter/explicit-setter split.
- `QuicMsgStream(trio.abc.HalfCloseableStream)` +
`QuicListener(trio.abc.Listener)`, incl. the exact
EOF/reset/use-after-close semantics `_transport.py` already
match-cases on, and hanging the acceptor tasks off the
existing `Endpoint.listen_tn`.
- a prep-PR boundary: annotation widening, the shared
`rebind_from_sockname` gate and a `tpt_key`-based
`transport_from_stream()` dispatch, all landable w/ tcp/uds as
the only backends.
Further, notes this is our first tpt w/ real transport security
+ peer auth, so an inbound node-id allowlist hook belongs here —
and that it says nothing about the other backends.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
Plan doc for gh #378, the cheapest new backend we can add: it's
stdlib-only (CPython ships `AF_TIPC` + 23 `TIPC_*` consts) and
per the contract doc `trio`'s stream/listener wrappers don't care
about the addr family, so `MsgpackTransport` framing and
`trio.serve_listeners()` are reused verbatim.
Deats,
- `TIPCAddress` as a *service name* `(type, instance)` w/ scope
as the `.bindspace`; `bind()` publishes the singleton
name-range, peers `connect()` by name and the kernel resolves
+ load-balances. I.e. registration/lookup for free, no
registrar in the loop.
- the self-tagging `('tipc:<stype>:<scope>', instance)` unwrapped
form + why it must be match-ordered before `TCPAddress`'s.
- `get_random()` via a blake2b digest of the actor id (there's no
`port=0` analogue) and the silent-crosstalk risk that follows:
TIPC *allows* dup binders and round-robins, so a collision
doesn't `EADDRINUSE`, it cross-talks.
- an `Address.rebind_from_sockname` ClassVar to opt out of
`Endpoint.start_listener()`'s `getsockname()` reconcile, which
for TIPC always returns a port-id, never the bound name.
- the `TIPC_TOP_SRV` topology-service subscription as an `@acm`
yielding a chan of typed name-table events — push-based
register/dereg, the real "end game cluster proto" bit.
- commit sequencing, hard capability gating (`modprobe tipc`;
bare `AF_TIPC` is `EAFNOSUPPORT` on a stock box), CI matrix
notes, risks + follow-up seeds.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
First doc of a new `ai/tpt-backends/` set: the normative
description of what a `tractor` tpt backend *is* as of `main`,
written so the 3 sibling plans (TIPC, QUIC, `wg`) can be worked
independently (by another model/provider) w/o design drift.
Deats,
- the backend duck-type as empirically derived from
`_tcp.py`/`_uds.py`: the `Address` protocol surface, the
mod-level `start_listener()`/`close_listener()` pair and
`Msgpack<Proto>Stream(MsgpackTransport)`.
- the ONE reflection you can't break:
`Endpoint.start_listener()` resolves the tpt mod via
`inspect.getmodule(self.addr)`, so an `Address` type and its
listener fns MUST live in the same mod.
- a 10-item registration checklist (`_address_types`,
`_key_to_transport`, `_addr_to_transport`, `wrap_address()`
match-cases, `TransportProtocolKey`, maddr tables, ..) incl.
the import-time `_default_lo_addrs` trap.
- where the `trio.SocketListener` assumption is *actually*
load-bearing (just the `getsockname()` reconcile) vs. merely
annotated.
- the handshake/discovery invariants a new backend inherits,
dep policy (extras + import-laziness per the #470 boot-latency
budget), `--tpt-proto` harness plumbing and code style.
Also, records a verified finding the plans lean on hard:
`trio.SocketStream`/`SocketListener` are addr-*family* agnostic
— the only ctor checks are "is a trio sock" + `SOCK_STREAM` (+
an `OSError`-suppressed `SO_ACCEPTCONN`) — so any `SOCK_STREAM`
family CPython can make drops into the existing
`trio.serve_listeners()` path unmodified.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))