All checks were successful
PR Checks / plugin-checks (pull_request) Successful in 8s
R2, the game half. The website is the author of record for groups and grants; this plugin makes the framework's own permission store match what it is sent. **One verb, and the plugin does the diffing (D32).** `perm.sync` carries the whole set the site authors for this server. The plugin compares it against the live store and writes only what differs, then answers with a report: what it applied, what was already right, the names it could not resolve, the memberships waiting on a first connection, and every holder the site did not author. `perm.catalogue` is the other half — a live read of what the loaded plugins have registered, which is the option source the authoring form is built from. **The first command whose work is unbounded, so it is drained rather than run.** A community with two thousand linked players sends thousands of operations in one frame, and applying them in the tick the frame arrives is a freeze an operator will blame on the game. A sync compiles into a list of single-store operations and drains two hundred at a time on a timer. That is §5's rule — nothing the far side sends may cost the main thread an unbounded amount of work — applied to the inbound half for the first time. **The pre-check §12.2 rule 1 exists for.** `GrantUserPermission` returns void, throws nothing and logs nothing for a name no plugin registered, so every grant naming an unloaded plugin's permission would disappear without a trace. The plugin checks `PermissionExists` first and reports the name as unresolved; it does NOT register the name itself (D33), because that fabricates a permission the operator never installed. **`UserHasPermission` cannot answer the question the diff asks.** It is true for a member of a group that carries the permission, so a direct grant would look already applied the moment a group covered it — and a revoke already done. `GetPermissionUsers` reads the user's own set, which is what a direct grant is. Six permission hooks join the read path, all void, all in `ExpectedHooks`. They are not the drift record — only the site's desired set can say whether a change is foreign — they are a reason to reconcile sooner, and they are suppressed while this plugin is writing, or every sync would announce itself as drift. `rg.perms` reports the last report from the console, which is the fastest way to tell "no plugin registered that name" from "the store has never seen that player". Protocol bumped to 4 here and in overlay.toml, in the same change as the emitters. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM
51 lines
2.7 KiB
TOML
51 lines
2.7 KiB
TOML
# Release metadata for the deployable overlay.
|
|
#
|
|
# Consumed by the release workflow, which folds these values into the
|
|
# manifest.json shipped inside the overlay tarball. The Runic Gateway installer
|
|
# reads that manifest to decide what it is deploying and whether it is compatible
|
|
# with the sidecar it is about to install.
|
|
#
|
|
# There is deliberately NO version key here. The release version is derived from
|
|
# git tags and conventional commits by the release workflow, so there is no bump
|
|
# commit to keep in sync and no way for this file to disagree with the tag.
|
|
|
|
# ── The loopback wire-protocol version this overlay speaks ───────────────────
|
|
#
|
|
# The plugin half of the compatibility contract. It MUST equal the sidecar's
|
|
# PROTOCOL_VERSION (Rust-Link's sidecar/src/main.rs) for a deployment to work:
|
|
# the sidecar rejects a mismatched WEBSITE with 409, and a mismatched PLUGIN is
|
|
# worse, because the game link has no such check — it would simply mis-parse.
|
|
#
|
|
# That asymmetry is why this file exists. The plugin announces its protocol in
|
|
# `server.hello`, which is only readable after the game server has booted with it
|
|
# loaded — far too late for an installer to refuse a bad pairing. This
|
|
# declaration is what lets the bundle CI check the pair BEFORE an operator
|
|
# installs either half.
|
|
#
|
|
# Keeping it honest is a manual duty: when the protocol changes, bump it here in
|
|
# the same change that alters the emitters, exactly as the sidecar bumps
|
|
# PROTOCOL_VERSION and the module bumps its own constant.
|
|
#
|
|
# Current: 4 — the transport, the read path, identity, and the permission mirror
|
|
# (docs/rust-link/PROTOCOL.md §10).
|
|
protocol = 4
|
|
|
|
# ── Oxide compatibility ──────────────────────────────────────────────────────
|
|
#
|
|
# The overlay only ADDS a file — one plugin into `oxide/plugins/` — and patches
|
|
# nothing, so it is expected to work on any reasonably current Oxide. This is the
|
|
# oldest build it is known good on.
|
|
#
|
|
# There is no `patches_verified_against` key, and there is no `patches/` tier:
|
|
# Rust's server is a binary and Oxide's hook API is the supported way in, so
|
|
# there is nothing to diff against. That is the whole reason the Rust payload is
|
|
# simpler than the ServUO one.
|
|
min_oxide_version = "2.0.7585"
|
|
|
|
# The `oxide/plugins/` files this overlay expects to find already installed. They
|
|
# are not shipped here — they are third-party plugins an operator installs from
|
|
# uMod — and the installer's `doctor` reports a missing one rather than
|
|
# installing it. Listing them is what turns "the site shows no clans" into a
|
|
# named prerequisite.
|
|
requires_plugins = ["Clans", "Kits", "PopupNotifications", "ZoneManager"]
|