Pin the calling task's current netns when `bindspace` is omitted
on Linux/Trio. Retain the scope through child startup so a reused
subprocess worker cannot select a different namespace.
Pass the child-owned FD with its expected device and inode. Check
both before entry, skip `setns()` for an already-current namespace,
and release the transferred FD before runtime setup. Require a
live integer FD for every `Bindspace`.
Add canonical `parent_addr` and `registry_addrs` spawn inputs.
Require an explicit parent to name a live listener and keep each
child's registrar selection in its own `SpawnSpec` and runtime
snapshot, preserving supervisor and sibling inheritance.
Drop async root `bindspace=` entry; retain MP rejection of explicit
bindspaces. Cover real UDS cross-netns bootstrap, stale workers,
concurrent FD borrowers, registrar isolation, and failed starts.
(this patch was generated in some part by `Codex` using `GPT-6`
(`openai`))
Add `FrozenProcessLocal` for immutable coordinator snapshots while
retaining the existing default wire-encoding rejection.
Prevent callers and msgspec config from replacing or omitting the
process-local sentinel. Wrap subclass validation hooks with base
checks so mutable and frozen structs cannot reopen the wire bypass.
Cover direct and nested encoding, shallow frozen behavior, sentinel
replacement, omitted defaults and hostile validation hooks.
Prompt-IO: ai/prompt-io/opencode/20260907T011533Z_5966e8aa_frozen_process_local.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Keep address descriptors and structural protocols independent of
concrete IPC, actor runtime, tunnel and optional dependency mods.
Use `AddressDeclaration` for plain and tunnelled address values,
while concrete TCP/UDS factory dispatch remains in `._addr`. Drop
runtime model-name aliases to `Any` and the self-referential
`get_type_hints()` regression.
Also,
- preserve lazy `multiaddr` and net imports
- verify canonical type identity across discovery, IPC and tunnels
- route random and bound address reconstruction through
concrete types
Prompt-IO: ai/prompt-io/opencode/20260903T201530Z_d097090f_discovery_types.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Keep platform-independent spawn coverage active while avoiding Linux
`/proc` and `os.setns()` assumptions on macOS and Windows.
Deats,
- define one reusable Linux netns test mark
- apply it only to tests requiring real Linux namespace APIs
- preserve cross-platform bootstrap and transport unit coverage
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Enter a realized netns before root registry, IPC and runtime
startup, then restore the calling thread before owned bindspace
teardown.
Deats,
- duplicate the namespace FD so caller ownership stays intact
- preserve primary body errors across restore and close failures
- reject persistent `mp_forkserver` roots with stale netns risk
- cover cancellation, real netns entry, UDS RPC and public WG
composition
Prompt-IO: ai/prompt-io/opencode/20260830T025202Z_b1f6ade8_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Move bindspace, tunnel and WireGuard lifecycles out of actor
discovery and expose them through one lazy public package.
Deats,
- keep `import tractor` free of multiaddr, pyroute2 and WG impls
- move network-focused tests under `tests/net`
- update IPC, spawn, docs and multihost callers to the new API
- pin `CURRENT_NETNS` through the calling thread's procfs link
Prompt-IO: ai/prompt-io/opencode/20260830T025201Z_b1f6ade8_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Thread an optional live `Bindspace` through actor spawn APIs and
give each Trio exec child an inherited namespace descriptor before
runtime bootstrap.
Deats,
- duplicate the namespace FD without changing parent ownership
- preserve caller `pass_fds` and process options
- send the `(fd, inode)` pair through the `_child` CLI
- close the parent duplicate on success, failure and cancellation
- reject MP backends until descriptor reduction is implemented
- avoid cleanup before child publication
- exercise real E2E namespace relay through handshake and RPC
- verify parent FD-table cleanup after successful child spawn
Prompt-IO: ai/prompt-io/opencode/20260828T172943Z_2ca8c570_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Race the initial peer handshake against process exit during Trio
child bootstrap so a dead child cannot park its spawning task.
Restore and harden the design originally implemented in `3b0724eb`.
Deats,
- register peer events before provisional nursery publication
- raise `ActorFailure` with child status when process death wins
- prioritize observed death over a simultaneous handshake
- preserve waiter exceptions without `ExceptionGroup` wrapping
- log expected losing-waiter cancellation at debug level
- remove exact failed-startup peer events during backend cleanup
- cover controlled schedules and full Trio backend cleanup
Based-on: 3b0724eba8
Prompt-IO: ai/prompt-io/opencode/20260828T045119Z_fb6d81d3_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Consume an optional child-owned `(fd, inode)` capability before
multiprocessing or Trio child bootstrap begins runtime setup.
Deats,
- enter the netns before Trio patching and actor construction
- close the inherited FD before actor runtime startup
- preserve entry errors when descriptor cleanup also fails
- reject malformed FD values without closing unrelated descriptors
- cover multiprocessing and Trio ordering with real stand-in FDs
Prompt-IO: ai/prompt-io/opencode/20260828T012205Z_9ae7cd86_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add private `enter_netns()` bootstrap validation before actor runtime
integration.
Deats,
- verify the inherited FD against its expected namespace inode
- constrain `setns()` to Linux network namespaces
- verify `/proc/self/ns/net` after the entry syscall
- leave inherited FD ownership to the future spawn caller
- exercise guards with real FDs and unprivileged syscall fakes
Prompt-IO: ai/prompt-io/opencode/20260827T232500Z_d067505a_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Record native tagged encoding for complete `TunnelledAddress` graphs as
a deferred design follow-up.
Deats,
- cover concrete overlay-address and tunnel-spec unions
- preserve optional `BindspaceRef` metadata through decoding
- replace untyped payload inspection with typed roundtrip tests
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add `TunnelledAddress.with_bindspace_ref()` to annotate frozen tunnel
declarations with stable, serializable namespace metadata.
Deats,
- omit absent refs from the existing msgspec shape
- reject declared and realized namespace-name mismatches
- report the declared key before realization and inode afterward
- keep transport peeling free of live bindspace capability state
- expose realized refs through real listener diagnostics
Prompt-IO: ai/prompt-io/opencode/20260826T030534Z_d130431c_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Replace the unshipped `BindspaceIdentity` and `BindspaceHandle` names
with `BindspaceRef` and `Bindspace` across existing lifecycle APIs.
Deats,
- define refs as wire-safe, host-local and non-owning records
- reserve `Bindspace` for the live FD-backed capability
- rename the capability's realized-resource field to `.ref`
- update lifecycle tests and active design contracts
- omit compatibility aliases for the unshipped model names
Prompt-IO: ai/prompt-io/opencode/20260827T211115Z_d130431c_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add `open_wg_bindspace()` to enter one declared bindspace and an
ordered WireGuard interface stack as one async lifetime.
Deats,
- snapshot caller layer ordering before the first checkpoint
- enter interfaces outermost-first through `AsyncExitStack`
- unwind interfaces before releasing the namespace capability
- yield the live `BindspaceHandle` for endpoint allocation
- test mutable input and cancellation ordering with lifecycle fakes
Prompt-IO: ai/prompt-io/opencode/20260826T022434Z_2245f094_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add `open_wg_iface()` to create, configure and remove one WireGuard
interface inside a pinned bindspace through pyroute2.
Deats,
- validate listen/dial bearer policy before kernel side effects
- configure local addresses, private key, listen port and peers
- fill an omitted dial endpoint from the selected tunnel bearer
- clean partial synchronous failures before returning to Trio
- shield owned interface creation and teardown from cancellation
Prompt-IO: ai/prompt-io/opencode/20260826T003430Z_6dd39da0_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add process-local `WGPeerConfig` entries and make
`WGInterfaceConfig` own a unique peer tuple for listener and dial
provisioning.
Deats,
- carry peer public keys, allowed CIDRs and optional endpoints
- redact per-peer preshared keys while blocking wire encoding
- validate peer routes, endpoint ports and keepalive intervals
- reject duplicate peers before future kernel mutation
- support multi-peer listeners without overloading tunnel identity
Prompt-IO: ai/prompt-io/opencode/20260826T001442Z_dcdf4d82_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add process-local `WGInterfaceConfig` for key material, interface
addresses and peer-routing policy required by future provisioning.
Deats,
- redact private and preshared keys from representation
- block config from default actor-IPC encoding via `ProcessLocal`
- validate keys, interface CIDRs, allowed CIDRs and bounded integers
- keep public endpoint and peer identity in `WGTunnelSpec`
- move allowed-IP policy out of the serializable tunnel declaration
Prompt-IO: ai/prompt-io/opencode/20260825T234631Z_b973e78c_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add serialized `BindspaceSpec.lifecycle` policy and dispatch it through
`open_bindspace()` without inferring ownership from transport role.
Deats,
- distinguish borrowed `attach` from owned `open` policy
- validate handle ownership against the declared lifecycle
- share policy-neutral FD pinning between both netns contexts
- reject unsupported lifecycle values before side effects
- exercise both dispatcher branches and owned cancellation cleanup
Prompt-IO: ai/prompt-io/opencode/20260825T191845Z_5b2a064a_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add `open_netns()` to create a named Linux netns through pyroute2,
pin its identity and yield an owned `BindspaceHandle`.
Deats,
- run synchronous creation and removal in Trio worker threads
- shield both privileged side effects from caller cancellation
- reuse `attach_netns()` to pin identity and manage the FD
- close the FD before removing the owned namespace
- fake privileged operations while testing ordering and cancellation
Prompt-IO: ai/prompt-io/opencode/20260825T190529Z_e1007547_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add `attach_netns()` to pin a current or named Linux netns in a
borrowed `BindspaceHandle` without creating or entering it.
Deats,
- name the current-namespace default `CURRENT_NETNS`
- derive stable identity from the opened FD with `fstat()`
- open descriptors with `O_CLOEXEC` and close them on context exit
- constrain named lookup beneath the standard iproute2 run directory
- report field-specific validation and missing-resource errors
Prompt-IO: ai/prompt-io/opencode/20260825T045557Z_fdccfd7e_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Separate serializable `BindspaceSpec` and `BindspaceIdentity` values
from a process-local `BindspaceHandle` carrying FD and ownership
authority.
Deats,
- add global `ProcessLocal` wire guards for local handle structs
- derive valid kinds and ownership from their `Literal` aliases
- require a positive inode while keeping the mutable name optional
- pin supplied FDs to identity inodes with `fstat()`
- cover round trips, nested encoding and stale capabilities
Caught-during: review remediation
Found-via: `/run-tests` test_bindspace_handle_pins_local_capability
Prompt-IO: ai/prompt-io/opencode/20260822T042026Z_29141f0b_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Make `TCPAddress` and `UDSAddress` explicitly satisfy
`Address.namespace`, then retain each original listener declaration
beside its peeled, resolved transport address.
Deats,
- remove `TunnelledAddress`'s attribute fallback
- add required `Endpoint.declared_addr` metadata
- report declaration namespaces in endpoint/server formatting
- preserve concrete `Endpoint.addr` for transport reflection
- cover plain and tunneled namespace visibility
Prompt-IO: ai/prompt-io/opencode/20260822T032520Z_d35c802b_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Validate a declared tunnel key against one `pyroute2` snapshot
containing the iface's own key and configured peers.
Deats,
- share worker offload across all WireGuard key readers
- forward `WGTunnelSpec.iface` and `.netns` to the read
- reject malformed declarations before netlink I/O
- export the async helper and cover local, peer and absent keys
- replace multihost's `wg show` subprocess probe
Prompt-IO: ai/prompt-io/opencode/20260822T023226Z_59a8ecfd_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add a candidate Layer-C architecture where a private, eagerly
spawned manager owns pyroute2 and tunnel provisioning for a simple
WG-enabled actor tree.
Deats,
- overlap manager reconciliation with sibling process startup
- contain `AsyncWireGuard` in an infected-asyncio child
- limit requests and capabilities by bindspace security domain
- define readiness, crash, restart and teardown semantics
- retain pre-provisioned and multi-manager escape hatches
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Add async `read_wg_pubkey()` and `read_wg_peers()` helpers which
offload `WireGuard.info()` calls to a `trio` worker thread.
Deats,
- add the Linux-only `wg` extra and pin `pyroute2`
- pass `flags=0` so a read never creates a named netns
- normalize multipart replies, validate keys and de-dup peers
- always close the netlink client, including error paths
- test thread offload, netns forwarding and client cleanup
Prompt-IO: ai/prompt-io/opencode/20260821T233204Z_5d92595f_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Record `py-multibase` as a direct Tractor dependency, matching the WG
key-codec requirement already declared in `pyproject.toml`.
Also retain uv's Python 3.14 markers for `greenback` deps so the lock
matches the current py313-py314 project range.
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Separate legacy declaration inputs from canonical serialized outputs
in IPC and architecture docs, and state the same-version registrar
tree contract while no wire-format negotiation exists.
Add PR #505's feature news fragment for `TunnelledAddress` and tagged
TCP/Unix output.
Review: PR #505 (goodboy)
https://github.com/goodboy/tractor/pull/505#pullrequestreview-5094473850
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Unpack TCP's canonical proto tag before selecting the overlay host,
then pass a tagged bind declaration to the child actor. The example
otherwise raises before `echo_srv` starts.
Review: PR #505 (goodboy)
https://github.com/goodboy/tractor/pull/505#pullrequestreview-5094473850
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Store the validated `waddr.unwrap()` result so legacy and tagged
declarations share one registry identity. This lets stale-entry
eviction replace an older actor which used the compatibility form.
Cover TCP and UDS registrations crossing from legacy to tagged addrs.
Review: PR #505 (goodboy)
https://github.com/goodboy/tractor/pull/505#pullrequestreview-5094473850
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Normalize implicit listener declarations through `wrap_address()`,
just like caller-supplied addrs. Tagged defaults otherwise reach
endpoint reflection as plain tuples and fail before socket startup.
Add a TCP/UDS regression which omits `accept_addrs` and proves
each generated ep holds the expected concrete `Address`.
(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`))
- 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`))
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`))
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`))
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`))
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`))
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`))
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`))
Validate WireGuard keys and tunnel descriptors strictly. Inspect
iface keys asynchronously by local/peer role and reject unsupported
nested tunnels.
Correct both host binds and service publication, document an
unprivileged two-host setup and pin the merged `py-multiaddr` codec
revision in the lock.
Prompt-IO: ai/prompt-io/opencode/20260831T022317Z_768b5316_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Bring the shared backend contract and TIPC plan in line with
current runtime behavior and downstream implementation evidence.
Rework the QUIC plan around actor-owned endpoint, bootstrap,
stream, listener and cleanup lifecycles, with explicit validation
gates for the still-unverified UniFFI details.
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Replace mutable branch pointers with the exact source commit and
historical base-to-commit range. Normalize the substantive scope
while leaving the recorded raw response unchanged.
Prompt-IO: ai/prompt-io/claude/20260813T001102Z_27c34aeb_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
§3.2 specced a pure fn `_peel_tunnel_segs(proto_names) ->
(bearer_names, tunnel_specs, overlay_names)` to split a maddr at
its tunnel seg. It should never be written: `py-multiaddr` ships
that whole surface already and the plan simply missed it, even
though gh #443's 2nd bullet links the README sections in
question.
Replaced w/ a ⚠️ CORRECTION carrying the verified API table
(`.decapsulate_code(P_WG)` for the bearer, `.split()`/`.join()`
for a seg tail, `.value_for_protocol()` to read a value,
`.encapsulate()` to recompose) plus *why* it works on an infix
`/wg/` seg: the cut is by proto-code, never by matching an addr
value, and the key seg has no addr of its own.
Also,
- adopt `bearer`/`overlay` as the role names throughout, and say
plainly why not `inner`/`outer` — the call-stack reading of
"inner" is the exact opposite of the encapsulation one.
- warn that `value_for_protocol('ip4')` on a full tunnelled
maddr silently yields the *bearer's* host; only call it on a
peeled sub-maddr.
- note nesting (wg-in-wg) falls out of `.decapsulate_code()`
cutting at the *last* occurrence, so peel repeatedly rather
than recursing through a bespoke splitter.
- `mk_maddr()` for `TunnelledAddress` is `.encapsulate()`
composition, not `str` building.
- README: drop the "degrades to a plain segment split" para,
since that path is gone — no codec now means one actionable
raise.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
`py-multiaddr` already ships the entire tunnel compose/peel
surface and this module was reimplementing it — a raw
`maddr.split('/')` plus index arithmetic, sitting directly under
a comment congratulating itself for not hand-rolling a parser.
Same NIH trap gh #429 existed to close, just one layer up. The
API was linked from gh #443's own 2nd bullet the whole time.
So every cut now goes through the real thing,
| need | API |
| --- | --- |
| isolate the bearer | `.decapsulate_code(P_WG)` |
| per-seg maddrs | `.split()` |
| rejoin a seg tail | `Multiaddr.join()` |
| read the key | `.value_for_protocol('wg')` |
| recompose | `.encapsulate()` |
`.decapsulate_code()` turns out to handle the infix `/wg/` seg
cleanly *because* it cuts on proto-code and never tries to match
an addr value — the key seg has no addr of its own, which was
the exact thing I'd assumed would need bespoke handling.
Deats,
- rename the role fields `inner`/`inner_proto` ->
`overlay`/`overlay_proto`, matching `py-multiaddr`'s
encapsulation model (earlier segs wrap later ones) and #443's
owner table. `inner` collided head-on w/ call-stack `inner`,
where it reads as higher-up + later-called, while here the
encapsulated addr is bound *first* and sits deeper.
- drop `_segments()` and its degraded hand-split path entirely.
W/o the codec there's now one actionable `RuntimeError`
instead of a silent downgrade, superseding the swallow fix in
7d6e7955.
- add `.as_multiaddr()` so callers can stay in `Multiaddr` land;
`.maddr` is now just `str()` of it.
- accept `str|Multiaddr` on the way in.
- carry `bearer_ip`/`overlay_ip` so a v6 stack re-renders as v6
— the old `.maddr` hardcoded `/ip4/` and would silently
mangle it.
- both host scripts follow the rename to `.overlay`.
⚠️ `value_for_protocol('ip4')` on a *full* tunnelled maddr
silently returns the **first** match, i.e. the bearer's host, so
it's only ever called here on an already-peeled sub-maddr.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
it lands" framing in plan-03 and the example README was stale in
both directions: the branch pin is obsolete, yet you still can't
just `pip install multiaddr`.
Deats,
- §3.2's grammar table is now re-verified against the upstream
merge (`f86519da`) rather than only `baudco@wg_support` in a
throwaway venv. Also notes the codec enforces a 32-byte key,
so a truncated one is a `StringParseError` and not a silently
mangled parse.
- §1 says merged-but-unreleased; the still-open work is spec
registration (py-multiaddr#107 + gh #483).
- §3.4 swaps "pin the branch" for the `[tool.uv.sources]` `rev`
pin, and fixes the `_have_wg_maddr_proto()` recipe it
suggested — probing w/ `Multiaddr('/wg/uAAAA')` now ALWAYS
raises bc the codec wants 32B, i.e. that feature-detect would
report `False` even w/ the proto perfectly well known.
- risk table row goes "#108 not merged" -> "merged but
unreleased".
- example README: `uv sync` alone now suffices bc of the pin;
documents the 32B check and points at
`_have_wg_maddr_proto()` as the gate.
The one surviving `baudco` mention is deliberate, it records
where the grammar was *first* verified.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
`_segments()` called `Multiaddr(maddr)` purely to validate, then
swallowed every failure under `except Exception: pass`. That was
harmless pre-#108 — w/o a `wg` codec there was nothing to
validate — but now that the codec is pinned in, the swallow is
load-bearing and disabled: a malformed key sails past validation
into `wg8_pubkey()`, which happily emits a corrupt b64 str, and
the returned struct then fails its own `.maddr` round-trip. No
raise, just quietly wrong output.
Deats,
- add `_have_wg_maddr_proto()`, the gate plan-03 already
referenced but which never actually existed. Impl'd as
`protocols.protocol_with_name('wg')` under
`except ProtocolNotFoundError` and cached in a mod global,
same shape as the TIPC plan's `is_tipc_available()`.
- only validate when that gate is `True`, and let
`StringParseError` propagate — a maddr which doesn't parse
must NOT reach `wg8_pubkey()`.
- keep the degraded split for a pre-#108 install, now w/ an
explicit `XXX` naming the validation you give up.
So parsing stays pure but becomes total-or-raises. Our own
`ValueError`s (missing `/wg/` seg, bare tunnel w/o an overlay
ep) are unaffected, as is the `wg(8)` b64 round-trip.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
py-multiaddr#108 (the `/wg/u<key>` maddr proto) merged upstream
on 2026-07-28 as `f86519da`, but ships in no release yet — the
latest `0.2.0` predates it by ~4 months and carries no `wg`
codec at all. So `examples/multihost/wg_lan/` can't parse its
own maddrs off PyPI.
Pinned by `rev` and not `branch` so CI stays reproducible. Note
the lock now records the git source *instead of* the `>=0.2.0`
specifier, i.e. the dep floor above is fully overridden for as
long as this pin lives.
TODO, drop the pin (and bump that floor) the moment a release
carries the codec; the only consumer is the `wg_lan` example
set.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
One record covering all 9 commits on this branch, per the NLNet
generative-AI policy and the existing `ai/prompt-io/claude/`
convention.
Uses diff-ref mode for both the plan docs and the example code
(`git diff main..ng_tpts_planning -- <path>`) rather than
duplicating content already in `git log -p`. Kept verbatim in
the `.raw.md`: the four verified findings (trio's
family-agnostic `SocketStream`/`SocketListener`, the round-trip
table proving `/wg/` is infix, the proto-key `UnwrappedAddress`
rationale, and `setns(2)`'s per-thread reality), since those are
reasoning rather than diffable output.
`## Human edits` records that the steering here was substantial
and mid-session rather than post-hoc: two model claims about wg
maddr semantics were challenged and retracted (incl. in an
already-posted issue comment), and the proto-key +
netns-as-runtime-config framings were human-directed. Also notes
the one model-initiated correction — a pre-publication
self-review that downgraded the `uniffi`/asyncio thesis and the
TIPC duplicate-binder claim to explicitly-flagged assumptions.
Prompt-IO: ai/prompt-io/claude/20260813T001102Z_27c34aeb_prompt_io.md
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
`tests/test_docs_examples.py` walks `examples/` **recursively**
and subproc-runs every collected file asserting `rc == 0`. Ran
its exact filter against the tree: all 4 of our files were being
collected — including `README.md`, since the filter never checks
the extension, so CI would have literally tried `python
README.md`. These need a real second host + a live `wg` tunnel,
so they can't ever satisfy that gate.
`'multihost' not in p[0]` is already in the test's exclusion
list w/ no dir yet using it, so this is a pure `git mv` — zero
test changes — and it's what the exclusion was plainly there
for. Collection drops 24 -> 20 files, 0 of them ours.
Also records *why* in the two places someone would look before
adding the next one: a callout at the top of the example README
and a note on plan 03's §3.4 deliverables. Anything needing a
second host or live tunnel goes under `examples/multihost/`.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
Re-renders gh #482's examples w/ the corrected (infix) maddr
grammar, as the "layer A" slice of the wg plan: declarative
maddrs only, tunnel pre-provisioned out-of-band, zero runtime
changes.
- `wg_maddr.py`: a `frozen=True` `msgspec.Struct` addr carrying
`bearer`/`peer_pubkey`/`inner` (+ `inner_proto`), a `.maddr`
property that re-renders the canonical form, and pure
`mb_pubkey()`/`wg8_pubkey()`/`parse_wg_maddr()`. The parser
rejects #482's inverted suffix form w/ an actionable error and
stays **side-effect free** — `verify_wg_peer()` is a separate,
explicitly impure step the caller composes, never something a
parse path shells out to.
- `host_a_srv.py`/`host_b_client.py`: the two-host runs, passing
only `addr.inner` into `open_nursery()`/`open_root_actor()`,
which is the whole point — the bearer + key layers are already
established before any bind happens.
- `README.md`: the grammar + the 3-owners table, the `#108`
branch install line, tunnel setup, and a "what changed vs
#482" section enumerating the corrections.
Runnable-shaped but **not yet run against a live tunnel**; that's
next, and the reason these sit on the planning branch rather than
in `examples/` proper. `_segments()` marks its stopgap for when
the `wg` codec isn't installed.
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))