NimbusPerms Plugin
Backend server plugin for permission injection, chat formatting, and name tag display using the Nimbus permission system.
NimbusPerms is a Java plugin for Spigot/Paper/Purpur/Leaf/Folia backend servers that bridges the Nimbus permission system to in-game functionality. It handles permission injection, chat formatting, and name tag display using prefix/suffix data from the controller.
Compatibility
NimbusPerms supports Spigot 1.8.8 through the latest Paper/Folia versions. See Compatibility for version-specific feature details.
Architecture
The plugin entry point is NimbusPermsPlugin (dev.nimbuspowered.nimbus.perms.NimbusPermsPlugin), a standard JavaPlugin that reads its API connection from JVM system properties (nimbus.api.url, nimbus.api.token). It requires the Nimbus SDK and only activates on Nimbus-managed services.
On enable, it selects a PermissionProvider implementation, then starts the ChatRenderer and NameTagHandler display components. Player join/quit events are forwarded to all three systems.
NimbusPermsPlugin
├── PermissionProvider (interface)
│ ├── BuiltinProvider → fetches from Nimbus REST API
│ └── LuckPermsProvider → delegates to LuckPerms, syncs display to API
├── ChatRenderer → Paper AsyncChatEvent renderer
└── NameTagHandler → Scoreboard Team prefix/suffixPermission providers
PermissionProvider interface
All providers implement dev.nimbuspowered.nimbus.perms.provider.PermissionProvider:
public interface PermissionProvider {
void enable(NimbusPermsPlugin plugin);
void disable();
void onJoin(Player player);
void onQuit(Player player);
void refresh(UUID uuid);
void refreshAll();
String getPrefix(UUID uuid);
String getSuffix(UUID uuid);
int getPriority(UUID uuid);
}Built-in provider
The default provider (BuiltinProvider) fetches permissions directly from the Nimbus REST API:
- On player join, sends
PUT /api/permissions/players/{uuid}with the player's name - Parses the response for
effectivePermissions,prefix,suffix, anddisplayGroup - Applies permissions via
PermissionAttachment, supporting wildcards (*,nimbus.cloud.*) and negations (-some.permission) - Listens on the WebSocket event stream for
PERMISSION_GROUP_CREATED,PERMISSION_GROUP_UPDATED,PERMISSION_GROUP_DELETED, andPLAYER_PERMISSIONS_UPDATEDevents to trigger live refreshes
LuckPerms provider
When provider: luckperms is set in config and LuckPerms is installed, the LuckPermsProvider activates:
- LuckPerms handles all permission injection natively -- NimbusPerms does not manage
PermissionAttachmentobjects - Display data (prefix, suffix, primary group, weight) is read from LuckPerms
CachedMetaData - On join and on LuckPerms
UserDataRecalculateEvent, display info is cached locally and synced to the Nimbus API so the controller has current data for proxy features (tab list, MOTD) - On
GroupDataRecalculateEvent, all online players are refreshed
Chat rendering
ChatRenderer detects at runtime which chat system to use:
- Paper 1.16.5+ —
ModernChatHandlerimplementsio.papermc.paper.chat.ChatRenderer, listens onAsyncChatEventatLOWESTpriority. Supports MiniMessage and Adventure Components. - Spigot 1.8.8–1.16.4 —
LegacyChatHandlerlistens onAsyncPlayerChatEvent. Formats with legacy&color codes viaChatColor.translateAlternateColorCodes(). - Folia — Uses
ModernChatHandler(Folia is Paper-based, soAsyncChatEventis always available).
The format string is fetched from GET /api/proxy/chat at startup and updated live via CHAT_FORMAT_UPDATED WebSocket events. Available placeholders:
| Placeholder | Value |
|---|---|
{prefix} | Player's permission group prefix |
{suffix} | Player's permission group suffix |
{player} | Player name |
{message} | Chat message content |
{server} | Current service name (e.g. Lobby-1) |
{group} | Current group name (e.g. Lobby) |
The format string supports MiniMessage tags and legacy & color codes (translated via ColorUtil.translate()).
Name tag handling
NameTagHandler uses Scoreboard Teams to display prefix/suffix above player heads and in the tab list. Teams are named with the nimbus_ prefix followed by a priority-based sort key (9999 - priority, zero-padded to 4 digits), ensuring higher-priority groups appear first in the tab list.
Key behaviors:
- Tags are applied 5 ticks after join to allow the provider to load display data
- Per-player tab overrides are supported via
PLAYER_TAB_UPDATEDevents (set through the SDK'sNimbus.setTabName()) - When a player quits, their team entry is removed and empty teams are unregistered
PERMISSION_GROUP_UPDATEDevents trigger a full refresh of all online players
Wildcard permission injection
PermissibleInjector (dev.nimbuspowered.nimbus.perms.handler.PermissibleInjector) extends PermissibleBase to add wildcard matching that Bukkit does not support natively:
*grants all permissionssome.node.*grants all permissions starting withsome.node.- Exact matches are checked first, then progressively broader wildcard patterns
Configuration
NimbusPerms uses a standard Bukkit config.yml with one key setting:
# Permission provider: "builtin" or "luckperms"
provider: builtinThe plugin is auto-deployed to every backend service at runtime by the
Permissions module (installed by default) via
ServiceFactory.resolveModulePlugins(). Deployment can be disabled with
[permissions] deploy_plugin = false in nimbus.toml. Templates stay
user-owned; the JAR is never written into templates/global/plugins/.
How permissions sync
Controller (nimbus-core)
│
├── REST API: /api/permissions/players/{uuid}
│ ← BuiltinProvider fetches on join
│ ← LuckPermsProvider syncs display data back
│
└── WebSocket: /api/events
→ PERMISSION_GROUP_UPDATED → refreshAll()
→ PLAYER_PERMISSIONS_UPDATED → refresh(uuid)
→ CHAT_FORMAT_UPDATED → update chat format
→ PLAYER_TAB_UPDATED → update tab overrideThe controller is the single source of truth for built-in permissions. With LuckPerms, the LuckPerms database is authoritative for permissions, but display data is synced back to the controller so proxy-level features (tab list formatting, MOTD player counts) have access to prefix/suffix information.
Compatibility
NimbusPerms runs on Spigot 1.8.8 through the latest Paper and Folia versions.
Feature availability by version
| Feature | Minimum version | Notes |
|---|---|---|
| Permission injection (BuiltinProvider) | Spigot 1.8.8 | Full functionality |
| LuckPerms provider | Spigot 1.8.8 | Requires LuckPerms installed |
| Chat formatting (Modern) | Paper 1.16.5 | AsyncChatEvent + MiniMessage + Adventure |
| Chat formatting (Legacy) | Spigot 1.8.8 | AsyncPlayerChatEvent + & color codes |
| Name tags (Scoreboard Teams) | Spigot 1.8.8 | Full functionality |
| Tab list prefix/suffix (Adventure) | Paper 1.16.5 | Rich text via Adventure Components |
| Tab list prefix/suffix (Legacy) | Spigot 1.8.8 | Legacy ChatColor formatting |
player.updateCommands() | Paper 1.13+ | Silently skipped on older versions |
| Folia support | Folia 1.19.4+ | Entity-bound scheduling for all player operations |
Folia details
On Folia, all player-bound operations (permission apply, name tag update, chat rendering) are scheduled on the player's region thread via SchedulerCompat.runForEntity(). Scoreboard Team operations use the global region scheduler. Event-driven refreshes (PERMISSION_GROUP_UPDATED, PLAYER_TAB_UPDATED) are dispatched per-player to the correct region.
Next steps
- Architecture -- System overview and module structure
- SDK -- Backend server plugin API
- Permissions Config -- Setting up permission groups on the controller