fix(bridge): dial the configured port, and back off when refused (D154, D155) #15

Merged
whitlocktech merged 2 commits from fix/config-before-link into main 2026-09-26 01:27:03 -05:00
Member

Found by the phase 18 walk (§34.3 step 2). The org lead chose to fold both into phase 18 (D154, D155). Merge together with Rust-Link#15, which makes the sidecar refuse a plugin that names another server. The second commit here is what keeps that refusal from becoming a flood.

1. The plugin dialed 7799 regardless of its config (D154)

Init() started LinkLoop before Loaded() read the config. Both frameworks call Init first, so the thread's first dial saw _config == null and used the 7799 fallback, on Oxide and Carbon alike:

[Runic Gateway] protocol 12, serverId 'beta', sidecar 127.0.0.1:7800
[Runic Gateway] connected to 127.0.0.1:7799

With one server per host, 7799 was its own sidecar by coincidence, which is how every earlier rig passed. With two on a host (the installer's named instances), the second server dialed the first server's sidecar and sat in its accept backlog. When alpha's plugin reloaded, alpha's sidecar accepted beta's connection, and the website showed server alpha with hostname "rg18 beta" and beta's wipe id.

Fix: LoadConfigValues() moves into Init(), before the thread starts. Loaded() keeps its log line and the lease/world/reward loads. LoadConfigValues has no other caller.

2. The backoff never held (D155's consequence)

The reconnect backoff waited on Wake, which Emit signals on every frame, so it lasted only until the next hook fired. That was invisible while a failed connect was silent. Against a sidecar that refuses, the walk measured ~250 redials a second, each printing two console lines and rebuilding the boards.

Fix: wait on _stopping, the handle that already exists so a connect isn't abandoned by Emit. Unload still ends the wait at once. The backoff also resets only after a link has held for 10 s (StableLinkMs); a link closed on arrival used to reset it to 500 ms on every accept.

Proof (phase 18 rig: alpha + beta on Oxide, gamma on Carbon)

  • Each plugin dials its own port (7799 / 7800 / 7801). All three sidecars report plugin_connected: true, and the site shows each server under its own hostname and wipe.
  • A plugin deliberately aimed at another server's sidecar (running Rust-Link#15) is refused every 5 s (the ceiling) instead of continuously:
    06:18:00.69 · 06:18:04.70 · 06:18:09.70 · 06:18:14.70 · 06:18:19.70 …
  • A stable link whose sidecar restarts is back in 0.49 s, as before. No Unload stall on any reload.
  • node scripts/checkPlugin.js and its tests are green.

Merging releases v0.1.1. The installer's bundle.yml recomposes v2/rust/current.json, and the walk re-runs from that release.

  • AI-assisted: Claude Code (Claude Opus 5.5)

🤖 Generated with Claude Code

https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY

Found by the phase 18 walk (§34.3 step 2). The org lead chose to fold both into phase 18 (**D154**, **D155**). **Merge together with Rust-Link#15**, which makes the sidecar refuse a plugin that names another server. The second commit here is what keeps that refusal from becoming a flood. ## 1. The plugin dialed 7799 regardless of its config (D154) `Init()` started `LinkLoop` before `Loaded()` read the config. Both frameworks call `Init` first, so the thread's first dial saw `_config == null` and used the **7799 fallback**, on Oxide and Carbon alike: ``` [Runic Gateway] protocol 12, serverId 'beta', sidecar 127.0.0.1:7800 [Runic Gateway] connected to 127.0.0.1:7799 ``` With one server per host, 7799 was its own sidecar by coincidence, which is how every earlier rig passed. With two on a host (the installer's named instances), the second server dialed the **first server's** sidecar and sat in its accept backlog. When alpha's plugin reloaded, alpha's sidecar accepted beta's connection, and the website showed **server `alpha` with hostname "rg18 beta" and beta's wipe id**. **Fix:** `LoadConfigValues()` moves into `Init()`, before the thread starts. `Loaded()` keeps its log line and the lease/world/reward loads. `LoadConfigValues` has no other caller. ## 2. The backoff never held (D155's consequence) The reconnect backoff waited on `Wake`, which `Emit` signals on every frame, so it lasted only until the next hook fired. That was invisible while a failed connect was silent. Against a sidecar that refuses, the walk measured **~250 redials a second**, each printing two console lines and rebuilding the boards. **Fix:** wait on `_stopping`, the handle that already exists so a *connect* isn't abandoned by `Emit`. `Unload` still ends the wait at once. The backoff also resets only after a link has held for 10 s (`StableLinkMs`); a link closed on arrival used to reset it to 500 ms on every accept. ## Proof (phase 18 rig: alpha + beta on Oxide, gamma on Carbon) - Each plugin dials its own port (7799 / 7800 / 7801). All three sidecars report `plugin_connected: true`, and the site shows each server under its own hostname and wipe. - A plugin deliberately aimed at another server's sidecar (running Rust-Link#15) is refused **every 5 s** (the ceiling) instead of continuously: `06:18:00.69 · 06:18:04.70 · 06:18:09.70 · 06:18:14.70 · 06:18:19.70 …` - A stable link whose sidecar restarts is back in **0.49 s**, as before. No `Unload` stall on any reload. - `node scripts/checkPlugin.js` and its tests are green. Merging releases v0.1.1. The installer's `bundle.yml` recomposes `v2/rust/current.json`, and the walk re-runs from that release. - [x] AI-assisted: Claude Code (Claude Opus 5.5) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-09-26 01:07:56 -05:00
fix(bridge): read the config before the link thread dials (D154)
All checks were successful
PR Checks / plugin-checks (pull_request) Successful in -1m29s
04491e70af
Init() started LinkLoop before Loaded() read the config, so the thread's
first dial saw _config == null and used the 7799 fallback on both Oxide and
Carbon. One server per host made that its own sidecar by coincidence. With
two on a host (the installer's named instances, phase 18), the second
server dialled the FIRST server's sidecar, sat in its accept backlog, and
the moment the first server's plugin reloaded that sidecar took the second
server's connection: the website showed server "alpha" with beta's
hostname and wipe.

LoadConfigValues() now runs in Init() before the thread starts; Loaded()
keeps its log line and the lease/world/reward loads.

Walked on the phase 18 rig: three instances (two Oxide, one Carbon 7801),
each now dials its own port.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-09-26 01:19:29 -05:00
fix(bridge): a backoff that holds against a sidecar that refuses (D155)
All checks were successful
PR Checks / plugin-checks (pull_request) Successful in -1m38s
3e928bb2f6
The reconnect backoff waited on `Wake`, which `Emit` signals on every
frame, so it lasted only until the next hook fired. That was invisible
while a failed connect was silent; against a sidecar that now refuses a
plugin naming another server (D155) the phase 18 walk measured some 250
redials a second, each printing two console lines and rebuilding the
boards. It now waits on `_stopping`, the handle that already exists so a
CONNECT is not abandoned by `Emit`; Unload still ends the wait at once.

The backoff also resets only after a link has held for 10 s
(StableLinkMs). A connection closed on arrival used to reset it to 500 ms
on every accept, so a refusal loop never backed off at all.

Walked: a plugin aimed at another server's sidecar is refused every 5 s
(the ceiling), not continuously; a stable link whose sidecar restarts is
back in 0.49 s, as before; no Unload stall on reload.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude changed title from fix(bridge): read the config before the link thread dials (D154) to fix(bridge): dial the configured port, and back off when refused (D154, D155) 2026-09-26 01:19:40 -05:00
whitlocktech approved these changes 2026-09-26 01:26:57 -05:00
whitlocktech merged commit 897233c75f into main 2026-09-26 01:27:03 -05:00
whitlocktech deleted branch fix/config-before-link 2026-09-26 01:27:04 -05:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/Rust-Plugins#15
No description provided.