Skip to content

ItzXynx/Discord-Permission-Engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Discord Permission Engine

A faithful, minimal implementation of Discord's permission resolution algorithm — built with pure functions, BigInt bitmasks, and zero dependencies.


Why

Discord's permission system looks simple on the surface (it's just bitwise math) but its real behaviour depends on a strict resolution order across roles, @everyone, role overwrites, and member-specific overwrites. Getting that order wrong is the most common source of permission bugs in bots and self-hosted tools.

This library implements the algorithm exactly as Discord documents it, in roughly 100 lines of core logic.


How the algorithm works

Permissions are resolved in this exact order:

  1. Base permissionsOR together every role the member has, including @everyone.
  2. Administrator short-circuit — if the ADMINISTRATOR flag is set, return all permissions immediately. No overwrite can revoke it.
  3. @everyone channel overwrite — apply (perms & ~deny) | allow.
  4. Role overwrites — accumulate allow and deny across every role overwrite that matches one of the member's roles, then apply (perms & ~deny) | allow once.
  5. Member overwrite — apply the member-specific overwrite last. This always wins.

The core formula at every overwrite step is:

permissions = (permissions & ~deny) | allow

Install

git clone <this-repo>
cd discord-permission-engine
npm install

No dependencies are required.


Library usage

const { resolve, PERMISSIONS } = require('./src/resolver');

const result = resolve({
  member: { id: 'user_1', guildId: 'guild_1', roles: ['mods'] },
  roles: [
    { id: 'guild_1', name: '@everyone', permissions: '1024' },
    { id: 'mods',    name: 'Moderators', permissions: '8192' }
  ],
  channel: {
    overwrites: {
      everyone: { allow: '0', deny: '2048' },
      members:  [{ id: 'user_1', allow: '0', deny: '8192' }]
    }
  }
});

result.has('VIEW_CHANNEL');     // true
result.has('SEND_MESSAGES');    // false  (denied by @everyone overwrite)
result.has('MANAGE_MESSAGES');  // false  (denied by member overwrite)
result.permissions;             // ['VIEW_CHANNEL']
result.bitmaskString;           // '1024'

CLI usage

node cli.js examples/basic.json

Output:

CREATE_INSTANT_INVITE  ❌
KICK_MEMBERS           ❌
...
VIEW_CHANNEL           ✅
SEND_MESSAGES          ✅
MANAGE_MESSAGES        ❌
...

Filter to just the flags you care about:

node cli.js examples/conflict.json --filter VIEW_CHANNEL,SEND_MESSAGES,MANAGE_MESSAGES
VIEW_CHANNEL     ✅
SEND_MESSAGES    ❌
MANAGE_MESSAGES  ❌

JSON output:

node cli.js examples/basic.json --json

Input shape

{
  "member": {
    "id": "user_1",
    "guildId": "guild_1",
    "roles": ["role_a", "role_b"]
  },
  "roles": [
    { "id": "guild_1", "name": "@everyone", "permissions": "1024" },
    { "id": "role_a",  "permissions": "2048" }
  ],
  "channel": {
    "overwrites": {
      "everyone": { "allow": "0", "deny": "0" },
      "roles":    [{ "id": "role_a", "allow": "0", "deny": "0" }],
      "members":  [{ "id": "user_1", "allow": "0", "deny": "0" }]
    }
  }
}

Permission values are strings to safely round-trip through JSON without losing precision (Discord uses 53+ bit values). Internally everything is BigInt.


Edge case worth knowing

In examples/conflict.json, the user has the Moderators role, which is granted MANAGE_MESSAGES both at the guild level and via a role overwrite. Yet the final resolution returns MANAGE_MESSAGES = false.

Why? Because the member-specific overwrite is always applied last, after all role overwrites have been merged. A single member overwrite can revoke a permission that every one of the user's roles grants. This is the rule that trips most people up — and it's the exact behaviour Discord's client and API enforce.

The only thing that bypasses this is ADMINISTRATOR, which short-circuits the entire pipeline.


Tests

npm test

Covers base merging, admin override, conflicting role overwrites, member-overwrite priority, and the no-channel path.


Project layout

src/
  flags.js        permission constants (BigInt bit shifts)
  engine.js       pure resolution logic
  formatter.js    bitmask → readable output
  resolver.js     public API
cli.js            CLI entry
examples/         sample inputs
tests/            node:test suite

License

MIT

Discord-Permission-Engine

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors