docs(modules): the module-rust plan — 18 decisions of record and a 21-phase schedule #249
Reference in New Issue
Block a user
No description provided.
Delete Branch "docs/rust-module-plan"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What & why
The dry run designed
module-ruston paper and deliberately did not build it. This is the document that builds it:docs/modules/rust/PLAN.md, ~1,000 lines — eighteen decisions of record, a 21-phase schedule across five repos, an event catalogue and an engagement catalogue.Three repos were created empty for it today:
Module-Rust,Rust-Link,Rust-Plugins.The decisions
registerAuthProvideratMODULE_API_VERSION1.10.0error--game servuo|rust.csdownloadsadmin.users.detailandsite.footer.status/ruston all three tiersFour findings worth the review time
R2 is a direction the Integration Kit has no chapter for. Chapters 3–4 are the read path; chapter 5 is ledgered one-shot commands. Site-owned permissions is neither — continuously reconciled state where the website is authoritative. Its nearest relative in the contract is the Team provider inverted. Whether that earns a sixth chapter is phase 19's question.
R5 reversed its own reasoning when the source was read. For Teams the uMod Clans plugin is worse than first-party, not richer: fifteen
[HookMethod]s and every one is a mutation, no read API at all, and three hooks raised — none a membership transition. It cannot answer core's three provider questions. First-party is permanent for the provider, not a first step.R16 dissolved a problem rather than solving one.
GiveKitneeds a connectedBasePlayer, so an event firing at 03:00 rewarded only whoever was online. Granting the kit'sRequiredPermissioninstead means an entitlement waits — which removes the pending-grant queue entirely, makes it the same machinery as R2, and makesreversible: 'ledger'honest where a direct grant could only ever be'none'. The action had been declared around the wrong noun.The contract audit (§7) cost twelve phases. The first draft was built from the game-facing chapters and under-planned the website module badly — the template makes eight of the ten non-event registrations and the plan covered three.
purge.sql, the mount prefixes, the swagger fragment, the vite alias mechanism,checkImports,declareModuleSlotand the entire packaging and release path were all missing.How it was tested
Documentation only — no code. Every factual claim was verified against a source rather than recalled:
registerAuthProviderabsent at 1.10.0, and the ten-plus-four registration list, checked againstMODULE_API.mdandintegration-kit/template/server/index.js.GiveKitreturn contract, Kits'RequiredPermissionmodel, Clans' missing read API, ZoneManager'sEraseTemporaryZone(owner, …)and BetterChat'sAPI_RegisterThirdPartyTitleall come from the source.OnPluginLoaded/OnPluginUnloaded, the seven first-party Clan hooks and the twelve Team hooks, from the mirror indocs/modules/rust/./api/v1/public/modulesvs/api/v1/public/statusvs/api/health, checked inwebsite/server.service.rsandmain.rsmention ServUO zero times;OverlayComponentandServUoCompatname the game in the bundle schema.D:\rust, Oxide 2.0.7585, world save present,oxide/plugins/empty. Itsstart.batwas fixed in passing: it updatedC:\rustserverand launchedD:\rust, so the server that boots had never been updated by its own script.The plan is not started; nothing here changes behaviour.
Checklist
AI-assisted contributions (required)
Claude Code (Opus 5). I have reviewed and understandevery change, and take responsibility for it. AI-authored commits are
marked with a
Co-Authored-By/Assisted-Bytrailer.License
(GNU GPL v3.0 or later), and I have the right to contribute it.
🤖 Generated with Claude Code
https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
An admin edits any loaded plugin's configuration from the website and it reloads automatically. Base tier generates a form from the config VALUES themselves - boolean to toggle, number to numeric field, string to text, array to list, nested object to group - so it works for whatever plugins happen to be installed, including ones added after we ship. Advanced tier is raw JSON. Same posture as R2, the site as authority over the game host, but a different SHAPE: R2 is continuously reconciled state pushed on connect, this is request/reply on demand. It must not be built on the permission mirror. The mechanics, all verified: configs at oxide/config/<Plugin>.json, oxide.reload rereads one, and OnPluginLoaded / OnPluginUnloaded are real hooks in the Server category - so whether a reload actually SUCCEEDED is observable rather than assumed. That is what makes the feature safe. The trap that would silently corrupt every float: JavaScript cannot tell 1 from 1.0, and Oxide configs deserialize into typed C# classes. JSON.parse of {"Rate":1.0} yields the number 1 and JSON.stringify writes it back as 1, so a naive read-modify-write rewrites every whole-numbered float as an integer, on fields nobody touched. Newtonsoft may coerce it or may throw, and a throw at load means the plugin does not come back. So never parse the whole document, mutate and re-serialize - edit textually, or use a parser that preserves number literals. The fields at risk are exactly the ones a Rust server tunes: gather rates, multipliers, scales. Five more limits of inferring a schema from values are recorded, since the feature's whole promise is that it works without knowing the plugin: empty arrays and null carry no type; enum-like strings are indistinguishable from free text; there are no descriptions, minimums or maximums, so the key name is the entire label; nested objects need recursion with a depth limit and a raw-JSON fallback; and the file after a reload may not be what we wrote, because Oxide merges missing defaults and saves. Safety needs more than usual here, because a bad config does not fail the write, it fails the next LOAD and the plugin stays down - and R6/R17 make four plugins required, so a broken ZoneManager config takes event participation with it. The write path is: read with a version and require it back on write so a concurrent on-disk edit conflicts rather than being clobbered; validate it parses; back up, write, reload; then watch for OnPluginLoaded within a window and, if it does not arrive, restore the backup and reload again AUTOMATICALLY. That rollback is the feature's real content - without it this is a web form that can take the shard's plugins down one typo at a time. Two more obligations. Plugin configs routinely hold API keys and Discord webhooks, so a config reader hands those to anyone who can open the page: mask values whose key matches key/token/secret/password/webhook and treat them write-only, as the platform already treats the uo-link token. And gate it on its own site permission with an audit trail of who changed which key from what to what and whether the reload succeeded - it is an admin writing to the game host's filesystem, the most powerful thing the site can do to a server. One distinction kept explicit: editing a config FILE is not a lease. A lease borrows a convar for a while and the game restores it on a deadline; this writes a file and is permanent until someone changes it back. They look similar from a web form and an event should never reach for this one. Lands as phase 7b, beside permissions, since it shares the admin surface and the gating. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4