diff --git a/ai/prompt-io/opencode/20260819T003327Z_53516b09_prompt_io.md b/ai/prompt-io/opencode/20260819T003327Z_53516b09_prompt_io.md new file mode 100644 index 00000000..3e317780 --- /dev/null +++ b/ai/prompt-io/opencode/20260819T003327Z_53516b09_prompt_io.md @@ -0,0 +1,37 @@ +--- +model: openai/gpt-5.6-sol +service: opencode +session: a998acff-af66-4969-a65e-f77cbf5ef8dd +timestamp: 2026-08-19T00:33:27Z +git_ref: wkt/tipc_backend_378 +scope: code +substantive: true +raw_file: 20260819T003327Z_53516b09_prompt_io.raw.md +--- + +## Prompt + +Develop a practical bootstrap plan for testing TIPC across two +physical motherboards. + +## Response summary + +Expanded the two-host example into a reproducible smoke-test runbook +and made host A authorize the stable module name imported by host B. +Documented the cluster-domain-socket analogy and the prospective +`pyroute2` management path. + +## Files changed + +- `examples/multihost/tipc_cluster/README.md` - document setup, + validation, resilience testing, capture and cleanup. +- `examples/multihost/tipc_cluster/host_a_srv.py` - authorize the + `host_a_srv` RPC module under direct script execution. + +## Human edits + +The human selected two separate physical motherboards as the target +environment and requested that the resulting local work be packaged +as its own commit boundary. The human also proposed “Cluster Domain +Sockets” as clearer terminology and reusing the planned `pyroute2` +dependency for TIPC management. diff --git a/ai/prompt-io/opencode/20260819T003327Z_53516b09_prompt_io.raw.md b/ai/prompt-io/opencode/20260819T003327Z_53516b09_prompt_io.raw.md new file mode 100644 index 00000000..b4d61723 --- /dev/null +++ b/ai/prompt-io/opencode/20260819T003327Z_53516b09_prompt_io.raw.md @@ -0,0 +1,27 @@ +--- +model: openai/gpt-5.6-sol +service: opencode +timestamp: 2026-08-19T00:33:27Z +git_ref: wkt/tipc_backend_378 +diff_cmd: git diff HEAD~1..HEAD +--- + +## Prompt + +The user asked for a bootstrap plan to try TIPC with two real, +physically separate Linux motherboards. + +## Response + +The generated changes turn the existing sketch into an operator +runbook and correct the RPC module authorization used by direct +script execution: + +> `git diff HEAD~1..HEAD -- examples/multihost/tipc_cluster/README.md examples/multihost/tipc_cluster/host_a_srv.py` + +The runbook covers matching revisions, cluster identity, interface +selection, Ethernet bearer setup, link and name-table validation, +RPC, failure/rejoin testing, diagnostics, cleanup and a future +network-namespace fixture. It also records “Cluster Domain Sockets” +as explanatory terminology and identifies a future `pyroute2` TIPC +generic-netlink codec as the path away from manual `tipc(8)` calls. diff --git a/examples/multihost/tipc_cluster/README.md b/examples/multihost/tipc_cluster/README.md index fca083a2..c03cf0ed 100644 --- a/examples/multihost/tipc_cluster/README.md +++ b/examples/multihost/tipc_cluster/README.md @@ -2,6 +2,10 @@ TIPC is a linux-kernel cluster IPC protocol whose service names live in a **cluster-wide name table maintained by the kernel**. +It is also described as **Cluster Domain Sockets**: the Unix-domain +socket model extended from one kernel to a cluster. That name is a +useful explanation for new users, while the code keeps `tipc` as its +protocol key to match Linux's `AF_TIPC`, kernel module and tooling. For `tractor` that means: - an actor's IPC address is a service name `(stype, instance)`, @@ -85,30 +89,55 @@ Everything above is single-node (`modprobe` is enough). To span hosts you need a **bearer** on both, which is the one thing that can't be CI'd. +For the first physical test, use two wired Linux hosts on the same +L2 segment. Prefer a direct cable or uncomplicated switch; avoid +Wi-Fi, guest VLANs and port isolation until the basic link works. +Use the same checkout and Python environment on both hosts: + ```bash # on BOTH hosts +git rev-parse HEAD # must match on A and B +uv sync --all-extras --dev sudo modprobe tipc -# over ethernet (L2) — simplest when the hosts share a segment -sudo tipc bearer enable media eth device eth0 +# choose the real wired iface; do not assume `eth0` +ip -br link +IFACE=enp3s0 + +# inspect existing cluster identity before changing anything +tipc node get address # must differ between hosts +tipc node get netid # must match between hosts + +# use one private test netid on BOTH hosts, before enabling bearers +sudo tipc node set netid 37801 + +# ethernet is simplest when the hosts share an L2 segment +sudo tipc bearer enable media eth device "$IFACE" # ..or over UDP when L2 isn't available — and MANDATORY over a # `wg` mesh, see below sudo tipc bearer enable media udp name uc localip 10.0.11.1 # verify BEFORE running anything: this must list the peer +tipc bearer list tipc link list tipc node list ``` -Then: +If the link does not appear, first verify carrier, a common TIPC +network ID, distinct node addresses, a common VLAN and compatible +MTUs. Ethernet TIPC uses EtherType traffic rather than IP routing, +so a successful `ping` alone does not prove the bearer can work. + +Run each command from `examples/multihost/tipc_cluster/`: ```bash # host A -python host_a_srv.py +uv run python host_a_srv.py # host B -python host_b_client.py +watch -n 0.5 tipc nametable show # optional second terminal +uv run python host_b_client.py ``` Note what's absent from both scripts: any IP, hostname or port. @@ -116,6 +145,51 @@ Both sides name the *same service*, and the kernel routes it. Move `host_a_srv.py` to a third node and host B's dial keeps working, unchanged. +For a first resilience pass, use a local console or separate +management link so the test does not cut off your own SSH session: + +```bash +# host B: record the healthy baseline +tipc link list +tipc link statistics show + +# either host: withdraw and recreate the bearer +sudo tipc bearer disable media eth device "$IFACE" +tipc link list +sudo tipc bearer enable media eth device "$IFACE" +tipc link list + +# prove name withdrawal/republication and RPC recovery +tipc nametable show +uv run python host_b_client.py +``` + +Capture `uname -a`, both node addresses, `tipc bearer list`, +`tipc link list`, `tipc link statistics show`, the name table and +both Python transcripts. Those artifacts distinguish an actor bug +from bearer discovery, cluster identity or switch configuration. + +Clean up a disposable Ethernet test on both hosts with: + +```bash +sudo tipc bearer disable media eth device "$IFACE" +tipc link list +``` + +Restore any pre-existing network ID only after all bearers are +disabled. The first useful automation target is a two-node network +namespace fixture that asserts link-up, remote publication, RPC, +withdrawal and republication in that order; physical hardware then +remains the validation layer for real NIC and switch behaviour. + +The commands above use iproute2's `tipc` frontend, which speaks the +kernel's `TIPCv2` generic-netlink family. The planned `pyroute2` +dependency already manages WireGuard, interfaces and namespaces and +provides generic-netlink primitives, but it does not currently ship a +TIPC message codec. Adding one upstream would let `tractor` replace +these manual commands with one Python netlink stack instead of +shelling out; until then, `tipc(8)` remains the canonical frontend. + ### over a `wg` mesh TIPC over WireGuard is the intended reference multihost @@ -231,12 +305,10 @@ self-skip when the module is absent. ## CI Single-host TIPC *is* CI-able — the module ships with the -standard Ubuntu kernel package, so a `sudo modprobe tipc` step -plus a `--tpt-proto tipc` matrix entry should work. That's not -wired up yet; verify in a throwaway workflow first, and fall -back to a container job with `--cap-add NET_ADMIN` if the -runners refuse. Cross-node (bearer) testing stays manual — this -README is that smoke test. +standard Ubuntu kernel package. CI loads it with `sudo modprobe +tipc` and runs the suite with `--tpt-proto tipc` as a blocking +matrix leg. Cross-node bearer testing stays manual — this README +is that smoke test. ## normative refs diff --git a/examples/multihost/tipc_cluster/host_a_srv.py b/examples/multihost/tipc_cluster/host_a_srv.py index a32770d2..398345d0 100644 --- a/examples/multihost/tipc_cluster/host_a_srv.py +++ b/examples/multihost/tipc_cluster/host_a_srv.py @@ -57,7 +57,10 @@ async def main() -> None: ) as an: await an.start_actor( 'host_a', - enable_modules=[__name__], + # Host B imports this same module name to construct the + # RPC `NamespacePath`; direct script execution would + # otherwise expose it as `__main__` on host A. + enable_modules=['host_a_srv'], ) print( 'host_a up — `tipc nametable show` on EITHER host\n'