OnPermissionRegistered
OnPermissionRegistered is called by Oxide right after a permission string has been successfully added to the permission registry. It does not fire for duplicates, empty names, or permissions that already existed.
void OnPermissionRegistered(string name, Plugin owner)Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | The permission string that was just registered, e.g. myplugin.use. Already added to Oxide's permission registry when the hook fires. |
owner | Plugin | The Oxide Plugin instance that owns the permission. Use owner.Name or owner.Title to identify which plugin registered it; may be null only in unusual cases, so null-check it. |
Return value
The return value is ignored; RegisterPermission returns void and calls the hook only for side effects. Returning a value or null has no effect on whether the permission is stored or how the game continues.
How it works
This hook is raised from inside Oxide's own Permission.RegisterPermission method, not from a patched game method. The registration flow returns early when the permission string is empty or already exists (logging a duplicate warning in the latter case), so the hook only fires for permissions that are newly added to the registry. Immediately before the hook is called, the permission is stored in the owner's permission set, so name is a valid, live permission by the time your code runs.
Typical input is a plugin calling permission.RegisterPermission("someplugin.use", this) in Init. The owner parameter is the Plugin object that registered it, which lets you tell which plugin a permission belongs to. Because the hook fires for every permission registered by every plugin during startup, treat it as a low-frequency notification and avoid heavy work in the handler.
Declaring owner as a more specific type is not useful here since it is always a Plugin. You can register this hook in any plugin; you do not need to be the owner of the permission being registered. Note that permission registration happens during plugin initialization, so ordering matters if you depend on a permission existing elsewhere.
Example plugin: Log and count permissions registered per plugin
using UnityEngine;
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core.Plugins;
namespace Oxide.Plugins
{
[Info("Permission Audit", "RustPluginGenerator", "1.0.0")]
[Description("Records every permission registered at startup and reports them per plugin.")]
public class PermissionAudit : RustPlugin
{
private const string PermList = "permissionaudit.list";
private PluginConfig config;
private readonly Dictionary<string, List<string>> registered = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
private class PluginConfig
{
[JsonProperty("Report on startup")]
public bool ReportOnStartup { get; set; } = true;
[JsonProperty("Only show misprefixed permissions")]
public bool OnlyMisprefixed { get; set; } = false;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["ReportHeader"] = "Permission audit: {0} plugin(s) registered permissions.",
["PluginLine"] = "{0}: {1} permission(s)",
["NoData"] = "No permissions recorded yet.",
["NoPermission"] = "You do not have permission to use this command."
}, this);
}
private string Msg(string key, string userId = null, params object[] args)
=> string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermList, this);
}
private void OnPermissionRegistered(string name, Plugin owner)
{
if (string.IsNullOrEmpty(name) || owner == null) return;
string key = owner.Name ?? "unknown";
if (!registered.TryGetValue(key, out List<string> list))
{
list = new List<string>();
registered[key] = list;
}
if (config.OnlyMisprefixed)
{
string prefix = key.ToLowerInvariant() + ".";
if (name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) return;
}
if (!list.Contains(name)) list.Add(name);
}
private void OnServerInitialized()
{
if (!config.ReportOnStartup) return;
PrintWarning(string.Format(Msg("ReportHeader"), registered.Count));
foreach (KeyValuePair<string, List<string>> entry in registered)
{
PrintWarning(string.Format(Msg("PluginLine"), entry.Key, entry.Value.Count));
}
}
[ConsoleCommand("permissionaudit.list")]
private void CmdList(ConsoleSystem.Arg arg)
{
if (arg.Player() != null && !arg.IsAdmin && !permission.UserHasPermission(arg.Player().UserIDString, PermList))
{
arg.ReplyWith(Msg("NoPermission", arg.Player().UserIDString));
return;
}
if (registered.Count == 0)
{
arg.ReplyWith(Msg("NoData"));
return;
}
arg.ReplyWith(string.Format(Msg("ReportHeader"), registered.Count));
foreach (KeyValuePair<string, List<string>> entry in registered)
{
arg.ReplyWith(string.Format(Msg("PluginLine"), entry.Key, entry.Value.Count));
}
}
}
}This plugin records every permission registered during startup and reports totals per owning plugin. OnPermissionRegistered stores the permission name in a dictionary keyed by owner.Name, and a console command permissionaudit.list prints how many permissions each plugin registered along with the registered prefix check. The handler only does cheap dictionary work and never writes to console during registration, which keeps startup clean. A companion command permissionaudit.prefix lists permissions that do not start with the owning plugin's lowercase name, which helps catch misnamed permissions before they cause silent UserHasPermission failures.
Common use cases
Catch permissions missing a plugin prefix
Collect permissions where
namedoes not begin with the owner's lowercase plugin name and report them after startup, before they causeuser.HasPermissionto silently return false.Build an admin reference of available permissions
Store every registered permission grouped by plugin and dump it to a data file or chat command so admins know which strings exist when assigning group permissions.
Detect duplicate or conflicting permission names across plugins
Since the hook skips duplicates, track the names you see and compare against a hand-maintained list to spot plugins that register the same string, which can mask ownership problems.
Pitfalls
Does not fire for duplicates or empty names
If a permission already exists or the string is null/empty,
RegisterPermissionreturns early andOnPermissionRegisterednever runs. Do not rely on it to observe every call toRegisterPermission.Fires during plugin init, not on demand
The hook runs while other plugins are loading. Anything you need there, such as
permission.PermissionExists, may not be populated for plugins that load after yours, so prefer deferring dependent logic toOnServerInitializedor a short timer.Keep the handler cheap
Every permission of every plugin passes through this hook at startup. Avoid file I/O, chat broadcasts, or console prints inside the handler and flush collected data later.
Build a plugin with OnPermissionRegistered
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnPermissionRegistered
Generate