7.1 KiB
Raw output — next-gen tpt-backend implementation plans
Generated planning docs
git diff main..ng_tpts_planning -- ai/tpt-backends/
Five markdown docs. 00_shared_backend_contract.md is normative and the other three are written against it so they can be worked independently:
00_shared_backend_contract.md— the backend duck-type (<Proto>Address(msgspec.Struct, frozen=True)+ module-levelstart_listener()/close_listener()+ aMsgpack<Proto>Stream(MsgpackTransport)), theinspect.getmodule(self.addr)reflection inEndpoint.start_listener()that forces the Address class and its listener fns to share a module, a 10-item registration checklist, the dep policy, the test-harness shape, and §1.1’s proto-key conclusion (below).01_tipc_backend.md— service addressing viaTIPC_ADDR_NAMESEQ(bind/publish) andTIPC_ADDR_NAME(connect/lookup),TIPC_TOP_SRVtopology subscriptions as a push-based registry, theget_random()instance-collision hazard, and a step-0 capability-probe spike.02_quic_iroh_backend.md—irohoveraioquic/quiche/trio-asyncio, a_uniffi_trio.pybridge built onTrioToken.run_sync_soon(),trio.abc.Listener/HalfCloseableStreamadapters, and an API-truth table to fill in during step 0.03_wg_tunnel_bindspace.md—wgas a bindspace rather than aMsgTransport, aTunnelledAddresswrapper delegating.proto_key/.unwrap()to.inner,pyroute2for layer B, and@acm-managed netns/iface for layer C.README.md— index.
Generated example code
git diff main..ng_tpts_planning -- examples/multihost/wg_lan/
wg_maddr.py—WGTunnelledAddr(msgspec.Struct, frozen=True)carryingbearer: tuple[str, int],peer_pubkey: str,inner: tuple[str, int],inner_proto: Literal['tcp'], plus a.maddrproperty that re-renders the canonical form. Pure helpersmb_pubkey(),wg8_pubkey(),parse_wg_maddr(), and_segments()(with a marked stopgap for when thewgcodec isn’t installed).verify_wg_peer()is impure by design and kept out of the parse path.host_a_srv.py/host_b_client.py— the two-host runs; both pass onlyaddr.innertoopen_nursery()/open_root_actor().README.md— grammar, owner table,#108-branch install line, tunnel setup, “what changed vs #482”.
Verified findings (non-code, verbatim)
trio is address-family agnostic
Read against the installed trio. SocketStream/SocketListener ctor checks are only “is a trio sock object” + type == SOCK_STREAM, plus an OSError-suppressed SO_ACCEPTCONN probe. No AF_* check anywhere; TCP_NODELAY/TCP_NOTSENT_LOWAT are set under suppress(OSError). A TIPC SOCK_STREAM sock should therefore drop straight into trio.serve_listeners() with the existing MsgpackTransport framing, making TIPC mostly table-registration boilerplate w/ zero new deps.
the wg maddr grammar — /wg/ is infix, not suffix
Installed baudco/py-multiaddr@wg_support (PR multiformats/py-multiaddr#108) into a throwaway venv and round-tripped every candidate form:
| maddr | [p.name for p in m.protocols()] |
|---|---|
/ip4/1.2.3.4/udp/51820/wg/u<k> |
['ip4','udp','wg'] |
/ip4/../udp/../wg/u<k>/ip4/../tcp/.. |
['ip4','udp','wg','ip4','tcp'] |
/ip4/10.0.11.1/tcp/1616/wg/u<k> |
['ip4','tcp','wg'] |
/ip4/192.168.1.50/udp/51820/wg/u<A_pub>/ip4/10.0.11.1/tcp/1616
\_______ bearer __________/\__ key __/\______ overlay ______/
Segments before /wg/ are the bearer — the underlay (ip, udp-port) that wg(8) itself listens on (ListenPort). Segments after are the overlay endpoint, the only part tractor binds. The third row above is #482’s original suffix form: it parses, but is semantically inverted.
Three parts, three owners — and only one is an Endpoint:
| part | bound by | in the runtime? |
|---|---|---|
| bearer | kernel, via wg-quick/pyroute2 |
no |
/wg/u<key> |
nothing — an identity | no, verified out-of-band |
| overlay | tractor’s IPCServer |
yes, as .inner |
proto-key-tagged UnwrappedAddress
Shape-matching in wrap_address() does not survive four backends. TIPC’s natural unwrapped form is a (str, int), indistinguishable from TCPAddress; iroh’s is a (str, str), already swallowed by the existing UDS case (case (_, filename) if type(filename) is str). Ordering hacks and prefix-tagging only paper over it.
Recommended prerequisite for all three backends: carry an explicit proto-key spelled with the multiaddr protocol name — ('tcp', host, port), ('unix', path), ('tipc', stype, inst, scope) — so wrap_address() collapses to _address_types[addr[0]] and the collision class stops existing. This also makes the on-wire form agree with mk_maddr()/parse_maddr() instead of being an independent invention. It is a wire-format change (SpawnSpec, _root_mailbox, _registry_addrs) plus every fixture and downstream config, so it wants its own migration commit landed before any new backend — and it is the moment to stop handing raw tuples to users at all, making Address the public currency and UnwrappedAddress an internal serialization detail (the discipline ipaddress uses).
netns is a runtime-level config API
setns(2) affects the calling thread only and does not move already-created sockets. So a netns is a spawn/boot-time input alongside enable_transports/tpt_bind_addrs, and there is deliberately no await actor.enter_netns(...) — a mid-life API would silently leave the IPC server bound in the old namespace. Corollary for layer B: pass netns= down to pyroute2 rather than assuming a trio.to_thread worker inherits it.
examples/ collection would have failed CI
tests/test_docs_examples.py walks examples/ recursively and subproc-runs every collected file asserting rc == 0. Its filter never checks the extension, so all four wg_lan files were collected — including README.md, which would have been run as python README.md. 'multihost' not in p[0] was already in the exclusion list with no directory using it. Moving the set under examples/multihost/wg_lan/ drops collection 24 → 20 with zero test changes; confirmed via pytest --collect-only.
Corrections applied during the session
The human corrected two claims that had been asserted without verification, both since retracted in-place in the docs and in the posted issue comments:
- that
wghas “nothing to bind at the tunnel layer, exactly one bind” — wrong; a wg stack is genuinely composed, and the real axis is who owns each layer’s endpoint. - that
/ip4/../udp/443/quic-v1was “also composed” — wrong; that is one endpoint with a protocol qualifier, not a tunnel.
A self-review before publication also downgraded two overconfident claims to explicitly-flagged assumptions: the uniffi-uses-asyncio-only-as-executor thesis (contradicted that plan’s own “do not guess from memory” step 0) and TIPC’s duplicate-binder round-robin behaviour (unverified).