Permissions#
Every /vg subcommand is gated by a permission node. Nodes are checked in a fixed
order: LuckPerms first if available, then a vanilla op-level fallback.
Resolution order#
For a player source, PermissionResolver checks in this order and stops at the
first definitive answer:
- LuckPerms, when
permissions.useLuckPermsistrueand the bridge is available. Atruegrants, afalsedenies. - Op level, when LP is absent, disabled, or returns
UNDEFINED. Grant iffopLevel >= requiredLevel, whererequiredLevelis the per-node default or apermissions.perNodeOpLevelsoverride.
The console is always authorised — a non-player source never fails a command gate.
LuckPerms is a soft dependency#
The bridge is pure reflection; VonixGuardian never imports net.luckperms.*. It
compiles, ships, and runs with no LuckPerms present. Detection happens once per JVM
and is cached:
| State | Behaviour |
|---|---|
| Absent from classpath | Cached as unavailable; op-level fallback for the rest of the JVM's life. |
| Present but not yet registered | Not cached — the next call re-probes. This is why LP works even though it loads after some mods. |
| Present and registered | Used for every check. |
Nothing needs configuring to switch between modes. Install LuckPerms and the next check uses it; remove it and the fallback resumes.
Node reference#
Canonical nodes live under vonixguardian.<action>. The default op level applies
only when the fallback path is in use — with LuckPerms, the node's value decides.
| Node | Gates | Default op |
|---|---|---|
vonixguardian.base |
The /vg root literal itself. |
2 |
vonixguardian.inspect |
/vg inspect, i, inspector |
2 |
vonixguardian.lookup |
/vg lookup, l, page |
2 |
vonixguardian.near |
/vg near |
2 |
vonixguardian.rollback |
/vg rollback, rb, ro, /vg apply, /vg cancel (rollback preview) |
3 |
vonixguardian.restore |
/vg restore, rs, re, /vg apply, /vg cancel (restore preview) |
3 |
vonixguardian.undo |
/vg undo |
3 |
vonixguardian.purge |
/vg purge |
4 |
vonixguardian.status |
/vg status, stats, version |
3 |
vonixguardian.consumer |
/vg consumer |
4 |
vonixguardian.reload |
/vg reload |
4 |
vonixguardian.migrate_db |
/vg migrate-db |
4 |
vonixguardian.command.config |
/vg config, /vg entitylog |
4 |
vonixguardian.command.teleport |
/vg teleport, tp |
3 |
vonixguardian.command.give |
/vg give |
4 |
vonixguardian.help |
/vg help |
0 |
Two naming shapes, both real
Most nodes are vonixguardian.<verb>, but config, teleport, and give are
vonixguardian.command.<verb>. That is the shipped registry, not a typo —
grant exactly the strings in this table. Legacy
vonixguardian.command.<verb> aliases are also accepted for the other verbs, so
an older permission set keeps working, but the canonical form above is what
/vg status and the override map expect.
Per-category child nodes#
For lookup, rollback, and restore, category-scoped child nodes allow a
narrower grant than the whole family. They are checked per row when results are
filtered.
| Lookup child | Default op | Rollback child | Restore child |
|---|---|---|---|
vonixguardian.lookup.block |
2 | vonixguardian.rollback.block |
vonixguardian.restore.block |
vonixguardian.lookup.container |
2 | vonixguardian.rollback.container |
vonixguardian.restore.container |
vonixguardian.lookup.inventory |
2 | vonixguardian.rollback.inventory |
vonixguardian.restore.inventory |
vonixguardian.lookup.item |
2 | vonixguardian.rollback.item |
vonixguardian.restore.item |
vonixguardian.lookup.kill |
2 | — | — |
vonixguardian.lookup.click |
2 | — | — |
vonixguardian.lookup.session |
2 | — | — |
vonixguardian.lookup.sign |
2 | — | — |
vonixguardian.lookup.username |
2 | — | — |
vonixguardian.lookup.chat |
3 | — | — |
vonixguardian.lookup.command |
3 | — | — |
Fall-open by design. If a family has no child node for a category, the family
node is used. There is no way to be scoped out of a family that has no
subdivision — holding vonixguardian.rollback grants rollback for messages and
sessions too, where those types are reversible at all.
A player with vonixguardian.lookup but not vonixguardian.lookup.chat sees block
and container rows in a /vg lookup and has chat rows silently filtered out of the
result. #count respects the same filter, so counts match what is visible.
Reserved nodes#
These exist in the registry but do not gate a subcommand:
| Node | Default op | Purpose |
|---|---|---|
vonixguardian.lookup.ip |
4 | Reserved for IP-visibility scoping. |
vonixguardian.bypass.inspector |
0 | Reserved for inspector bypass. |
Do not build a permission scheme around them; they are declared for forward compatibility and are not enforced on a command path.
Setting up LuckPerms#
Two roles cover most servers: moderator investigates, admin mutates.
# Moderator — read-only investigation
/lp group moderator permission set vonixguardian.base true
/lp group moderator permission set vonixguardian.inspect true
/lp group moderator permission set vonixguardian.lookup true
/lp group moderator permission set vonixguardian.near true
/lp group moderator permission set vonixguardian.status true
/lp group moderator permission set vonixguardian.command.teleport true
# Admin — inherits moderator, gains mutation and maintenance
/lp group admin parent add moderator
/lp group admin permission set vonixguardian.rollback true
/lp group admin permission set vonixguardian.restore true
/lp group admin permission set vonixguardian.undo true
/lp group admin permission set vonixguardian.purge true
/lp group admin permission set vonixguardian.reload true
/lp group admin permission set vonixguardian.consumer true
/lp group admin permission set vonixguardian.command.give true
Notes that save support tickets:
- Grant
vonixguardian.baseto anyone who holds another node. It gates the root literal; without it some clients hide/vgfrom tab-completion entirely. - Three nodes carry the
.command.infix —command.config,command.teleport,command.give. Copy them exactly from the table; a plausible-lookingvonixguardian.teleportgrants nothing. purgeat op 4 is deliberate. It is destructive and irreversible.consumerandreloadare operational, not investigative. Pausing the writer during an investigation silently loses events; keep them with admins.- Wildcards work:
vonixguardian.*grants every node in the table.
Op-level fallback#
Without LuckPerms there is no per-command granularity by default — any player at
permissions.defaultOpLevel or above passes every node check, including
purge.
"permissions": {
"useLuckPerms": true,
"defaultOpLevel": 3,
"perNodeOpLevels": {}
}
defaultOpLevel must be 0–4. 3 matches the vanilla /op tier, so a normally
opped administrator can use everything.
Per-node overrides#
perNodeOpLevels restores per-command control without LuckPerms. This is the
supported way to let a builder inspect without letting them purge:
"permissions": {
"useLuckPerms": true,
"defaultOpLevel": 4,
"perNodeOpLevels": {
"vonixguardian.base": 0,
"vonixguardian.inspect": 0,
"vonixguardian.lookup": 0,
"vonixguardian.near": 0,
"vonixguardian.status": 2,
"vonixguardian.rollback": 3,
"vonixguardian.restore": 3,
"vonixguardian.purge": 4
}
}
With that map, a level-0 player can inspect and look up, a level-3 admin can roll back, and purge stays at 4.
Rules:
- Keys are canonical node strings from the table. An unknown key logs a WARN and is ignored at runtime.
- Values must be 0–4. Anything else is a hard config validation error.
- The map only affects the fallback path. When LuckPerms answers, the map is not consulted.
permissionsis restart-only —/vg reloaddetects the change and reportsRequires restart.
If a node is absent from the map, its own default op level applies. A malformed override does not silently degrade to the coarse default; it is rejected at load.
Choosing a scheme#
| Situation | Recommended |
|---|---|
| Small server, one admin | LuckPerms absent, defaultOpLevel: 3. Nothing to configure. |
| Staff team on a modded pack | LuckPerms with moderator and admin groups above. |
| No LuckPerms but you want tiers | perNodeOpLevels map with defaultOpLevel: 4. |
| Letting players audit their own builds | Grant lookup broadly; keep rollback and purge with staff. |
Verify a grant before handing out roles
Run /vg status and read the Permissions section — it reports the default
op level and how many per-node overrides are active. If the override count is
0 when you expected a map, the config did not load it, and every node is
using its default.