Context
Discussed while wrapping up phase 0.3 (#9, #10, #11): the wire protocol (server/WIRE.md) is transport-agnostic, line-oriented request/response over a byte stream. Nothing about it assumes which side initiates the TCP connection.
Right now (and as planned for 0.4's TCP transport) the model is host-as-client: AmiPilotServer binds/listens, and the host connects in when it wants to drive a test. That works standing at a real machine or under Copperline's bridged/loopback hostsocket networking (see the hostsocket session notes), but it doesn't fit NAT-mode emulation or a real Amiga behind NAT — there's no way for the host to reach in.
Proposal
A dial-out mode: AmiPilotServer periodically/persistently Connect()s out to a configured host:port (with reconnect/backoff) instead of listening, using bsdsocket.library the same way 0.4's inbound TCP transport would. The host runs a small listener and accept()s whichever configured Amigas check in, dispatching per-connection sessions. Framing and dispatch (server/WIRE.md, the shared HandleCommand) stay identical — this only changes which side initiates.
Bonus found during the pytest-plugin work (#11): this would also let the host side just block on accept() instead of the polling/retry-connect dance host/amipilot/pytest_plugin.py's _connect_with_retry currently has to do. Two real bugs were found and fixed there specifically because the host has to guess when the guest is ready (reconnect-per-attempt stranding replies on abandoned sockets; a VERSION sent before the guest opened its transport being silently dropped, not buffered). If the Amiga dials out only once its transport is genuinely open, accept() naturally blocks until that real moment — no polling, no premature-send race, no reconnect logic needed on either side.
Scope notes
- Needs a way to identify which Amiga just connected if a host might accept from more than one — likely a hostname/ID field added to the existing
VERSION handshake payload.
- The host needs a persistent listener process rather than a dial-on-demand script — a different operational shape than today's "host drives one Amiga on demand" pytest fixture.
- Listen-mode (today's model) should stay available for the "walk up to a real Amiga and drive it standing there" case; this would be an additional mode, not a replacement.
- Natural fit for 0.4's TCP transport work, not a separate phase.
Context
Discussed while wrapping up phase 0.3 (#9, #10, #11): the wire protocol (
server/WIRE.md) is transport-agnostic, line-oriented request/response over a byte stream. Nothing about it assumes which side initiates the TCP connection.Right now (and as planned for 0.4's TCP transport) the model is host-as-client:
AmiPilotServerbinds/listens, and the host connects in when it wants to drive a test. That works standing at a real machine or under Copperline's bridged/loopback hostsocket networking (see the hostsocket session notes), but it doesn't fit NAT-mode emulation or a real Amiga behind NAT — there's no way for the host to reach in.Proposal
A dial-out mode:
AmiPilotServerperiodically/persistentlyConnect()s out to a configured host:port (with reconnect/backoff) instead of listening, usingbsdsocket.librarythe same way 0.4's inbound TCP transport would. The host runs a small listener andaccept()s whichever configured Amigas check in, dispatching per-connection sessions. Framing and dispatch (server/WIRE.md, the sharedHandleCommand) stay identical — this only changes which side initiates.Bonus found during the pytest-plugin work (#11): this would also let the host side just block on
accept()instead of the polling/retry-connect dancehost/amipilot/pytest_plugin.py's_connect_with_retrycurrently has to do. Two real bugs were found and fixed there specifically because the host has to guess when the guest is ready (reconnect-per-attempt stranding replies on abandoned sockets; aVERSIONsent before the guest opened its transport being silently dropped, not buffered). If the Amiga dials out only once its transport is genuinely open,accept()naturally blocks until that real moment — no polling, no premature-send race, no reconnect logic needed on either side.Scope notes
VERSIONhandshake payload.