feat(rust): site-owned permissions — the site is the author, the game is the cache #8
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/phase-7-permissions"
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?
R2, and the first phase where this module writes to a game. Groups and grants are authored on the website and pushed into each server's own permission store, so every plugin that already calls
UserHasPermissionhonours them with no adapter — and a wipe stops being a data-loss event, because the game forgets and the site does not.Depends on Rust-Link#4 and Rust-Plugins#4 (protocol 4).
Seven org-lead decisions
*for the fleetThree sets, and every interesting question is a difference between two
The middle one is why
rust_perm_pushedexists, and it cannot be inferred: 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 two have opposite correct answers. The site's memory of its own authorship is the only thing that tells them apart.What lands is not what was sent
Two states are invisible from a push that looked like it worked, and neither is recorded as pushed:
GrantUserPermissionno-ops silently, so the site would otherwise believe it gave a privilege it did not;A direct grant to an unseen player works immediately, which is the asymmetry R16's offline entitlement rests on and the reason groups are not the only shape.
The loop
Every thirty seconds it asks a cheap question — does the digest of the desired set still equal what this server last confirmed — and does nothing when the answer is yes. A sync happens on a change, a restart, a wipe, a
perm.drifthook from the game, a failed attempt past its backoff, or the fifteen-minute audit that finds drift on a server nobody has touched. The digest is sorted before hashing, because an unsorted one differs between two reads of an unchanged set and would push to every game server for ever.Nothing in a request writes to a game: the buttons write to the site and the loop reconciles. Sync now is the exception and says so — an operator who has just changed something should not have to trust a timer to find out that a host is unreachable.
This module's first admin page
Everything before it was configured through the API, because nothing until now had to be authored. What is on the page is decided by what an operator can get wrong — four states that are invisible from the game and from a list of grants, each a sentence rather than a number:
The
admin.users.detailslot phase 6 built gains the same thing for one person, which is where an operator actually asks "why can this player spawn a kit".Four defects the browser found that 133 green tests did not
<h1>under core's chrome title — the same words twice.A fifth was found by a test rather than a browser, and it was the better catch:
buildDesiredresolved a grant's accounts from the join inlistGrantsrather than from the link map, which gives the right answer by accident until somebody changes that query and one of a person's two accounts quietly stops being granted.What was proven, and how
A real core at the pinned ref with this module installed, the real
rust-linksidecar, and a stand-in plugin speaking protocol 4 on the loopback — enough to walk everything the site owns:unresolved: ["kits.gold"]andpending: ["7656003:vip"]reported and kept out of the pushed ledger;pushed − desired);reason: "restart", re-pushed the whole set, and the group, its permission, both memberships and the grant came back — which is R2's central promise;perm.driftframe from the game marking the server dirty, and the sync after it reporting the hand edit authoritatively;transport-errorand retrying on its backoff while the first one stayed in sync.Not proven, deliberately: that a site grant gates a third-party plugin in game. That acceptance line needs a second, non-admin Steam account on the rig (PLAN.md §12.5) — the org lead's call to arrange — so it is written down as a walk to run rather than claimed.
Checks
133 server tests, 39 client tests,
check:imports,check:bundle,check:swagger,check:externals— all green.swagger-fragment.json(28 paths) androutes.manifest.json(30 routes, all documented) regenerated against a real core at the pinned ref.Base branch
edge, per D18.AI disclosure
🤖 Generated with Claude Code
https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM