tractor/ai/prompt-io/claude/20260813T001102Z_27c34aeb_p...

7.1 KiB
Raw Blame History

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-level start_listener()/close_listener() + a Msgpack<Proto>Stream(MsgpackTransport)), the inspect.getmodule(self.addr) reflection in Endpoint.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.1s proto-key conclusion (below).
  • 01_tipc_backend.md — service addressing via TIPC_ADDR_NAMESEQ (bind/publish) and TIPC_ADDR_NAME (connect/lookup), TIPC_TOP_SRV topology subscriptions as a push-based registry, the get_random() instance-collision hazard, and a step-0 capability-probe spike.
  • 02_quic_iroh_backend.mdiroh over aioquic/quiche/trio-asyncio, a _uniffi_trio.py bridge built on TrioToken.run_sync_soon(), trio.abc.Listener/ HalfCloseableStream adapters, and an API-truth table to fill in during step 0.
  • 03_wg_tunnel_bindspace.mdwg as a bindspace rather than a MsgTransport, a TunnelledAddress wrapper delegating .proto_key/.unwrap() to .inner, pyroute2 for 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.pyWGTunnelledAddr(msgspec.Struct, frozen=True) carrying bearer: tuple[str, int], peer_pubkey: str, inner: tuple[str, int], inner_proto: Literal['tcp'], plus a .maddr property that re-renders the canonical form. Pure helpers mb_pubkey(), wg8_pubkey(), parse_wg_maddr(), and _segments() (with a marked stopgap for when the wg codec isnt 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 only addr.inner to open_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 #482s 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 tractors IPCServer yes, as .inner

proto-key-tagged UnwrappedAddress

Shape-matching in wrap_address() does not survive four backends. TIPCs natural unwrapped form is a (str, int), indistinguishable from TCPAddress; irohs 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:

  1. that wg has “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 layers endpoint.
  2. that /ip4/../udp/443/quic-v1 was “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 plans own “do not guess from memory” step 0) and TIPCs duplicate-binder round-robin behaviour (unverified).