Skip to content

Permissions & Job Gates ​

Two separate systems, and they answer different questions.

QuestionChecked
StaffMay this player write?Server, on open and on every verb
AccessMay this player read this leaf?Server before a body is sent, client before a signpost is drawn

Staff: three independent checks ​

Any one passing is enough, and all three are consulted.

CheckWhat it readsWorks on
ACErm_guidebook.press, or anything in staff.acesEvery framework, and none
Framework groupadmin, superadmin, owner by defaultFrameworks that expose groups
Framework permission callThe core's own answerOnly frameworks that have one

Why three

The frameworks disagree about what an administrator is. Relying on only one of them breaks on the other two.

The ACE route works everywhere:

cfg
add_ace group.admin rm_guidebook.press allow
add_principal identifier.license:XXXXXXXXXXXXXXXX group.admin

Both lines are needed

The commonest cause of a refused press is the missing add_principal, not the ace.

To use different group names:

lua
-- local/overrides.lua
RmGuidebook.Local = { staff = { groups = { 'admin', 'owner', 'developer' } } }

Remember that lists replace wholesale — that means those three and no others.

Which checks actually do anything on your framework is in Frameworks.

The console ​

Source 0 always clears the staff check, so /almanac_resync and /almanac_migrate can be driven from rcon or a scheduled task.

lua
RmGuidebook.Local = { staff = { allowConsole = false } }

The eight verbs ​

Each verb can be given an ACE of its own. A player holding it may use that verb whether or not they are staff — so this widens access rather than narrowing it.

lua
RmGuidebook.Local = {
    staff = { verbs = { entry_write = 'rm_guidebook.scribe' } },
}
cfg
add_ace group.moderator rm_guidebook.scribe allow

That is how you let a moderator write entries without letting them strike chapters.

VerbLets them
chapter_writeCreate and edit chapters
chapter_strikeDelete a chapter — and its entries with it
entry_writeCreate and edit entries
entry_strikeDelete an entry
signpost_writePlace and edit signposts
signpost_strikeRemove a signpost
hand/almanac_hand — open the almanac on someone else's screen
ferryCarry a player to a signpost

Striking a chapter strikes its entries

There is no separate confirmation for the entries that go with it. chapter_strike is the verb to be careful about delegating.

Access: job gates ​

Any chapter, entry or signpost can be limited to certain jobs at a minimum grade.

lua
access = { on = true, jobs = { { job = 'police', grade = 1 } } }

Grade is a minimum — grade 1 means grade 1 and above.

The rule runs in two places, and that is the point:

  • On the server, before a body is handed over. A restricted entry is not merely hidden; it cannot be opened.
  • On the client, before a signpost is drawn. A restricted signpost is not drawn at all for a player the rule refuses — no prompt, no title, no blip.

Hidden is not restricted ​

Two different things, and choosing the wrong one is a common mistake.

Hides from the indexBlocks readingUse for
Shown = offYesNoA page you reach from a signpost or a link
AccessYesYesAnything that actually matters

Anyone with a link or a signpost can still open a shown = off page. Use Access for anything that actually matters.

Search respects both ​

Three deliberate rules:

  • Hidden entries are not searchable. They are reachable by slug and by signpost, but not discoverable. That is the difference between unlisted and secret.
  • Restricted entries are not searchable by anyone who could not read them.
  • The query is matched against the text, not the markup. Searching for <strong> finds nothing, which is the point.

Testing a gate ​

access.staffSeesAll defaults to true, so staff see everything — which makes a gate look broken when it is working.

lua
RmGuidebook.Local = { access = { staffSeesAll = false } }

That lets you test a job-gated chapter without leaving the admin group. The alternative is a second account.

"Everyone can see a page they should not" ​

Check three things, in this order:

  1. access.enabled is true in your settings.
  2. The chapter and the entry both have the access block set. An open chapter containing a restricted entry still hides the entry — but the chapter is visible.
  3. You are not testing as staff. See above.

Where the job list comes from ​

The list offered in the access picker is merged from what the framework reported and your own jobs table, and yours wins.

On VORP and RSG the framework reports a real list with grade labels. On RedEM:RP and standalone there is no registry, so your jobs table is where the labels come from entirely.

lua
RmGuidebook.Local = {
    jobs = {
        vallaw = { label = 'Valentine law', grades = { [0] = 'Recruit', [1] = 'Deputy', [2] = 'Sheriff' } },
    },
}

/almanac_resync re-reads the list, so a job added since start-up appears without a restart.

Reader and route gates ​

Two coarser switches, both nil by default, meaning everyone:

lua
readers = { ace = 'rm_guidebook.reader' },   -- who may open the almanac at all
route   = { ace = 'rm_guidebook.navigator' }, -- who may ask for directions

Staff always pass either way.

What the server actually enforces ​

Worth being precise, because it is better than most:

  • The press is re-checked on every verb, not just on open. A client that flips its own staff flag achieves nothing.
  • A restricted body is never sent. The client cannot leak what it was not given.
  • A player naming an entry they may not read is told the leaf is missing, not that it exists.
  • /almanac_hand applies the target's permissions, not yours — handing a page out is not a way around the gate.

Turn on the press trace and every refusal is logged with the name of whoever was refused:

lua
RmGuidebook.Local = { debug = { enabled = true, log = { press = true } } }

Documentation for RedMorrow. Scripts are licensed per server — redistribution is not permitted.