Peel tunnels before `Channel` connects
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`))wkt/addr_unpacking
parent
f81fc5e516
commit
d0a9593aff
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
model: gpt-5.6-sol
|
||||
service: opencode
|
||||
session: tractor-addr-unpacking-followup
|
||||
timestamp: 2026-08-19T21:31:44Z
|
||||
git_ref: f81fc5e5
|
||||
scope: code
|
||||
substantive: true
|
||||
raw_file: 20260819T213144Z_f81fc5e5_prompt_io.raw.md
|
||||
---
|
||||
|
||||
## Prompt
|
||||
|
||||
The human requested runtime boundary integration with the established
|
||||
per-step implementation and commit-plan workflow.
|
||||
|
||||
## Response summary
|
||||
|
||||
Kept `TunnelledAddress` available to callers while peeling it at the
|
||||
last outbound boundary before transport lookup and dialing. Added a
|
||||
regression which captures both transport arguments and confirms plain
|
||||
TCP behavior is unchanged.
|
||||
|
||||
## Files changed
|
||||
|
||||
- `tractor/ipc/_chan.py` - peel tunnel annotations before outbound
|
||||
transport dispatch and connection.
|
||||
- `tests/ipc/test_channel_tunnel_boundary.py` - verify plain and
|
||||
tunnelled channel inputs deliver only TCP overlays.
|
||||
|
||||
## Human edits
|
||||
|
||||
The human chose the runtime-boundary slice, required the existing
|
||||
per-step commit-plan flow, and previously established that wrappers
|
||||
must retain bindspace metadata without impersonating transports. The
|
||||
agent implemented those constraints; no direct manual source edits were
|
||||
observed during this step.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
model: gpt-5.6-sol
|
||||
service: opencode
|
||||
timestamp: 2026-08-19T21:31:44Z
|
||||
git_ref: f81fc5e5
|
||||
diff_cmd: git diff HEAD~1..HEAD
|
||||
---
|
||||
|
||||
# Raw output - outbound tunnel boundary
|
||||
|
||||
The human requested the next tunnelled-address slice using the same
|
||||
per-step commit-plan flow. Existing design decisions require retaining
|
||||
tunnel metadata until the narrow IPC transport boundary and never
|
||||
teaching exact-type transport tables about tunnel wrappers.
|
||||
|
||||
> `git diff HEAD~1..HEAD -- tractor/ipc/_chan.py tests/ipc/test_channel_tunnel_boundary.py`
|
||||
|
||||
Extended channel address inputs to accept tunnel declarations, then
|
||||
called `strip_tunnels()` immediately before exact-type transport lookup
|
||||
and `connect_to()`. Added plain/tunnel parameterized coverage proving
|
||||
both operations receive the identical TCP overlay while the original
|
||||
wrapper retains its tunnel spec.
|
||||
|
||||
Verification included focused IPC tests, Ruff, discovery/IPC suites,
|
||||
and the full tractor suite.
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
'''
|
||||
Tunnel annotation peeling at the outbound IPC transport boundary.
|
||||
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
import trio
|
||||
|
||||
from tractor.discovery import (
|
||||
TunnelledAddress,
|
||||
WGTunnelSpec,
|
||||
tunnels_of,
|
||||
)
|
||||
from tractor.ipc import _chan
|
||||
from tractor.ipc._tcp import TCPAddress
|
||||
|
||||
|
||||
_PUBKEY: str = 'g3x7z0AdV1rM6UQU22CC7IL3/ivn4DzrE7ikDhCZ/Dc='
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def overlay() -> TCPAddress:
|
||||
return TCPAddress('127.0.0.1', 0)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tunnelled(
|
||||
overlay: TCPAddress,
|
||||
) -> TunnelledAddress:
|
||||
return TunnelledAddress(
|
||||
overlay=overlay,
|
||||
tunnel=WGTunnelSpec(
|
||||
peer_pubkey=_PUBKEY,
|
||||
bearer=('192.168.1.50', 51820),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('use_tunnel', [False, True])
|
||||
def test_channel_peels_before_transport_dispatch(
|
||||
monkeypatch,
|
||||
overlay: TCPAddress,
|
||||
tunnelled: TunnelledAddress,
|
||||
use_tunnel: bool,
|
||||
):
|
||||
'''
|
||||
Exact-type transport lookup cannot dispatch a `TunnelledAddress`,
|
||||
and passing one onward would make TCP dial the wrong object. Feed
|
||||
both a plain overlay and its annotated wrapper into
|
||||
`Channel.from_addr()`, capture lookup and connect arguments, and
|
||||
prove both transport operations receive only the same bindable
|
||||
TCP address while the caller's tunnel metadata remains intact.
|
||||
|
||||
'''
|
||||
seen: list[tuple[str, TCPAddress]] = []
|
||||
|
||||
class FakeTransport:
|
||||
@classmethod
|
||||
async def connect_to(
|
||||
cls,
|
||||
addr: TCPAddress,
|
||||
**kwargs,
|
||||
) -> FakeTransport:
|
||||
seen.append(('connect', addr))
|
||||
return cls()
|
||||
|
||||
def fake_transport_from_addr(
|
||||
addr: TCPAddress,
|
||||
) -> type[FakeTransport]:
|
||||
seen.append(('lookup', addr))
|
||||
return FakeTransport
|
||||
|
||||
monkeypatch.setattr(
|
||||
_chan,
|
||||
'transport_from_addr',
|
||||
fake_transport_from_addr,
|
||||
)
|
||||
|
||||
async def main() -> None:
|
||||
declared = tunnelled if use_tunnel else overlay
|
||||
chan = await _chan.Channel.from_addr(declared)
|
||||
assert isinstance(chan.transport, FakeTransport)
|
||||
|
||||
trio.run(main)
|
||||
|
||||
assert seen == [
|
||||
('lookup', overlay),
|
||||
('connect', overlay),
|
||||
]
|
||||
assert tunnels_of(tunnelled) == (tunnelled.tunnel,)
|
||||
|
|
@ -45,6 +45,10 @@ from tractor.discovery._addr import (
|
|||
Address,
|
||||
UnwrappedAddress,
|
||||
)
|
||||
from tractor.discovery._tunnel import (
|
||||
TunnelledAddress,
|
||||
strip_tunnels,
|
||||
)
|
||||
from tractor.log import get_logger
|
||||
from tractor._exceptions import (
|
||||
MsgTypeError,
|
||||
|
|
@ -181,16 +185,17 @@ class Channel:
|
|||
@classmethod
|
||||
async def from_addr(
|
||||
cls,
|
||||
addr: UnwrappedAddress,
|
||||
addr: UnwrappedAddress|Address|TunnelledAddress,
|
||||
**kwargs
|
||||
) -> Channel:
|
||||
|
||||
if not is_wrapped_addr(addr):
|
||||
addr: Address = wrap_address(addr)
|
||||
addr = wrap_address(addr)
|
||||
|
||||
transport_cls = transport_from_addr(addr)
|
||||
transport_addr: Address = strip_tunnels(addr)
|
||||
transport_cls = transport_from_addr(transport_addr)
|
||||
transport = await transport_cls.connect_to(
|
||||
addr,
|
||||
transport_addr,
|
||||
**kwargs,
|
||||
)
|
||||
# XXX, for UDS *no!* since we recv the peer-pid and build out
|
||||
|
|
@ -518,7 +523,7 @@ class Channel:
|
|||
|
||||
@acm
|
||||
async def _connect_chan(
|
||||
addr: UnwrappedAddress
|
||||
addr: UnwrappedAddress|Address|TunnelledAddress,
|
||||
) -> typing.AsyncGenerator[Channel, None]:
|
||||
'''
|
||||
Create and connect a `Channel` to the provided `addr`, disconnect
|
||||
|
|
|
|||
Loading…
Reference in New Issue