docs(rust-link): a sidecar refuses a plugin that names another server (D155)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-09-26 01:22:53 -05:00
parent 5d7633c051
commit 7bbe9fa5ef
3 changed files with 14 additions and 8 deletions

View File

@@ -113,7 +113,7 @@ When you create a server from it:
| Variable | | | Variable | |
|---|---| |---|---|
| **Modding Framework** (`FRAMEWORK`) | `oxide` or `carbon`. `vanilla` installs no bridge and the server boots exactly as egg 18's | | **Modding Framework** (`FRAMEWORK`) | `oxide` or `carbon`. `vanilla` installs no bridge and the server boots exactly as egg 18's |
| **Runic Gateway: server id** (`RUSTLINK_SERVER_ID`) | The id on the website. Read **once**, when the plugin writes its first config; after that the file holds it and the site locks it, so changing the variable later changes nothing | | **Runic Gateway: server id** (`RUSTLINK_SERVER_ID`) | The id on the website. Read **once**, when the plugin writes its first config; after that the file holds it and the site locks it, so changing the variable later changes nothing — the launcher hands the sidecar the id from the plugin's config and says so in the console |
| **Runic Gateway: sidecar port** (`RUSTLINK_WEB_PORT`) | **One of this server's allocations.** The panel does not tell a server which ports it holds, so a port that is not allocated binds but is never reachable | | **Runic Gateway: sidecar port** (`RUSTLINK_WEB_PORT`) | **One of this server's allocations.** The panel does not tell a server which ports it holds, so a port that is not allocated binds but is never reachable |
| **Runic Gateway: sidecar token** (`RUSTLINK_WEB_TOKEN`) | Leave blank: the sidecar generates one. Anyone who can see this server's startup variables can read a token typed here | | **Runic Gateway: sidecar token** (`RUSTLINK_WEB_TOKEN`) | Leave blank: the sidecar generates one. Anyone who can see this server's startup variables can read a token typed here |
| **Runic Gateway: bundle** (`RUNICGATEWAY_BUNDLE`) | Blank takes the current bundle. A tag pins one | | **Runic Gateway: bundle** (`RUNICGATEWAY_BUNDLE`) | Blank takes the current bundle. A tag pins one |
@@ -230,5 +230,6 @@ With the egg: reinstall to update (above); the console is the diagnosis.
| The site says the sidecar is unreachable | The website cannot reach the URL: a loopback bind with the site elsewhere (see above), a firewall, or — on the egg — a sidecar port that is not one of the server's allocations | | The site says the sidecar is unreachable | The website cannot reach the URL: a loopback bind with the site elsewhere (see above), a firewall, or — on the egg — a sidecar port that is not one of the server's allocations |
| The site answers `409` | The site's Rust module speaks another protocol than the bridge. Update whichever is behind (and see the note in *Before you begin*) | | The site answers `409` | The site's Rust module speaks another protocol than the bridge. Update whichever is behind (and see the note in *Before you begin*) |
| `doctor`: *plugin connected — no, and the server is running* | The plugin did not compile or cannot reach its sidecar. The server console names a compile error; the plugin's `Port` must equal the sidecar's game port | | `doctor`: *plugin connected — no, and the server is running* | The plugin did not compile or cannot reach its sidecar. The server console names a compile error; the plugin's `Port` must equal the sidecar's game port |
| The sidecar logs *refusing a plugin that names another server* | Two servers dial one sidecar: a plugin's `Port` is another server's game port. Correct that plugin's `Port` (in its config) and reload it. Nothing it sent was stored |
| `doctor`: *not the file that was deployed* | The plugin was edited by hand. `update --game rust` puts the released one back | | `doctor`: *not the file that was deployed* | The plugin was edited by hand. `update --game rust` puts the released one back |
| The egg's console prints a new token after every boot | Only a sidecar older than the first Rust-Link release does this; reinstall the server | | The egg's console prints a new token after every boot | Only a sidecar older than the first Rust-Link release does this; reinstall the server |

View File

@@ -197,10 +197,11 @@ Each pair is fully independent: its own ports, its own `sidecar.toml`, its own d
own token, its own row on the website. own token, its own row on the website.
Set `[game].server_id` in each `sidecar.toml` to match that server's plugin config. It is a Set `[game].server_id` in each `sidecar.toml` to match that server's plugin config. It is a
**cross-check**, not a second source of truth — the plugin's announcement wins — and it exists to **cross-check**, not a second source of truth, and it exists to catch exactly one mistake: two game
catch exactly one mistake: two game servers pointed at one sidecar by a copied config, which is servers dialling one sidecar, which is silent in every other design and produces one server's
silent in every other design and produces one server's history under another's name. When it fires history under another's name. When it fires, the sidecar refuses the plugin that names the other
you get a warning naming both ids. server, before anything it sent is stored, and logs an `ERROR` naming both ids; that plugin retries
every few seconds until its `Port` is corrected. The installer and the egg set it for you.
--- ---

View File

@@ -324,9 +324,13 @@ Two things about those are load-bearing:
working directory. A service manager's working directory must not decide where the database lands working directory. A service manager's working directory must not decide where the database lands
— on Windows that can be `%SystemRoot%\System32`, or a silently redirected VirtualStore copy. — on Windows that can be `%SystemRoot%\System32`, or a silently redirected VirtualStore copy.
- **`[game].server_id` is a cross-check, not a second source of truth.** The plugin announces its own - **`[game].server_id` is a cross-check, not a second source of truth.** The plugin announces its own
`serverId` and that is the authority; when both are set and they disagree, the sidecar logs the `serverId` and that is the authority for what a server is called. When `server_id` is set, the
disagreement loudly and keeps the plugin's. Two game servers pointed at one sidecar by a copied sidecar **refuses a plugin that names another server**: it closes the connection on the first
config is the mistake this catches, and it is silent in every other design. frame that disagrees, before that frame is filed, and logs both ids at `ERROR` (D155). Until a
frame has named this server, no command is sent to the peer and `/health` does not report a
plugin connected. Blank, nothing is checked. Two game servers dialling one sidecar is the mistake
this catches. It was a warning that kept the plugin's id until phase 18, when a walk showed the
cost: one server's history filed under another's name, on the website (PLAN.md §34.5).
`rust-link-sidecar --print-config` resolves the configuration exactly as a normal start would — `rust-link-sidecar --print-config` resolves the configuration exactly as a normal start would —
writing the file and generating the token if they are missing — and prints it as JSON on stdout, writing the file and generating the token if they are missing — and prints it as JSON on stdout,