From e2ed76eac4d16540c1469d56838fa3f083b23fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Fri, 28 Aug 2026 22:41:52 +0100 Subject: [PATCH] bind a free port, do not assert one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit a plugin sidecar does not choose a port. it binds whatever is free and REPORTS it, which is how every other sidecar here works and why none of them can collide. av-club asserted 3020 because it started life as a standalone service. that assumption cost a deploy the same day it became a plugin: fofos still had a copy of the pre-plugin code running from ~/garbage/av-club, on `main`, holding 3020 — so the plugin's sidecar died on EADDRINUSE in a restart loop while the platform reported "sidecar not available". two things claiming one number, and neither able to say so. AV_CLUB_PORT still pins it, for running outside Officer — the standalone mode fofos/platform was written against, where the other side has to know where to look. inside Officer nothing sets it. the announcement now sends `server.port`, never the requested value. reporting what you asked for rather than what you got is how a sidecar ends up advertising a port it is not on. Co-Authored-By: Claude Opus 5 Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043 --- sidecar/config.ts | 15 ++++++++++++++- sidecar/index.ts | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sidecar/config.ts b/sidecar/config.ts index 72012cd..5745560 100644 --- a/sidecar/config.ts +++ b/sidecar/config.ts @@ -22,7 +22,20 @@ function num(key: string, fallback: number, min: number): number { return value; } -export const PORT = num("AV_CLUB_PORT", 3020, 1); +/** + * The port to listen on. **0 means "any free one", and that is the default.** + * + * A plugin sidecar does not choose a number. It binds whatever is free and REPORTS it to the platform, + * which is how every other sidecar here works and why none of them can collide. av-club asserted 3020 + * because it began life as a standalone service, and on 2026-08-28 that assumption cost a deploy: a + * stale copy of the pre-plugin code was still holding 3020 on fofos, so the plugin's sidecar died on + * EADDRINUSE in a restart loop. + * + * `AV_CLUB_PORT` still works and still pins the port. That is for running this outside Officer — the + * standalone mode fofos/platform was written against — where something on the other side has to know + * where to find it. Inside Officer nothing should set it. + */ +export const PORT = num("AV_CLUB_PORT", 0, 0); /** * Where assets live. Outside the repo by default, and deliberately not under any directory a deploy diff --git a/sidecar/index.ts b/sidecar/index.ts index 43172e8..8ace688 100644 --- a/sidecar/index.ts +++ b/sidecar/index.ts @@ -217,8 +217,8 @@ const connection = createSidecarConnector({ reply({ type: "error", id: (_cmd as { id: string }).id, error: "av-club takes no commands; use its HTTP API" }); }, onConnected() { - connection.send({ type: "av-club:server", port: PORT }); - console.log(`[av-club] reported port ${PORT} to the API`); + connection.send({ type: "av-club:server", port: server.port! }); + console.log(`[av-club] reported port ${server.port} to the API`); }, });