Add TunnelledAddress and tagged address forms - #505
Open
goodboy wants to merge 8 commits into
Open
Conversation
This was referenced Aug 20, 2026
There was a problem hiding this comment.
Pull request overview
This PR advances Tractor’s addressing model by (1) adding native WireGuard (/wg/) multiaddr support via a TunnelledAddress wrapper that carries tunnel metadata locally while delegating wire serialization to the overlay transport address, and (2) migrating transport .unwrap() emission to canonical tagged tuples (e.g. ('tcp', host, port) and ('unix', path)) while preserving legacy input compatibility at decoding boundaries.
Changes:
- Add
tractor.discovery._tunnelwithTunnelledAddress+ WireGuard maddr parse/compose helpers, plus boundary utilities (strip_tunnels,tunnels_of). - Switch TCP/UDS address serialization to canonical tagged tuples and update discovery/multiaddr/IPC call sites + tests accordingly (retaining legacy pair inputs).
- Ensure tunnel wrappers are peeled at IPC transport boundaries (server listen + channel connect), and update the multihost WG example and docs.
Reviewed changes
Copilot reviewed 39 out of 40 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Adds py-multibase to the lock to support WG key encoding/decoding. |
| tractor/msg/types.py | Loosens spawn address tuple typing to accommodate protocol-specific tagged shapes validated at boundaries. |
| tractor/ipc/_uds.py | Updates UDS unwrapped form to canonical ('unix', path) and broadens decoding to tagged/legacy inputs. |
| tractor/ipc/_tcp.py | Updates TCP unwrapped form to canonical ('tcp', host, port) and adjusts callers that previously unpacked (host, port). |
| tractor/ipc/_server.py | Broadens accepted listen address declarations and peels tunnel wrappers before Endpoint construction. |
| tractor/ipc/_chan.py | Accepts tunnelled declarations and strips tunnels immediately before transport dispatch/connect. |
| tractor/discovery/_tunnel.py | New module implementing WG tunnel spec, tunnelled address wrapper, WG maddr parsing/composition, and peeling helpers. |
| tractor/discovery/_multiaddr.py | Adds WG-aware parsing/composition and updates TCP/UDS handling to the tagged unwrap contract. |
| tractor/discovery/_addr.py | Introduces canonical tagged address aliases, updates wrapping/dispatch for tagged+legacy forms, and recognizes TunnelledAddress as wrapped. |
| tractor/discovery/init.py | Re-exports tunnel and WG helpers from the discovery public API. |
| tractor/_root.py | Strips tunnels when allocating random bind addrs derived from contacted registry addresses. |
| tests/test_local.py | Updates assertions to compare canonical tagged unwrapped addresses. |
| tests/ipc/test_server_tunnel_boundary.py | New regression ensuring tunnel wrappers are peeled before Endpoint reflection/dispatch. |
| tests/ipc/test_each_tpt.py | Updates runtime registry address comparisons to canonical tagged serialization. |
| tests/ipc/test_channel_tunnel_boundary.py | New regression ensuring tunnel wrappers are peeled before outbound transport lookup/connect. |
| tests/discovery/test_tunnelled_addr.py | New tests for delegation semantics, peeling, tunnel enumeration, and serialization behavior. |
| tests/discovery/test_tpt_bind_addrs.py | Updates TCP port assertions and disjointness checks for tagged unwrapped tuples. |
| tests/discovery/test_multiaddr.py | Expands coverage for WG parsing/composition + updates TCP/UDS expectations to tagged unwrap shapes. |
| tests/discovery/test_address_serialization.py | New focused tests for canonical tagged decoding and legacy compatibility. |
| pyproject.toml | Pins unreleased WG codec revision for multiaddr in PEP 621 metadata and adds py-multibase; enables direct references for Hatch. |
| examples/multihost/wg_lan/wg_maddr.py | Removes duplicate parser; keeps only explicit impure WG peer verification using production parser types. |
| examples/multihost/wg_lan/README.md | Updates documentation to match production tunnel wrapper model and dependency contract. |
| examples/multihost/wg_lan/host_b_client.py | Migrates demo client to TunnelledAddress + updated transport enabling via overlay proto. |
| examples/multihost/wg_lan/host_a_srv.py | Migrates demo server to TunnelledAddress + mk_maddr printing; adjusts key/bearer access. |
| ai/tpt-backends/03_wg_tunnel_bindspace.md | Updates plan documentation to reflect implemented layer-A behavior and refined layer-C capability model. |
| ai/tpt-backends/00_shared_backend_contract.md | Clarifies bindspace semantics vs. process namespace lifecycle (plan-03). |
| ai/prompt-io/opencode/20260820T033108Z_ba07e09d_prompt_io.raw.md | Records agent prompt I/O for the tagged-writer migration step. |
| ai/prompt-io/opencode/20260820T033108Z_ba07e09d_prompt_io.md | Summarizes that prompt I/O session (writers). |
| ai/prompt-io/opencode/20260820T033107Z_ba07e09d_prompt_io.raw.md | Records agent prompt I/O for tagged-reader compatibility work. |
| ai/prompt-io/opencode/20260820T033107Z_ba07e09d_prompt_io.md | Summarizes that prompt I/O session (readers). |
| ai/prompt-io/opencode/20260820T021516Z_dfad66a0_prompt_io.raw.md | Records prompt I/O for bindspace capability design doc updates. |
| ai/prompt-io/opencode/20260820T021516Z_dfad66a0_prompt_io.md | Summarizes that prompt I/O session (docs). |
| ai/prompt-io/opencode/20260819T213145Z_f81fc5e5_prompt_io.raw.md | Records prompt I/O for inbound tunnel boundary integration. |
| ai/prompt-io/opencode/20260819T213145Z_f81fc5e5_prompt_io.md | Summarizes that prompt I/O session (inbound boundary). |
| ai/prompt-io/opencode/20260819T213144Z_f81fc5e5_prompt_io.raw.md | Records prompt I/O for outbound tunnel boundary integration. |
| ai/prompt-io/opencode/20260819T213144Z_f81fc5e5_prompt_io.md | Summarizes that prompt I/O session (outbound boundary). |
| ai/prompt-io/opencode/20260818T075031Z_dd02c7c0_prompt_io.raw.md | Records prompt I/O for WG multiaddr integration batch. |
| ai/prompt-io/opencode/20260818T075031Z_dd02c7c0_prompt_io.md | Summarizes that prompt I/O session (WG integration). |
| ai/prompt-io/opencode/20260818T021729Z_d9a6e2e9_prompt_io.raw.md | Records prompt I/O for tunnelled-address handoff completion. |
| ai/prompt-io/opencode/20260818T021729Z_d9a6e2e9_prompt_io.md | Summarizes that prompt I/O session (handoff completion). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
963
to
970
| async def listen_on( | ||
| self, | ||
| *, | ||
| accept_addrs: list[tuple[str, int|str]]|None = None, | ||
| accept_addrs: list[ | ||
| UnwrappedAddress|Address|TunnelledAddress | ||
| ]|None = None, | ||
| stream_handler_nursery: Nursery|None = None, | ||
| ) -> list[Endpoint]: |
TunnelledAddress and tagged address forms
35 tasks
goodboy
force-pushed
the
wkt/addr_unpacking
branch
from
August 28, 2026 19:18
5d92595 to
7d0b84e
Compare
goodboy
force-pushed
the
ng_tpts_planning
branch
from
August 28, 2026 19:18
d9a6e2e to
85a4458
Compare
goodboy
force-pushed
the
wkt/addr_unpacking
branch
from
August 30, 2026 02:01
7d0b84e to
5b606ba
Compare
goodboy
force-pushed
the
ng_tpts_planning
branch
from
August 30, 2026 02:01
85a4458 to
4730b4c
Compare
Introduce the first layer-A address type from the `wg` bindspace plan without treating a transparent tunnel as a `MsgTransport`. Deats, - add frozen `WGTunnelSpec` and `TunnelledAddress` structs which delegate proto identity, bindspace, validity and wire serialization to their overlay - add `strip_tunnels()` and `tunnels_of()` for nested wrappers - recognize wrappers in `is_wrapped_addr()` while keeping them out of `_address_types` - cover delegation, namespace fallback and nested peeling semantics Also, - widen `Address.namespace` ids for named netns - export the new discovery API - clarify that tractor's layer-C bindspace lifecycle may provision the kernel-owned bearer without making it a `MsgTransport` Prompt-IO: ai/prompt-io/opencode/20260818T021729Z_d9a6e2e9_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Teach discovery to preserve WireGuard bearer and identity metadata around a bindable TCP overlay. Deats, - encode `wg(8)` keys as strict 32-byte multibase values - peel nested stacks with `Multiaddr.decapsulate_code()` and compose them with `.encapsulate()` instead of splitting strings - integrate wrappers with `parse_maddr()`, `mk_maddr()`, `wrap_address()` and `parse_endpoints()` - pin the unreleased py-multiaddr#108 codec in package metadata - cover exact round trips, nesting, bad grammar and missing codecs Prompt-IO: ai/prompt-io/opencode/20260818T075031Z_dd02c7c0_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Drop the example-local address struct and hand-rolled single-tunnel parser now that discovery owns the production implementation. Keep only the explicit `wg(8)` peer probe in the multihost helper, and update the examples and plan for nested parsing, packaged codec dependencies and tractor-owned bindspace provisioning. Prompt-IO: ai/prompt-io/opencode/20260818T075031Z_dd02c7c0_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Retain tunnel annotations through address declaration, then hand only the bindable overlay to exact-type transport lookup and dialing. Broaden `Channel.from_addr()` and `_connect_chan()` inputs accordingly, and cover plain plus tunnelled TCP dispatch arguments. Prompt-IO: ai/prompt-io/opencode/20260819T213144Z_f81fc5e5_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Carry tunnel declarations through listener configuration, then strip them immediately before constructing transport endpoints. Also allocate random listener addresses from a contacted registry's overlay, and prove a real TCP listener never stores the wrapper while the source declaration retains its bindspace metadata. Prompt-IO: ai/prompt-io/opencode/20260819T213145Z_f81fc5e5_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Separate serializable bindspace declarations from live namespace identity, FDs, ownership and teardown resources. Require child namespace entry during spawn bootstrap, before actor runtime initialization, then distinguish listen/dial provisioning and owned/borrowed cleanup without encoding operation role into maddrs. Prompt-IO: ai/prompt-io/opencode/20260820T021516Z_dfad66a0_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
- Define canonical `tcp` and `unix` tuple shapes while retaining legacy pair aliases as the emitted `UnwrappedAddress`. - Dispatch tagged tuple/list payloads explicitly, accept `uds` as a Unix input alias, and preserve legacy TCP, UDS, and native IPv6 readers. - Cover tag aliases, msgpack-style lists, legacy payloads, and IPv6 socket addresses before switching writers. Prompt-IO: ai/prompt-io/opencode/20260820T033107Z_ba07e09d_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
- Make `TCPAddress.unwrap()` emit `('tcp', host, port)` and
`UDSAddress.unwrap()` emit `('unix', path)` while retaining the
compatibility readers from the preceding change.
- Pass concrete TCP fields to Trio, compose multiaddrs from tagged
values, and let `SpawnSpec` carry protocol-specific tuple shapes
for validation by `wrap_address()`.
- Compare runtime, registry, bind, and tunnel addresses through
canonical serialized forms and cover both TCP and UDS operation.
Prompt-IO: ai/prompt-io/opencode/20260820T033108Z_ba07e09d_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
goodboy
force-pushed
the
wkt/addr_unpacking
branch
from
August 30, 2026 02:03
5b606ba to
51f4e31
Compare
goodboy
force-pushed
the
ng_tpts_planning
branch
from
August 30, 2026 02:04
4730b4c to
768b531
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add
TunnelledAddressand tagged addrsMotivation
Tractor's address layer previously flattened WireGuard maddrs to
their overlay socket address and serialized every transport as an
untagged pair. The runtime could neither retain tunnel metadata until
network setup nor distinguish transport protocols from the serialized
shape.
This change keeps tunnel declarations as transparent wrappers until
the socket bind/connect boundaries and gives TCP and Unix addresses
explicit wire tags. Legacy tuple and msgpack-list inputs remain
readable during the migration.
Src of research
The implementation follows the shared backend contract and plan 03's
three-layer WireGuard model. Layer A uses py-multiaddr's merged
WireGuard codec and treats a tunnel as bindspace metadata around an
existing L4 address, rather than inventing another message transport.
Summary of changes
WGTunnelSpecandTunnelledAddressstructures withnested maddr parsing, formatting, namespace and bindspace
delegation, and recursive
strip_tunnels()support.immediately before
Channeldialing andEndpointlistenerbinding.
API and pin py-multiaddr's merged WireGuard codec revision.
live capabilities, namespace identity, ownership and teardown
resources.
('tcp', host, port),('unix', path)andinput-only
('uds', path)forms while retaining legacy pair andnative IPv6 readers.
.unwrap()emission, spawn payloads, multiaddr compositionand runtime comparisons to canonical tagged forms.
nested maddr regression coverage.
45 debugger/devx tests. A downstream Piker runtime boot and tagged
registry regression also pass against this head.
Scopes changed
tractor.discovery._tunnelowns tunnel declarations, nestedparsing and transparent overlay delegation.
tractor.discovery._addrand._multiaddrown taggedserialization compatibility and composed maddr conversion.
tractor.ipc._chanand._serverenforce the tunnel-peelingsocket boundary without changing transport dispatch.
tractor.ipc._tcp,._udsandtractor.msg.types.SpawnSpecemitand carry protocol-specific tagged tuple shapes.
examples.multihost.wg_lanconsumes discovery's canonicalWireGuard parser instead of maintaining a second grammar.
Future follow up
and tagged registrar values in
piker.service._registryand retaincanonical runtime comparisons.
wg(8)subprocess reads with focusedpyroute2queries under Triowithout introducing another event loop.
scoped bindspace capabilities, enter namespaces during child
bootstrap, drop provisioning authority and test owned versus
borrowed teardown under
CAP_NET_ADMIN.after the merged WireGuard codec ships and finish protocol
registration through tractor issue
wgmultiaddr protocol: upstream spec submission plan #483.Links
#108
(this pr content was generated in some part by
opencodeusinggpt-5.6-sol(openai))