feat(admin): staff write plane + help-page queue #1

Merged
whitlocktech merged 5 commits from feature/admin-controls into main 2026-07-13 08:39:45 +00:00
Member

Adds a staff write plane to the bridge (the site can now moderate the live shard) plus the in-game help-page (support) queue. Design and decisions are in docs/ADMIN_CONTROLS.md; the shipped scope is Phase 1 + Phase 2 (Phase 3 is not planned).

What's included

Phase 1 — account & session moderation (BridgeAdmin.cs + config)

  • admin.kick / admin.ban (timed + indefinite) / admin.unban / admin.broadcast
  • Opt-in per shard (AdminWriteEnabled, default off); required actor; hard Owner-protection floor (AdminAccessFloor, default CoOwner) so a compromised sidecar can never touch the Owner
  • admin.ok / admin.error replies; every applied action emits an admin.audit (origin:"web") event; attribution is web:<actor>
  • Sidecar: POST /admin/{kick,ban,unban,broadcast} with a status mapping (200 / 403 floor / 404 unknown / 400 missing-actor / 401 no-token)

Bidirectional audit (§5.5 — patches/commandlogging-event.patch + patches/BridgeModerationAudit.cs)

  • In-game bans/kicks/broadcasts are forwarded to the site as admin.audit (origin:"in-game"), so the moderation log is complete regardless of origin
  • The patch adds CommandLogging.OnWrite; the subscriber lives in patches/ (references a post-patch symbol), applied like the Phase-7 vendor-sale patch

Phase 2 — help-page queue (BridgePages.cs)

  • Polled + diffed (no EventSink), keyed by sender serial → page.new / page.updated / page.closed
  • Inbound pages.snapshot, page.respond (delivers a staff reply to the player; shows as "Staff"; optional close), page.close
  • Sidecar: GET /pages, POST /pages/{id}/respond, POST /pages/{id}/close

Verification

Every slice was verified live against a booted ServUO + the real sidecar, then the server was shut down and test artifacts reverted:

  • Admin verbs: all four succeed; the Owner-floor refusal, unknown-target 404, missing-actor 400, and no-token 401 all confirmed
  • Bidirectional audit: a genuine [bcast and resolved ban/kick log lines produced the right admin.audit frames with the target parsed; non-moderation lines ignored
  • Page queue: snapshot, page.new ×2, respond, close (page removed), page.closed, and 404 on an unknown page

Deploy notes

  • The plugin write plane and page queue are drop-in overlay/ (deployed by deploy.ps1).
  • The bidirectional-audit slice needs the one-line patches/commandlogging-event.patch applied and patches/BridgeModerationAudit.cs copied in — see patches/README.md.
  • The write plane is off by default; set AdminWriteEnabled=true in Bridge.cfg to enable it. Authorization is enforced on the website (admin/moderator roles).

Downstream/remaining is website-side only: the admin UI (moderation log consuming admin.audit, support-queue view over page.* + /pages).

🤖 Generated with Claude Code

Adds a staff **write plane** to the bridge (the site can now moderate the live shard) plus the in-game **help-page (support) queue**. Design and decisions are in `docs/ADMIN_CONTROLS.md`; the shipped scope is **Phase 1 + Phase 2** (Phase 3 is not planned). ## What's included **Phase 1 — account & session moderation** (`BridgeAdmin.cs` + config) - `admin.kick` / `admin.ban` (timed + indefinite) / `admin.unban` / `admin.broadcast` - Opt-in per shard (`AdminWriteEnabled`, default **off**); required `actor`; hard **Owner-protection floor** (`AdminAccessFloor`, default `CoOwner`) so a compromised sidecar can never touch the Owner - `admin.ok` / `admin.error` replies; every applied action emits an `admin.audit` (`origin:"web"`) event; attribution is `web:<actor>` - Sidecar: `POST /admin/{kick,ban,unban,broadcast}` with a status mapping (200 / 403 floor / 404 unknown / 400 missing-actor / 401 no-token) **Bidirectional audit** (§5.5 — `patches/commandlogging-event.patch` + `patches/BridgeModerationAudit.cs`) - In-game bans/kicks/broadcasts are forwarded to the site as `admin.audit` (`origin:"in-game"`), so the moderation log is complete regardless of origin - The patch adds `CommandLogging.OnWrite`; the subscriber lives in `patches/` (references a post-patch symbol), applied like the Phase-7 vendor-sale patch **Phase 2 — help-page queue** (`BridgePages.cs`) - Polled + diffed (no EventSink), keyed by sender serial → `page.new` / `page.updated` / `page.closed` - Inbound `pages.snapshot`, `page.respond` (delivers a staff reply to the player; shows as "Staff"; optional close), `page.close` - Sidecar: `GET /pages`, `POST /pages/{id}/respond`, `POST /pages/{id}/close` ## Verification Every slice was verified **live against a booted ServUO + the real sidecar**, then the server was shut down and test artifacts reverted: - Admin verbs: all four succeed; the Owner-floor refusal, unknown-target 404, missing-actor 400, and no-token 401 all confirmed - Bidirectional audit: a genuine `[bcast` and resolved ban/kick log lines produced the right `admin.audit` frames with the target parsed; non-moderation lines ignored - Page queue: snapshot, `page.new` ×2, respond, close (page removed), `page.closed`, and 404 on an unknown page ## Deploy notes - The plugin write plane and page queue are drop-in `overlay/` (deployed by `deploy.ps1`). - The bidirectional-audit slice needs the one-line `patches/commandlogging-event.patch` applied and `patches/BridgeModerationAudit.cs` copied in — see `patches/README.md`. - The write plane is **off by default**; set `AdminWriteEnabled=true` in `Bridge.cfg` to enable it. Authorization is enforced on the website (admin/moderator roles). Downstream/remaining is website-side only: the admin UI (moderation log consuming `admin.audit`, support-queue view over `page.*` + `/pages`). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wtclaude added 5 commits 2026-07-13 07:58:40 +00:00
Phase 1 (plugin side) of docs/ADMIN_CONTROLS.md: a staff write plane so the
website can moderate the live shard.

- BridgeAdmin.cs: inbound admin.kick, admin.ban (timed + indefinite),
  admin.unban, admin.broadcast. Each requires an `actor`, refuses targets at
  or above AdminAccessFloor (default CoOwner — Owner-only shield), replies
  admin.ok/admin.error with the reqId echoed, and emits an admin.audit
  (origin=web) broadcast. Attribution is web:<actor> in the console log and
  the ban BanDealer tag. Kicking enumerates NetState.Instances so a
  character-select session is caught too.
- BridgeConfig/Bridge.cfg: AdminWriteEnabled (default OFF — opt-in),
  AdminAccessFloor, broadcast/reason length caps, ban duration clamp.
- tools/stub_sidecar_admin.ps1: live smoke-test harness; *.log gitignored.

Verified: compiles clean against ServUO (0 err/warn); live run on the seeded
shard confirms all four verbs, the audit stream, timed-ban fields, and the
Owner-floor refusal, with no exceptions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
Phase 1 (sidecar side): POST /admin/{kick,ban,unban,broadcast} forward to the
shard, correlated on a fresh reqId, with an admin-specific status mapping —
unknown target -> 404, protected target / plane-disabled -> 403, missing actor
/ bad body -> 400. actor is required and checked up front. Documents the
endpoints and the admin.audit event in INTEGRATION.md.

Verified end-to-end (real sidecar + booted shard): 200 on success, 403 on the
Owner floor, 404 unknown target, 400 missing actor, 401 no token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
Phase C / §5.5: so the site's moderation log is complete regardless of origin,
in-game uses of the write-plane verbs are forwarded as admin.audit
(origin:"in-game").

- patches/commandlogging-event.patch: adds CommandLogging.OnWrite, raised in
  WriteLine before the m_Enabled guard so it fires even when file logging is
  off. Scripts-layer file -> dynamic build, no core rebuild.
- patches/BridgeModerationAudit.cs: subscriber. Taps OnWrite for resolved
  ban/kick (parsing the target from the log line) and EventSink.Command for
  [bcast. Lives in patches/ (not overlay/) because it references OnWrite,
  which only exists post-patch — same rule as BridgeVendorSale.cs.
- tools/scaffolding/BridgeAuditProbe.cs: gated headless verification.

Verified live: a genuine [bcast plus simulated ban/kick log lines produced
admin.audit frames with origin=in-game, actor, and the target parsed
(seed_010); a non-moderation line was correctly ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
Phase 2 of docs/ADMIN_CONTROLS.md: surface the in-game help-page queue to the
website.

- BridgePages.cs: the queue has no EventSink, so it is polled (PageSweepSeconds,
  default 5s) and diffed, keyed by sender serial (one page per player) ->
  page.new / page.updated / page.closed. Inbound pages.snapshot -> pages.list;
  page.respond delivers a staff reply to the player (online: a gump now; offline:
  queued for next login; shows as "Staff") and can close; page.close removes it.
- BridgeConfig/Bridge.cfg: PageSweepSeconds. BridgeBoot: reload re-arms the poll,
  status reports it.
- sidecar/src/web.rs: GET /pages, POST /pages/{id}/respond, POST /pages/{id}/close.
- INTEGRATION.md: page events (§4) and endpoints (§6).
- tools/scaffolding/BridgePageProbe.cs: gated headless verification.

Verified live (probe-seeded tickets): snapshot returns the queue, the poll emits
page.new for both and page.closed on removal, respond -> 200, close removes the
page, unknown page -> 404.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
Records the owner decision to stop after the help-page queue: Phase 3
(mute/notes/teleport/save) will not be built. The Tier-B candidates remain
catalogued in §3 for the record.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
whitlocktech approved these changes 2026-07-13 08:39:36 +00:00
whitlocktech merged commit 5562e09fb0 into main 2026-07-13 08:39:45 +00:00
whitlocktech deleted branch feature/admin-controls 2026-07-13 08:39:45 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/link#1
No description provided.