docs(modules): module-rust supports Carbon too, ships a Pterodactyl egg, and the rigs move to the panel #253

Merged
whitlocktech merged 4 commits from docs/rust-carbon-and-pterodactyl into main 2026-09-16 02:54:25 +00:00
Member

Four new decisions of record (R19–R22), a new framework reference, and §14 recording the first panel rig as built.

R19 — the plugin is framework-agnostic: Oxide and Carbon, from now

Carbon is not a fork of Oxide but a separate loader shipping an Oxide compatibility layer, so one .cs in Oxide.Plugins deriving from RustPlugin serves both — that is Carbon's own documented first example. #if CARBON is a real symbol and Oxide defines no equivalent, so #if !CARBON is the Oxide branch; it works only because we ship source rather than a DLL.

Three existing decisions take an amendment and none is reversed:

  • R18 roots its config walk at Interface.Oxide.ConfigDirectory, never a literal. Carbon uses carbon/configs (plural) and lets an operator relocate all thirteen directories from the command line, so a hardcoded oxide/config/ is wrong on Carbon and on a customised Oxide install alike.
  • R2 is unchanged in design but permanently narrowed: Carbon persists permissions as Protobuf or SQLite, closing the file-reading shortcut R2 never planned to take. §12.2's PermissionExists pre-check survives intact — Carbon's GrantUserPermission returns bool where Oxide's returns void, so the framework that would tell us whether the write landed is the one we cannot listen to portably.
  • R4's doctor asks which framework rather than whether Oxide.

R19 also writes down what it refuses: no Carbon-only hook or convar enters a catalogue without an Oxide answer first. The OnCarbon* family is a tempting staff-audit feed and exactly how Carbon would quietly become required.

Derived from the community "Rust Autowipe" egg, which already carries a FRAMEWORK variable offering vanilla|carbon|oxide — the operator picks the framework at deploy time, which is R19's justification restated as a deployment fact.

The sidecar runs inside the game's container, which is what lets D2 stand unchanged rather than be reopened: a container's 127.0.0.1 is genuinely private, so the game link stays loopback and stays unauthenticated.

R21 — both rigs move to the panel

Oxide and Carbon cannot coexist in one install, so R19 cannot be proven on a single server. Also retires the D:\rust wipe-day routine and makes the rig Linux, where every previous finding came from Windows and Mono.

R22 — the sidecar is configured from the egg's variables

Nearly free: rust-link's config.rs already reads all five keys from the environment with env-over-file-over-defaults precedence, and Pterodactyl exposes egg variables as container environment. No second configuration mechanism. The game bind must not be operator-editable (D2), the web bind must derive from an allocation, and the db path must not be able to agree with REMOVE_FILES.

New: docs/modules/rust/CARBON.md

The difference list and nothing else — file layout, the permission store, the c. commands, 30 Carbon-only hooks and 13 uMod names Carbon's catalogue omits (at least two of which look like renames). Sourced from Carbon's own published metadata and source at main, and labelled throughout as not yet proven on a live Carbon server — it is the hypothesis phase 3 tests.

§14 — the rig as built, and two findings that change decisions

rust-oxide (id 17, e6758c06) exists on the panel, made with the application key and driven with the client key. The tier-2 loop was run rather than described: phase 1's real RunicGateway.cs pushed from the working tree, oxide.reload through the client API, the edit observed in Oxide's own log, source restored — about ten seconds end to end.

Three things worked that were not certain to: files/write creates missing parents, a plugin placed before Oxide exists survives Oxide arriving, and the plugin compiles and loads on Linux, which no previous phase had established.

Two findings with teeth:

  • The image installs the framework on every boot — Carbon from the rolling production_build tag, Oxide from releases/latest. So a restart is a framework upgrade, neither is pinnable through the egg, and R4's weaker "current enough" claim turns out not to be Carbon-specific.
  • R20's wrapper plan does not survive Carbon. The entrypoint prepends LD_PRELOAD=…/libdoorstop.so to the whole startup string, so ./rust-link-sidecar & ./RustDedicated … puts the preload on the sidecar. The server starts cleanly, reports nothing, and is silently unmodded — on the Carbon half only. Raised as a decision (own entrypoint vs. careful composition), not settled.

A correction carried in the open rather than dropped

An earlier commit on this branch recorded that the client key "authenticates and then lists zero servers" and reasoned from there to a second claim, that includes were broken on the panel. Both were wrong and wrong the same way — the servers were being deleted while the probing happened, so two calls minutes apart read two different worlds. §14.2 keeps it, because the reusable part is that a differential diagnosis across two API calls silently assumes the state did not move between them.

🤖 Generated with Claude Code

https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4

Four new decisions of record (R19–R22), a new framework reference, and §14 recording the first panel rig as built. ## R19 — the plugin is framework-agnostic: Oxide **and** Carbon, from now Carbon is not a fork of Oxide but a separate loader shipping an Oxide compatibility layer, so one `.cs` in `Oxide.Plugins` deriving from `RustPlugin` serves both — that is Carbon's own documented first example. `#if CARBON` is a real symbol and Oxide defines no equivalent, so `#if !CARBON` is the Oxide branch; it works only because we ship source rather than a DLL. Three existing decisions take an amendment and none is reversed: - **R18** roots its config walk at `Interface.Oxide.ConfigDirectory`, never a literal. Carbon uses `carbon/configs` (plural) *and* lets an operator relocate all thirteen directories from the command line, so a hardcoded `oxide/config/` is wrong on Carbon and on a customised Oxide install alike. - **R2** is unchanged in design but permanently narrowed: Carbon persists permissions as Protobuf or SQLite, closing the file-reading shortcut R2 never planned to take. §12.2's `PermissionExists` pre-check survives intact — Carbon's `GrantUserPermission` returns `bool` where Oxide's returns `void`, so **the framework that would tell us whether the write landed is the one we cannot listen to portably.** - **R4**'s `doctor` asks *which* framework rather than whether Oxide. R19 also writes down what it **refuses**: no Carbon-only hook or convar enters a catalogue without an Oxide answer first. The `OnCarbon*` family is a tempting staff-audit feed and exactly how Carbon would quietly become required. ## R20 — a Pterodactyl egg is a Rust-Link deliverable, phase 18 Derived from the community "Rust Autowipe" egg, which already carries a `FRAMEWORK` variable offering `vanilla|carbon|oxide` — the operator picks the framework at deploy time, which is R19's justification restated as a deployment fact. **The sidecar runs inside the game's container**, which is what lets D2 stand unchanged rather than be reopened: a container's `127.0.0.1` is genuinely private, so the game link stays loopback and stays unauthenticated. ## R21 — both rigs move to the panel Oxide and Carbon cannot coexist in one install, so R19 cannot be proven on a single server. Also retires the `D:\rust` wipe-day routine and makes the rig Linux, where every previous finding came from Windows and Mono. ## R22 — the sidecar is configured from the egg's variables Nearly free: `rust-link`'s `config.rs` already reads all five keys from the environment with env-over-file-over-defaults precedence, and Pterodactyl exposes egg variables as container environment. No second configuration mechanism. The game bind must not be operator-editable (D2), the web bind must derive from an allocation, and the db path must not be able to agree with `REMOVE_FILES`. ## New: `docs/modules/rust/CARBON.md` The difference list and nothing else — file layout, the permission store, the `c.` commands, **30 Carbon-only hooks and 13 uMod names Carbon's catalogue omits** (at least two of which look like renames). Sourced from Carbon's own published metadata and source at `main`, and labelled throughout as **not yet proven on a live Carbon server** — it is the hypothesis phase 3 tests. ## §14 — the rig as built, and two findings that change decisions `rust-oxide` (id 17, `e6758c06`) exists on the panel, made with the application key and driven with the client key. The tier-2 loop was **run rather than described**: phase 1's real `RunicGateway.cs` pushed from the working tree, `oxide.reload` through the client API, the edit observed in Oxide's own log, source restored — about ten seconds end to end. Three things worked that were not certain to: `files/write` creates missing parents, a plugin placed before Oxide exists survives Oxide arriving, and **the plugin compiles and loads on Linux**, which no previous phase had established. Two findings with teeth: - **The image installs the framework on every boot** — Carbon from the rolling `production_build` tag, Oxide from `releases/latest`. So *a restart is a framework upgrade*, neither is pinnable through the egg, and R4's weaker "current enough" claim turns out not to be Carbon-specific. - **R20's wrapper plan does not survive Carbon.** The entrypoint prepends `LD_PRELOAD=…/libdoorstop.so` to the *whole* startup string, so `./rust-link-sidecar & ./RustDedicated …` puts the preload on the sidecar. The server starts cleanly, reports nothing, and is **silently unmodded** — on the Carbon half only. Raised as a decision (own entrypoint vs. careful composition), not settled. ## A correction carried in the open rather than dropped An earlier commit on this branch recorded that the client key "authenticates and then lists zero servers" and reasoned from there to a second claim, that includes were broken on the panel. **Both were wrong and wrong the same way** — the servers were being deleted while the probing happened, so two calls minutes apart read two different worlds. §14.2 keeps it, because the reusable part is that a differential diagnosis across two API calls silently assumes the state did not move between them. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
wtclaude added 3 commits 2026-09-16 02:30:55 +00:00
Three new decisions of record, and a new reference for the second modding
framework.

R19 - the bridge plugin is framework-agnostic from now, not ported later.
Carbon is not a fork of Oxide but a separate loader shipping an Oxide
compatibility layer, so one .cs in the Oxide.Plugins namespace serves both,
with #if CARBON only where the APIs genuinely differ. Three existing
decisions take an amendment and none is reversed: R18's config walk roots at
Interface.Oxide.ConfigDirectory rather than a literal oxide/config (Carbon
uses carbon/configs AND lets an operator relocate every directory from the
command line); R2's permission store is Protobuf or SQLite on Carbon, which
permanently closes the file-reading shortcut it never planned to take, while
the PermissionExists pre-check survives because Carbon's bool return is the
one thing we cannot read portably; R4's doctor asks which framework rather
than whether Oxide, and gets a weaker "current enough" claim because Carbon
ships rolling release tags.

R20 - a Pterodactyl egg is a third supported deployment path beside the
installer and the hand install, derived from the community "Rust Autowipe"
egg, which already carries a FRAMEWORK variable offering vanilla/carbon/oxide.
The sidecar runs inside the game's container, which is what lets D2 stand
unchanged: a container's 127.0.0.1 is genuinely private, so the game link
stays loopback and stays unauthenticated. Lands in phase 18 beside the
installer.

R21 - both rigs move to the Pterodactyl panel, because Oxide and Carbon
cannot coexist in one install and so a single server cannot prove R19. Also
retires the wipe-day maintenance that dominated section 4, and makes the rig
Linux where every prior finding came from Windows and Mono.

New: modules/rust/CARBON.md, the difference list - file layout, the
permission store, the c. commands, 30 Carbon-only hooks, and 13 uMod names
Carbon's catalogue omits (at least two of which look like renames). Sourced
from Carbon's own published metadata and source at main, and labelled
throughout as not yet proven on a live Carbon server.

One outstanding request, recorded in section 3: the panel token on disk is an
application key and Pterodactyl puts files, power and console on the client
API, so iteration needs a ptlc_ key only the account holder can mint.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
The token file now carries both keys. The client key authenticates as the
admin owner and then lists zero servers, on every listing type and for both
the short identifier and the full UUID, so tier 2 of R21's deployment loop is
not usable yet. Reinstall and SFTP are unaffected.

Records the diagnosis rather than only the symptom, including one trap worth
not repeating: includes are independently broken on this panel, so
servers?include=user returns an empty list where the same route without the
include returns six. Nothing built on an include is evidence about ownership.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
Section 14 records R21's first rig, made with the application key and driven
with the client key. Both work; neither can do the other's job, and there is
no egg-write endpoint at all, so a published egg is a JSON file a human
imports.

The tier-2 loop was run rather than described: phase 1's real RunicGateway.cs
pushed from the working tree, oxide.reload through the client API, the edit
observed in Oxide's own log, source restored. About ten seconds end to end.
Three things worked that were not certain to - files/write creates missing
parents, a plugin placed before Oxide exists survives Oxide arriving, and the
plugin compiles and loads on Linux, which no previous phase had established.

Two findings that change decisions. The image installs the framework on EVERY
boot, Carbon from the rolling production_build tag and Oxide from
releases/latest, so a restart is a framework upgrade and neither is pinnable
through the egg. And R20's "wrapper launches the sidecar then RustDedicated"
does not survive Carbon: the entrypoint prepends LD_PRELOAD to the whole
startup string, so the preload would land on the sidecar and the server would
start cleanly, report nothing, and be silently unmodded.

R22 (org lead): the sidecar's configuration moves into the egg's variables.
Nearly free - rust-link already reads all five keys from the environment with
env-over-file-over-defaults precedence - but the game bind must not be
operator-editable, the web bind must derive from an allocation, and the db
path must not be able to agree with REMOVE_FILES.

Also corrects an earlier claim in this branch. The client key listing zero
servers and includes returning empty were both CORRECT; the servers were
being deleted between reads. A differential diagnosis across two API calls
assumes the state did not move between them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
wtclaude added 1 commit 2026-09-16 02:51:18 +00:00
rust-carbon (id 18, 87fb1f67) is built on the same egg with FRAMEWORK=carbon,
a clean install rather than a converted one. Install 122s, boot 543s.

R19 is proven. The byte-identical RunicGateway.cs that runs on the Oxide rig,
with no conditional compilation in it at all, loaded on Carbon 2.0.259.0 and
behaved identically - same startup line, same no-stall retry against an absent
sidecar. That contract now holds on three platforms from one source file:
Windows/Mono, Linux/Oxide, Linux/Carbon.

R18's amendment is confirmed the best way it could have been. The plugin's own
config, written by the same Oxide-compat API on both rigs, landed at
oxide/config/RunicGateway.json on one and carbon/configs/RunicGateway.json on
the other. Had the R18 editor used a literal path it would not have found its
own plugin's config on half of all installs, and no test would have caught it.

One claim is REFUTED, and it was wrong about Oxide as well as Carbon. CARBON.md
said Oxide stores permissions as JSON and Carbon as Protobuf or SQLite. Both
rigs say otherwise: both store Protobuf, under IDENTICAL filenames
(oxide.users.data, oxide.groups.data), differing only in directory - Carbon
writes its own data into files named after Oxide. This strengthens R2's
API-only rule rather than weakening it: a file reader would have worked on both
rigs today and broken silently for the one operator who ran c.migrate_perms_sql.

Two things nobody had thought to claim, found by looking. Carbon auto-creates a
third default group, moderator, which R2's full-set push must tolerate or
report drift for ever. And c.plugins exposes per-plugin hook telemetry that is
most of section 6's fired-hook mechanism, free, and only on Carbon.

Also: a wrong console command is indistinguishable from success at the API.
Pterodactyl's command endpoint returns 204 either way and Carbon prints nothing
for an unknown command, so oxide.plugins on Carbon looks exactly like it
worked. Anything driving the console must read a log to know.

CARBON.md gains a scorecard (section 10) and drops its unproven banner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
whitlocktech merged commit 3c8b430fae into main 2026-09-16 02:54:25 +00:00
whitlocktech deleted branch docs/rust-carbon-and-pterodactyl 2026-09-16 02:54:26 +00:00
Sign in to join this conversation.
No description provided.