Commit Graph

649 Commits

Author SHA1 Message Date
af0e86e367 Merge pull request 'docs(rust): phase 13b as built — the rewards (PLAN.md §29.6–29.9, PROTOCOL.md §16)' (#270) from docs/rust-phase-13b-built into main
Reviewed-on: #270
2026-09-24 13:01:07 +00:00
86dc0ac804 docs(rust): phase 13b as built — the rewards (PLAN.md §29.6–29.9, PROTOCOL.md §16)
- PLAN.md §29.6: D106 (the news switch is a card on the visibility page),
  D107 (tally.open behind EventsEnabled), D108 (a tally is forgotten seven
  days after it opened).
- §29.7 as built: where the build departed from §29.3 (presence asked of
  ZoneManager, idem_key on the run-grant rows, fixed-choice sources) and
  the walk, on both rigs, without a player.
- §29.8 findings: real core refused a camelCase option-source id; Carbon
  refuses a grant to a Steam id it has never seen; panel-created data
  directories are not game-writable.
- §29.9 what is not proven: the player half, now PLAYER_WALK.md's rewards walk.
- PROTOCOL.md §16: protocol 10 — the tally, kits.list, kit credits on
  perm.sync, chat.say, three bounds, the sidecar's five routes.
- The 13b phase row, and §9/§10 correction notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-24 07:49:00 -05:00
5777e0757c Merge pull request 'docs(rust): phase 13b plan of record (PLAN.md §29)' (#269) from docs/rust-phase-13b into main
Reviewed-on: #269
2026-09-24 11:33:08 +00:00
fb12b18efc docs(rust): phase 13b plan of record (PLAN.md §29)
The rewards half of phase 13, written before its code: the participation
tally counted by the game, rust.kit.entitle through the site mirror's per-run
rows plus one extra kit use per reward, and rust.announce with the announce
leg. Records D98-D105 (org lead, 2026-09-24), the Kits 4.4.9 facts they rest
on, the catalogue, what each repo builds, the eight-step walk, and four
readings left open for review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-24 05:31:01 -05:00
37bc06234d Merge pull request 'docs(rust): phase 13 plan, and 13a as built (PLAN.md §28, PROTOCOL.md §15)' (#268) from docs/rust-phase-13 into main
Reviewed-on: #268
2026-09-24 10:07:16 +00:00
a39d860123 docs(rust-link): make the events walk's zone checks runnable
ZoneManager's listing command is `zone_list`, and it has no command that
says who is inside a zone. The zone steps now use `rgp.inzone`, a rig-only
probe command that asks ZoneManager's own IsPlayerInZone for every connected
player. They also no longer claim rg.hooks counts zone entries: the bridge
subscribes to those hooks in 13b.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-24 04:11:14 -05:00
c385de7155 docs(rust-link): the events player walk, and the permission walk's protocol-9 note
PLAYER_WALK.md gains the events walk: the checks phases 12 and 13a left for a
person on the rig (a kit unlocking under a group-permission lease, standing
in a zone, a zone surviving a restart, looting and killing what an event
placed, a zone expiring around a player, and spawn.max_* with players on).
The permission walk now says it can only pass on protocol 9, because the
phase-7 owner bug made its steps 1-3 impossible on Oxide until then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-24 04:09:52 -05:00
9c46b06172 docs(rust): phase 13a as built — protocol 9, D96–D97, the walk and its findings
- PLAN.md §28.5–28.8: two more org-lead decisions (D96 required zone minutes
  held by the game; D97 one placing verb per kind, because core infers cap
  boxes from examples), the seven-step walk on both rigs, the defect a
  mid-run restart found (the reconcile asked a world that had not loaded and
  the plugin pruned live crates) and its fix, and what is not proven.
- PROTOCOL.md §15: protocol 9 — the five world commands, the registry keyed
  by the website's key, what a restart and a wipe do, `worldReady`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-24 03:14:49 -05:00
0de9d7f90d docs(rust): phase 13 plan of record — split 13a/13b, D80–D95 (PLAN.md §28)
Phase 13 splits into 13a (the world verbs, the ownership registry and the
boot/wipe reconcile watch) and 13b (the participation tally, the kit
entitlement and announce). Records the org lead's sixteen decisions and the
rig facts they rest on: monument names repeat, crates keep their net id
across a restart while NPCs do not, and ZoneManager erases a plugin's
temporary zones when that plugin unloads. Also links the three core
findings phase 12 raised, now filed as website#205-#207.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-24 00:36:33 -05:00
68989c247b Merge pull request 'docs(rust): phase 12 — the leases (PLAN.md §27, PROTOCOL.md §14)' (#267) from docs/rust-phase-12 into main
Reviewed-on: #267
2026-09-24 04:02:45 +00:00
5f18097223 docs(rust): phase 12 as built and walked — §27.5-27.7, PROTOCOL.md §14
Walked on both rigs through real core: 21 keys observed live, the game's own deadline with core stopped, drift, reload and restart mid-hold, and the switch. Records two defects fixed in the phase and four found outside it and raised: the phase-7 grant owner bug, core restoring orphaned lease rows over a later run, core.lease dropping retry:false, and the 10s budget squeeze.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-23 21:14:09 -05:00
46b565ac9b docs(rust): phase 12 plan of record — the leases (PLAN.md §27, D73-D79)
The rig disproved three of §9's rows: vanilla Rust has no gather, craft
or smelt rate convar. core.lease never tells a module which server, so
the target names it. And every scheduled run has the empty scope, so
"caps are per server" holds only for runs an admin starts by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-23 20:17:23 -05:00
4049abc41a Merge pull request 'docs(rust): phase 11 — a Rust notification on a phone (PLAN.md §26, android M16)' (#266) from docs/rust-phase-11 into main
Reviewed-on: #266
2026-09-23 20:49:14 +00:00
f1b8329540 docs(rust): phase 11 as built and walked — §26.4-26.6, M16
The raid alert reached an emulator through ntfy.sh 19 seconds after a
rig raid, titled from its inbox row; switched off in the app, the next
raid enqueued in-app only. Closes M7's open QA item (on-device delivery
against a live ntfy), and amends the M7 and engagement phase 8 passages
D69 and D70 made stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-23 15:40:02 -05:00
83e57e6f47 docs(rust): phase 11 plan of record — a Rust notification on a phone (PLAN.md §26, M16)
Four org-lead decisions (D69-D72): the linked-account check asks the
site's own module and never gates switching off; a tickle is titled
from the inbox item its ref names; /player/rust and ?tab= open
natively; the relay hop is walked on ntfy.sh, closing M7's open QA item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-23 15:19:05 -05:00
b7d9872fed Merge pull request 'docs(rust): phase 10 — notifications and engagement (PLAN.md §25, PROTOCOL.md §13)' (#265) from docs/rust-phase-10 into main
Reviewed-on: #265
2026-09-23 18:43:14 +00:00
cb8e8853f6 docs(rust): phase 10 as built — protocol 7 and the raid alert
PLAN.md §25.5-25.8: what shipped, the walk that met the criterion on the
Oxide rig, the four things the rig found (every generic notice never
said which server; attackerId null for a spawned player; a disband
roster gone before the disband was read; in-app `subscribers` being
everyone), and what is not proven. PROTOCOL.md §13 specifies protocol 7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-23 13:41:01 -05:00
aa148adfc6 docs(rust): phase 10 plan of record (PLAN.md §25, D59-D68)
The notifications and engagement phase, as decided by the org lead on
2026-09-23: the raid alert goes to everyone authorised on the tool
cupboard, covers doors and external walls, never names the raider, and
fires online or offline with the choice left to a rule condition.
Nothing reaches the game this phase, which amends R7. Also corrects §10
where core's contract disagreed with it (there is no `self` ceiling).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-23 05:50:24 -05:00
edff0e3695 Merge pull request 'docs(rust): phase 9 — Teams from first-party clans (PLAN.md §24, PROTOCOL.md §12)' (#264) from docs/rust-phase-9 into main
Reviewed-on: #264
2026-09-23 10:31:35 +00:00
eea4f958b2 docs(rust): phase 9 as built — protocol 6 and first-party clans as Teams
PLAN.md §24.4-24.7: walked on the Oxide rig end to end and on Carbon
hook by hook; what the rig found (the refusal-first reconcile, the
dropped first feed line and its re-offer, core's 40-character dedupe
key, one module per site). R6 amended by D47. PROTOCOL.md §12 specifies
protocol 6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-23 05:28:16 -05:00
de25ed3276 docs(rust): phase 9 plan of record (PLAN.md §24, D47-D58)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-23 01:27:35 -05:00
a4c4476c3e Merge pull request 'docs(modules): the presence fix — nothing names who is online by default' (#262) from docs/rust-presence-fix into main
Reviewed-on: #262
2026-09-23 05:36:05 +00:00
3b7b9cca4f docs(modules): the presence fix - nothing names who is online by default
modules/rust/PLAN.md gains §23: the org lead's rule (2026-09-22) that
nothing names who is online by default, the five decisions it needed
(D42-D46), the fourth surface the rule reached (the leaderboard's
lastSeen), the viewer re-read from the users row, and a defect four
phases old that the walk found - every empty state in module-rust
rendered as a blank box, because core's EmptyState renders children and
the Integration Kit's template teaches title/message.

rust-link/PROTOCOL.md reclassifies six §8.4 kinds from public to
presence - public with an operator-chosen audience - with no wire change.

android/PLAN.md records the app's half as an amendment to M14.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-23 00:32:17 -05:00
eb4a8bcafc Merge pull request 'docs(modules): phase 8 as built, and the rig that put the sidecar where the design says it lives' (#261) from docs/rust-phase-8 into main
Reviewed-on: #261
2026-09-23 01:51:51 +00:00
11b8965aa7 docs(modules): phase 8 as built, and the rig that finally put the sidecar where the design says it lives
Three documents:

* `modules/rust/PLAN.md` §22 — the phase as built. Three org-lead decisions
  (D39-D41), what a player is told and what they are not, the refusals on a
  phone, and an honest limit the rig found: a rank can be live while every
  permission it carries resolves nowhere.
* `rust-link/INSTALL_RIG.md` — new. The sidecar runs INSIDE the game
  container now, which is the shape R20 says the egg ships and which retires
  the firewall wall phases 6, 7 and 7b each stopped at. A container's
  127.0.0.1 is genuinely private, so a stock plugin config and a stock
  sidecar find each other with nothing configured at all. Three things in
  the launcher are load-bearing and each is written down with the failure it
  prevents.
* `rust-link/PLAYER_WALK.md` — the account walk on a phone, and a correction:
  7b's "it needs a firewall rule on a development machine" is no longer true.

`android/PLAN.md` gains M15.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM
2026-09-22 20:35:46 -05:00
15d64b28fe Merge pull request 'docs(modules): phase 7b as built — configuration from the site, and the undo that makes it safe' (#260) from docs/rust-phase-7b into main
Reviewed-on: #260
2026-09-22 15:01:26 +00:00
7f008fd1f3 docs(modules): the Carbon rig answers too, and its root is a different directory
R19's byte-identical plugin now carries protocol 5 on both frameworks:
`framework=oxide root=/home/container/oxide/config` on one rig and
`framework=carbon root=/home/container/carbon/configs` on the other, from the
same file. That is R18's amendment proven rather than argued — a literal
`oxide/config/` in this editor would have missed every config on half of all
installs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM
2026-09-22 08:58:21 -05:00
82145d3b4a docs(modules): phase 7b as built — configuration from the site, and the undo that makes it safe
PROTOCOL.md §11 specifies protocol 5: the walk rooted at the framework's own
config directory, the version a write must present back, the set-shaped write,
the watched reload and the automatic restore. PLAN.md §21 is the phase as built
— four org-lead decisions, the float trap and what avoiding it cost, and four
defects a browser found that 179 green tests did not.

PLAYER_WALK.md gains the configuration walk, because the acceptance line needs
the sidecar and the game on one host and the rig cannot reach one yet.
INTEGRATION.md gains the four things an operator should know before they edit a
plugin's settings from a web page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM
2026-09-22 08:55:39 -05:00
132205f0f4 Merge pull request 'docs(modules): phase 7 as built — the permission mirror, and the set arithmetic behind it' (#259) from docs/rust-phase-7 into main
Reviewed-on: #259
2026-09-22 06:53:00 +00:00
bba2ab04e0 docs(modules): phase 7 as built — the permission mirror, and the set arithmetic behind it
Protocol 4 (`PROTOCOL.md` §10), phase 7 as built (`PLAN.md` §20), what an operator
needs to know about it (`INTEGRATION.md`), and the in-game leg as a walk to run
(`PLAYER_WALK.md`).

**The spec.** One verb carrying the whole desired set, diffed by the plugin
against the live store; a report whose two interesting fields are the ways a push
looks like it worked and did not (`unresolved`, `pending`); drift as a report
rather than an action; and the permission hooks as a live SIGNAL rather than the
record — a hook that stops firing costs latency, not correctness.

**The finding the design turns on, written where it belongs.** A name in the store
that is not in the desired set is either something the site retired or something a
human granted, and those have opposite correct answers. The store records who
granted a permission nowhere, so only the website can tell them apart — which is
why it keeps a ledger of what it pushed, and why revoking a hand edit needed a
table of its own.

**§10.5 is a rule generalising.** "A wedged sidecar must never stall the game"
becomes "nothing the far side sends may cost the main thread unbounded work",
because `perm.sync` is the first command whose work is not bounded by its own
shape. Three bounds, each on the side that can say something useful when it is hit.

**§20.7 says plainly what is not proven**: the acceptance line needs a second,
non-admin Steam account on the rig, and nothing in the plugin has been compiled.
The walk doc carries the seven steps, including the two things to confirm on
Carbon rather than assume.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM
2026-09-21 22:59:24 -05:00
4fe8864939 Merge pull request 'docs(modules): phase 6 as built — identity, and the sentence a player could not see' (#258) from docs/rust-phase-6 into main
Reviewed-on: #258
2026-09-21 22:24:42 +00:00
e35880e713 docs(modules): phase 6 as built — identity, and the sentence a player could not see
Protocol 3 in PROTOCOL.md §9, the identity walk in PLAYER_WALK.md, what an
operator needs in INTEGRATION.md, and PLAN.md §19.

Seven org-lead decisions (§19.0): /link is chat and its reply is private, codes
live in plugin memory as the UO bridge does, an alphabet with no O/0/I/1, a
Steam id another account holds is refused rather than moved, the website asks
EVERY server because a code does not say which one minted it, staff can sever a
link, and the activity-row overflow belongs to core.

§19.3 is the finding worth reading: a slot router is not registered under a tier,
so this repo own OpenAPI generator described two routes fewer than the module
serves — internally consistent, and wrong. The frozen-manifest job catches it,
which was verified by deleting the two paths and watching it fail.

§19.4 is what a browser found and 122 green tests did not. Core request
primitive reads data.message; this module has answered { error } since phase 1,
so every refusal this phase exists to write rendered as Service Unavailable.

The code-from-the-game half is written down rather than claimed: a code reaches
a player and nobody else, so no console can read one (D27).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM
2026-09-21 09:06:51 -05:00
54b4059091 Merge pull request 'docs(modules): phase 5 as built — Android leg A, and the capability a client had to be given' (#257) from docs/rust-phase-5 into main
Reviewed-on: #257
2026-09-17 09:21:44 +00:00
dfdb0a3f63 docs(modules): phase 5 as built — Android leg A, and the capability a client had to be given
module-rust's surface gets its second client. `modules/rust/PLAN.md` §18 records
the phase; `android/PLAN.md` M14 records the app's half, as every Android
milestone does.

The decision worth the most words is D16. This module declared five capability
strings and every one named a SURFACE — `servers`, `killfeed`, `leaderboard`,
`presence`, `wipes` — while a client gating a navigation group needs one that
names the MODULE. Core flattens every started module's capabilities into a single
list, so `servers` is a word another module could declare tomorrow and silently
reveal these screens on a site that does not run Rust. Gating on the module `id`
was considered and rejected in as many words: `id` is a mount prefix, §2.9
forbids inferring a route from a capability, and letting a client gate on `id`
makes the two the same value in practice.

Also recorded: D17 (poll while RESUMED — the phone's Page Visibility gate), D18
(the Rust repositories move to `edge`, releases at the cutover), D19 (the drawer
badge, and NavPaths learning `/rust`), why D15's footer slot had to be translated
rather than copied, and §18.4's three defects — none of which a green suite of
644 could see, because each is about what a screen looks like or when a number is
re-read.

§18.6 is deliberately short and honest: the website's own pages have still not
been read at phone width, and the badge's non-zero case was shown with a seeded
count rather than by people playing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
2026-09-17 03:45:01 -05:00
2f24236993 Merge pull request 'docs(modules): phase 4 as built — the first pages, and the four defects a browser walk found' (#256) from docs/rust-phase-4 into main
Reviewed-on: #256
2026-09-17 02:42:51 +00:00
788100e048 docs(modules): phase 4 as built — the first pages, and the four defects a browser walk found
Records PLAN.md section 17 and marks the phase row done. Four decisions (D12-D15),
what is on the pages, and the four defects only a live walk could find — two of
them in code phase 3 had already shipped: an unreachable refresh that erased the
server's description, and a "last reported" line reading the timestamp of our own
poll rather than of the server's last frame.

Also written down: why core's useAsync cannot poll, the real per-page cost of a
live count in the footer slot, and the test fake that was *nearly* core.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
2026-09-16 21:40:39 -05:00
a221d4409b Merge pull request 'docs(rust-link): protocol 2 — the read path, and phase 3 as built' (#255) from docs/rust-protocol-2 into main
Reviewed-on: #255
2026-09-16 16:36:42 +00:00
f80b9f95c8 docs(modules): the ingest walk, and the two-writer trap it walked into
The module half exercised against the live rig rather than only its own tests:
the working tree installed into a running core, pointed at the workstation
sidecar with a real Rust server behind it. The fresh-install path logged
`cursor started at the feed tail`, a console banid/unban arrived as two
rust_events rows carrying the wipe id, and the wipe row was created by being
mentioned rather than by any "a wipe started" call.

Then the boundary on real rows: with both ban events in the table, the public
events route answered {"events":[]} — and answered the same when asked for
player.banned BY NAME.

The trap is worth more than the proof. The state row read as online=1 with
hostname=NULL, a shape no code path writes. Two cores shared one database: a
second instance still held the phase 2 release in memory, spoke protocol 1, was
refused 409, and rewrote the row every thirty seconds. Nothing was wrong with
either. What made it look like a defect was two SELECTs in two round trips
assuming the state did not move between them — the same mistake the Pterodactyl
work recorded, and a single atomic read caught the coherent row alternating with
the other writer's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
2026-09-16 08:52:48 -05:00
30e72adfcf docs(rust-link): protocol 2 — the read path, and phase 3 as built
The specification the other three repositories are held against, plus the phase
record.

`PROTOCOL.md` §8 is the new contract. Its centre is one field: every frame now
carries `type` — `event`, `snapshot`, `reply`, `control` — and the sidecar files
on that and nothing else. That is the dumb-forwarder property made structural
rather than intended: ten new event kinds are zero change in Rust-Link, and only
a version adding an indexed column touches it at all.

Also in §8: the fifteen-kind catalogue and what each frame carries; `wipeId`
derived by the plugin, which REVERSES §3.2's "deriving one is the website's job"
and says why; boards re-sent on connect and on a cadence; the aggregate rule (a
hook that can fire more than once a second per player is a counter, not an
event); the void rule that stops a read-path hook vetoing a death or a login; and
`GET /feed`, a cursor route separate from `/events` because one route with two
orderings serves the wrong one to every caller that forgets the parameter.

§8.5 is the part to read twice. The classification of a kind as public or staff
is NOT on the wire, deliberately: a boundary declared by the sender is one a
compromised or out-of-date game host can widen, so the module holds a
default-deny allowlist and this table is what its test holds it against.

§8.8 corrects a catalogue rather than a defect: PLAN.md §10 sources
`rust.login.denied` from `CanUserLogin`, and that hook fires on every attempt —
the only way to learn of a denial from it is to be the denier. A denial is the
absence of an approval, and protocol 2 emits both facts so phase 10 can pair them.

`PLAYER_WALK.md` is new, and it exists because half this catalogue cannot fire
without somebody holding a mouse. Ten steps, what each one should produce, and
what counts as a pass — written so the walk can be run without watching the
output live, and so the answer afterwards is readable as a transcript.

PLAN.md §16 is phase 3 as built: the four decisions, the two defects only a
server that BOOTED with the plugin could find (a wipe id that was null for every
real session, and a two-second main-thread stall on unload), what was proven and
how, and — stated plainly rather than implied — the three measurements still
queued on the org lead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
2026-09-16 08:39:27 -05:00
ddf777fd8c Merge pull request 'docs(modules): phase 2 as built — packaging, release and the frozen manifest' (#254) from docs/rust-phase-2 into main
Reviewed-on: #254
2026-09-16 10:51:01 +00:00
5274d5a744 docs(modules): phase 2 as built — packaging, release and the frozen manifest
PLAN.md gains §15 and the phase-2 row is marked done.

What the phase found worth recording:

- The starting position was worse than the row implied: phase 1 built five
  guards and ran every one by hand, because the repo had no .gitea/workflows at
  all. So the org lead widened the phase to include the gate, not only the
  release (D4), and the frozen-manifest job shipped with it rather than waiting.
- That job answers a question §13 had to take on trust. Core mounts /status and
  /version at a tier root where the loader's collision probe cannot see them, so
  phase 1 could only check /rust by reading. Locally and in CI, against the same
  pinned core: 280 core routes, 6 added, all documented, none of core's moved.
- The bundle carries no node_modules (D5), and checkBundle.js asserts the
  emptiness rather than assuming it — the day a dependency appears is meant to
  be a loud day. Verified by breaking the include list.
- No Sonar for the three Rust repos until phase 19 (D6).
- The criterion is met. v0.1.0 published, downloaded, hashed independently
  (7296c76b… matches the manifest), and installed from its manifest URL through
  Admin → Modules into a core with the phase-1 directory and row torn down
  first. The volume received exactly the twenty files the include list names;
  the row reads started with a source and a sha where phase 1's had nulls; the
  public route answers 200, the player and admin tiers 401, and all five /rust
  paths merged into core's own spec.
- REGISTRY_TOKEN was already configured — the one prerequisite this work could
  not check for itself, since the bot can read no Actions secrets.
- One rig finding: Docker Desktop's Linux engine was found dead and answered
  every call with a bare 500, which presents as the website being broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
2026-09-16 05:42:36 -05:00
3c8b430fae Merge pull request 'docs(modules): module-rust supports Carbon too, ships a Pterodactyl egg, and the rigs move to the panel' (#253) from docs/rust-carbon-and-pterodactyl into main
Reviewed-on: #253
2026-09-16 02:54:23 +00:00
22159ec78f docs(modules): the Carbon rig, R19 proven, and one refuted claim
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
2026-09-15 21:51:11 -05:00
069e715b1b docs(modules): the Pterodactyl rig as built, R22, and a correction
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
2026-09-15 21:30:06 -05:00
a29cdf0fae docs(modules): record the Pterodactyl client key and its empty-server-list problem
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
2026-09-15 21:08:15 -05:00
5890da633f docs(modules): module-rust supports Carbon too, and ships a Pterodactyl egg
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
2026-09-15 20:59:23 -05:00
b4b05b4108 Merge pull request 'docs(modules): module-rust phase 1 as built — the transport, and three org-lead decisions' (#252) from docs/rust-phase-1 into main
Reviewed-on: #252
2026-09-16 01:29:48 +00:00
bd83b34614 docs(modules): module-rust phase 1 as built — the transport, and three org-lead decisions
Adds docs/rust-link/, the canonical spec for the Rust bridge: PROTOCOL.md (the
game link and the website API) and INTEGRATION.md (standing it up by hand, and
which of the three components is wrong when it does not work). A new top-level
directory mirroring link/, which is decision D1 below — it keeps the uo/link
symmetry and keeps module docs separate from bridge docs.

Records phase 1 in PLAN.md as section 13. Both criteria met: a server.hello
produced by the live Rust rig travelled game → sidecar → module → the public
website API, killing the sidecar left the game untouched, and all five guards are
green on the module skeleton.

Three org-lead decisions this phase needed, none settled by section 2:

* D1 — the bridge docs live at docs/rust-link/.
* D2 — loopback is the ONLY trust boundary on the game link, no token, exactly
  as on the ServUO bridge. Argued the other way on the grounds that Rust servers
  are far more often on GSPs; overruled, and the consequence is now written down
  as the mistake rather than defended against.
* D3 — the plugin reads Oxide's own config file, so it lands inside the phase-7b
  config editor for free. The argument against — that editing Host/Port from the
  website could cut the link carrying the edit — becomes that phase's guard
  rather than a reason for a second config mechanism.

Section 11.3 is corrected in place: it read module.json's "extensions" array as
held against reality by the loader in the way "mounts" is. Only half true. The
loader checks that a named slot EXISTS and never that the module filled it —
checkDeclared covers "mounts" alone — and only ONE of R13's two slots can be
declared there at all, because site.footer.status is a CLIENT slot and naming it
fails the load outright.

Section 13.3 records five defects a running server found that no test could,
including a bootId that regenerated on every plugin load rather than every server
start — which would have asked core to reconcile its whole ledger on every
oxide.reload, for a world that never moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
2026-09-15 19:55:35 -05:00
80c7a9dcd1 Merge pull request 'docs(modules): module-rust phase 0 as built — the rig is current, and four plan claims were wrong' (#251) from docs/rust-phase-0 into main
Reviewed-on: #251
2026-09-15 21:42:57 +00:00
b12c6dfde4 docs(modules): phase 0 fully closed — both zone transitions observed live
The org lead connected, so the occupancy criterion is met: a zone created
on the player's own position reported OnEnterZone, occupancy=1 by name,
and OnExitZone. R17's "presence transitions as events" is verified, which
is what the participation ledger and advance conditions rest on.

The exit was produced by relocating the zone rather than walking the
player out — CreateOrUpdateZone on an existing id moves the trigger
volume and fires OnExitZone. Useful for testing presence unattended.

Two ceilings recorded, both constraining later phases:

- No console session can observe a gate (the !player || idiom).
- An admin account cannot see a refusal from most plugins either, and the
  bypass is NOT uniform. PopupNotifications (player.IsAdmin) and
  ZoneManager (authLevel > 0) hard-bypass; Kits' RequiredPermission gate
  is config-gated with a shipped default of false, and Kits' own IsAdmin
  is a permission rather than auth level. So phase 13 is demonstrable on
  this rig as it stands and phase 7 is not — phase 7 needs a second,
  non-admin Steam account.

Adds §12.6: R18's two trees as they actually look now the configs exist.
Its data/ inventory was exactly right, but config/ is FLAT on a fresh
base set — it is data/ that nests, i.e. the nesting R18 cites to justify
the recursive walk is currently only visible in the tree it must never
walk. Also: oxide/data/ holds Oxide's own permission store, so straying
one directory over would edit R2's mirror underneath itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
2026-09-15 16:40:08 -05:00