docs(rust): perm.inventory in PROTOCOL 19.9, and the permission manager as built
PROTOCOL.md 19.9 specifies perm.inventory (owners, groups with parents, holders, leased pairs; sliced, paged, one snapshot) and perm.sync's protocol-13 changes (parents, titles and ranks written, absent means leave it, managed and foreign gone). PLAN_REDESIGNS 1.9 records what the build settled and the walk on both rigs. Refs RunicGateway/Module-Rust#21 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -2182,3 +2182,66 @@ zone** — that needs somebody in the game.
|
||||
servers is unreachable; a dead server that minted nothing no longer makes a wrong code look good.
|
||||
- **NPC attackers** (F2, D185) are named by the killfeed: a family (`scientistnpc_*` → Scientist,
|
||||
`bradleyapc` → Bradley APC) or the prefab without its variant digits (`wolf2` → Wolf). No wire change.
|
||||
|
||||
### 19.9 `perm.inventory` — the whole permission store (the permission manager)
|
||||
|
||||
The site owns every permission and group on a server, not just the ones it authored
|
||||
(`docs/modules/rust/PLAN_REDESIGNS.md` §1, D160). So it has to read all of them. It does that with one
|
||||
request/reply verb, through one new sidecar route:
|
||||
|
||||
POST /permissions/inventory ─── perm.inventory ───► {snapshotId?, page?}
|
||||
◄── perm.inventory ──── one page
|
||||
◄── perm.error ──────── busy · stale · too-large
|
||||
|
||||
**Page 0 without a `snapshotId` starts a fresh read.** The reply names the snapshot, and each later page
|
||||
is asked for by `snapshotId` and `page`:
|
||||
|
||||
```json
|
||||
{"kind":"perm.inventory","type":"reply","snapshotId":"62fe7e6cfac5","page":0,"pages":1,"more":false,
|
||||
"permissions":[{"name":"kits.admin","owner":"Kits"},{"name":"adminmodule.greet"}],
|
||||
"groups":[{"name":"default","title":"Default ","rank":0,"parent":"","permissions":[]}],
|
||||
"users":[{"steamId":"76561198038695917","name":"whitlocktech","permissions":["kits.admin"],"groups":["site"]}],
|
||||
"leased":[{"kind":"group-permission","subject":"default","object":"kits.vip"}],
|
||||
"stats":{"permissions":85,"groups":4,"users":2,"buildMs":148}}
|
||||
```
|
||||
|
||||
- **`permissions`** is every registered name, with **`owner`**, the plugin that registered it. The
|
||||
owner comes from `permission.PermissionExists(name, plugin)` asked of each loaded plugin. That is
|
||||
public API on both frameworks (Oxide takes a `Plugin`, Carbon a `BaseHookable`), never a guess from
|
||||
the name's prefix. A name no plugin owns has no `owner`; on Carbon those are its built-in modules'
|
||||
names (`adminmodule.*`).
|
||||
- **`groups`** have their title **verbatim** (Carbon's own end in a space), rank, parent, and the
|
||||
permissions the group itself carries, not what it inherits.
|
||||
- **`users`** is every Steam id holding anything, with its direct permissions and its groups, **except
|
||||
`default`**, which every connected player is in by framework rule. `name` is the store's last
|
||||
recorded name, when it has one.
|
||||
- **`leased`** is the group permissions an event lease holds (§14). The site does not take them for hand
|
||||
edits.
|
||||
- **Page 0 alone carries `permissions`, `groups`, `leased` and `stats`**, and every page carries
|
||||
`users`. A page is at most 700 KiB of rows, inside the 1 MiB line cap (§3.1).
|
||||
|
||||
**Cost.** Neither framework can list its users, so holders come from `GetPermissionUsers` for every
|
||||
permission and `GetUsersInGroup` for every group. Each of those walks every user. The read is therefore
|
||||
built **25 sources per tick**, on the sync drain's 50 ms timer, and replied to when complete. It is taken
|
||||
**once** and paged from memory for two minutes, so a grant made between two pages cannot tear it. A build
|
||||
past **8 s** is abandoned and answered `too-large` — the sidecar's ten-second wait has already lost it.
|
||||
A second read while one is building is `busy`; a page of a snapshot that is gone is `stale`, and the
|
||||
site starts again from page 0 (once). `rg.inventory` runs the same build from the server console and
|
||||
logs its summary instead of replying.
|
||||
|
||||
**`perm.sync`**, in the same bump:
|
||||
|
||||
- **Groups carry `parent`.** It is applied with `SetGroupParent` after every group exists and before any
|
||||
retirement. A parent that will not set (missing, or a loop) is reported in `notLanded` as
|
||||
`group:parent:name`.
|
||||
- **An existing group's title and rank are written when they differ.** Until now a title was only set
|
||||
when the sync created the group. **An absent `title` or `rank` means "leave it"**, and an empty title
|
||||
is a title. The site omits them while a person decides about an in-game change (the `adopt` policy).
|
||||
The report's `applied` gains **`groupsUpdated`**.
|
||||
- **`managed` and the report's `foreign` are gone.** The foreign scan looked only at names the site
|
||||
claimed. The site now reads the whole store and decides what an in-game change becomes itself
|
||||
(PLAN_REDESIGNS §1.5, §1.6).
|
||||
|
||||
Walked on both rigs, 2026-09-27/28. Oxide gave 85 permissions, every one owned (`RustCore` owns
|
||||
`oxide.*`), in 148 ms over four ticks. Carbon gave 104, the 30 `adminmodule.*` unowned, in 204 ms. On
|
||||
both, **`zonemanager.ignoreflag.nokits` belongs to ZoneManager**.
|
||||
|
||||
Reference in New Issue
Block a user