Skip to content
RustPluginGenerator

Oxide Permissions and Groups: Commands, Inheritance and Plugin Code

Oxide permissions are named strings such as `myplugin.use` that plugins register and that server owners grant to players or groups with `oxide.grant`. Groups bundle permissions and players, support a parent for inheritance, and ship with two defaults: `default` and `admin`. Developers register permissions in Init and check them with `permission.UserHasPermission`.

6 min readUpdated Oct 26, 2026

Oxide permissions are string names, usually written pluginname.action, that plugins register and that you assign to players or groups with console commands such as oxide.grant. A plugin checks whether a player holds a permission before it runs a feature. Carbon loads Oxide plugins and accepts the same oxide.* commands, so everything below applies there too.

How the permission system works

Three pieces exist:

  • Permissions: names registered by plugins. A permission that no plugin registered cannot be granted.
  • Groups: named collections. A group can hold permissions, have members and optionally inherit from a parent group.
  • Users: players identified by their 64-bit Steam ID. A user gets permissions directly or through the groups they belong to.

A player has a permission if it was granted to them directly, to any group they are in, or to a parent of one of those groups. Lookups are case-insensitive.

Permission data is stored by Oxide in its data folder (oxide/data, or carbon/data on Carbon), so it persists across restarts and map wipes.

Where to run the commands

Run them in the server console, over RCON, or in the F1 console as an owner. Chat does not run oxide.* commands. For the full command list see Oxide console commands.

Granting and revoking permissions

CommandEffect
oxide.grant user <name or id> <perm>Give a permission to one player
oxide.grant group <group> <perm>Give a permission to a group
oxide.revoke user <name or id> <perm>Remove a direct user permission
oxide.revoke group <group> <perm>Remove a group permission

Examples:

Text
oxide.grant user 76561198000000000 vip.use
oxide.grant group vip vip.use
oxide.grant group default kits.use
oxide.revoke group default kits.use
oxide.grant user "Player Name" backpacks.use

Prefer Steam IDs over names. Names can be ambiguous or contain spaces, and a name only matches players Oxide currently knows about. Use * as the permission to grant everything only if you truly intend it; it is rarely a good idea outside the admin group.

Revoking a user permission does not remove it if the player still receives it through a group. Check with oxide.show user.

Managing groups

CommandEffect
oxide.group add <group> [title] [rank]Create a group
oxide.group remove <group>Delete a group
oxide.group set <group> <title> <rank>Change title and rank
oxide.group parent <group> <parent>Make a group inherit from another
oxide.usergroup add <player> <group>Put a player in a group
oxide.usergroup remove <player> <group>Take a player out of a group

Example setup for a VIP tier:

Text
oxide.group add vip "VIP" 1
oxide.grant group vip vipgatherbonus.use
oxide.usergroup add 76561198000000000 vip

The title and rank are used by plugins that display group names, such as chat title plugins. The rank is a number; plugins that care about ordering use it.

Inspecting what is assigned

CommandShows
oxide.show permsEvery registered permission
oxide.show perm <perm>Which users and groups hold it
oxide.show user <name or id>A player's groups and permissions
oxide.show group <group>A group's members and permissions
oxide.show groupsAll groups

When a player says a feature does not work, run oxide.show user <id> first. It tells you in one output whether the permission is missing or the group membership never happened.

The default and admin groups

Oxide creates two groups:

  • default: every player is in it automatically. Granting a permission here gives it to everyone.
  • admin: intended for staff. Members are not automatically given all permissions; they receive only what you grant to this group.

Keep default limited to features you want all players to use, and grant sensitive permissions (teleporting others, entity removal, vanish) only to the group or players that need them.

Group inheritance

Setting a parent makes a group receive the permissions of the parent as well as its own:

Text
oxide.group add vip "VIP" 1
oxide.group add vipplus "VIP+" 2
oxide.group parent vipplus vip

Now anyone in vipplus has every vip permission plus whatever is granted to vipplus. Inheritance flows in one direction: parents do not receive child permissions. You can chain several levels, such as vip to vipplus to vipelite, so each tier only needs to list what it adds.

A common hierarchy is default as the base, vip inheriting from it, and admin separate. Put each permission at the lowest tier that should have it.

Naming rules

Every permission a plugin registers should start with the lowercase class name followed by a dot:

  • Plugin VipKits registers vipkits.use, vipkits.gold
  • Plugin Backpacks registers backpacks.use

If the name does not start with that prefix, Oxide logs a warning, though the permission still registers. See missing plugin name prefix for permission. The prefix prevents two plugins from colliding on a generic name like use, and it makes oxide.show perms easy to scan.

Registering the same permission twice produces a different problem, covered under duplicate permission registered.

Registering and checking permissions in code

Register in Init so permissions exist before anyone can run a command. See Init.

C#
using System.Collections.Generic;

namespace Oxide.Plugins
{
    [Info("Vip Perk", "RustPluginGenerator", "1.0.0")]
    [Description("Heals players who have the use permission")]
    public class VipPerk : RustPlugin
    {
        private const string PermUse = "vipperk.use";

        private void Init()
        {
            permission.RegisterPermission(PermUse, this);
        }

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["NoPermission"] = "You do not have permission to use this.",
                ["Healed"] = "You were healed."
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args) =>
            string.Format(lang.GetMessage(key, this, userId), args);

        [ChatCommand("vipheal")]
        private void CmdVipHeal(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;

            if (!permission.UserHasPermission(player.UserIDString, PermUse))
            {
                player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            player.Heal(player.MaxHealth() - player.health);
            player.ChatMessage(Msg("Healed", player.UserIDString));
        }
    }
}

UserHasPermission takes the player's ID as a string, so use UserIDString. Passing a ulong produces a type error; see CS1503 cannot convert from ulong to string.

Other useful members of the permission API include checking group membership and granting from code, but for most plugins register plus UserHasPermission is enough. Messages shown to players should go through the lang API, as in the example; see Rust plugin localization.

Checking inside hooks

Permission checks inside frequently fired hooks must stay cheap and must not log. For example in OnEntityTakeDamage, null-check the HitInfo and the player first, and only then call UserHasPermission. Remember that for many hooks any non-null return cancels the action, so return null when the player lacks the permission and you want default behavior. Check the individual hook page before returning a value.

Tiered VIP permissions pattern

For tiers, register one permission per tier and pick the best match. Keep tier values in the config so admins can tune them:

C#
using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace Oxide.Plugins
{
    [Info("Tiered Bonus", "RustPluginGenerator", "1.0.0")]
    [Description("Applies a bonus multiplier based on permission tier")]
    public class TieredBonus : RustPlugin
    {
        private const string PermVip = "tieredbonus.vip";
        private const string PermVipPlus = "tieredbonus.vipplus";
        private const string PermVipElite = "tieredbonus.vipelite";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Vip multiplier")]
            public float Vip = 1.25f;

            [JsonProperty("Vip plus multiplier")]
            public float VipPlus = 1.5f;

            [JsonProperty("Vip elite multiplier")]
            public float VipElite = 2f;
        }

        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);

        private void Init()
        {
            permission.RegisterPermission(PermVip, this);
            permission.RegisterPermission(PermVipPlus, this);
            permission.RegisterPermission(PermVipElite, this);
        }

        private float GetMultiplier(BasePlayer player)
        {
            if (player == null) return 1f;
            string id = player.UserIDString;
            if (permission.UserHasPermission(id, PermVipElite)) return config.VipElite;
            if (permission.UserHasPermission(id, PermVipPlus)) return config.VipPlus;
            if (permission.UserHasPermission(id, PermVip)) return config.Vip;
            return 1f;
        }

        private object OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item)
        {
            var player = entity as BasePlayer;
            if (player == null || item == null) return null;

            float multiplier = GetMultiplier(player);
            if (multiplier > 1f)
                item.amount = (int)(item.amount * multiplier);

            return null;
        }
    }
}

Order the checks from the highest tier down so the first match wins. Then pair the permissions with group inheritance: grant tieredbonus.vip to vip, tieredbonus.vipplus to vipplus, and so on. A player in vipplus then also holds the VIP permission through the parent, and the highest-first check still returns the right tier. For a ready-made version of the idea see the VIP gather bonus and VIP kit tiers recipes. Check the OnDispenserGather page for the exact parameters before relying on a signature.

Reacting to permission changes

OnUserPermissionGranted fires when a user receives a permission directly. Plugins that cache per-player state, such as a UI or tier lookup, can use it to refresh, but a simple per-call UserHasPermission check is usually cheap enough that no cache is needed.

Common mistakes

  • Granting before loading. The plugin must be loaded so the permission exists. Otherwise you see permission doesn't exist.
  • Typo in the group name. Granting to a group that was never created gives group doesn't exist. Run oxide.show groups to confirm.
  • Registering in the wrong place. Register in Init, not inside a command or hook that runs later; otherwise early grants fail.
  • Wrong prefix. Using a name that does not start with the lowercase class name logs a warning and makes permission lists confusing.
  • Passing a ulong. Always use UserIDString for permission calls.
  • Assuming admin means all permissions. Grant explicitly to the admin group.
  • Granting * or too much to default. Everyone then gets the feature. Review oxide.show group default regularly.
  • Revoking from the user while a group still grants it. Use oxide.show user to see the source.
  • Renaming a permission in an update. Existing grants point at the old name and silently stop working. Tell users when a plugin changes its permissions.

Quick workflow for server owners

  1. Install the plugin and confirm it loaded with oxide.plugins.
  2. List its permissions: oxide.show perms and look for the plugin prefix.
  3. Create groups once: oxide.group add vip "VIP" 1.
  4. Grant permissions to groups, not individual players, whenever more than one player needs them.
  5. Add players to groups with oxide.usergroup add.
  6. Verify with oxide.show user <id>.

For automatic promotion based on playtime, see the playtime group promotion recipe, or describe your own permission-gated plugin in the plugin generator.

FAQ

Does a player in the admin group automatically get every plugin permission?

No. Membership in the admin group only gives the permissions granted to that group. Being a Rust owner or moderator is a separate concept. Plugins may check player.IsAdmin on their own, but that is plugin logic and not part of the permission system.

Are permission names case-sensitive?

Lookups are case-insensitive, so MyPlugin.Use and myplugin.use refer to the same permission. By convention, use lowercase everywhere so commands, configs and documentation stay consistent.

Can I grant a permission before the plugin is loaded?

Oxide can only grant permissions it knows about. If the plugin that registers the permission is not loaded, the grant fails with a message that the permission does not exist. Load the plugin first, then grant. See the permission-doesnt-exist error page.

Do permissions survive a wipe?

Yes. Permission and group assignments are stored in Oxide's data folder, not in the Rust save, so a map wipe does not remove them. Delete or edit the permission data files only if you deliberately want to reset every assignment.

How do I give a permission to everyone?

Grant it to the default group, for example oxide.grant group default myplugin.use. Every player belongs to default, so the permission applies to all of them without per-player grants.

Skip the C#: describe your plugin

Open generator