
Role-based access control (RBAC) shouldn't mean dragging in heavy, runtime-dependent frameworks. Meet @polymech/acl, our new lightweight, zero-dependency ACL library for Node.js.
Inspired by the precision of Zend_ACL, we built @polymech/acl to solve complex permission scenarios—like path-scoped virtual filesystems and dynamic API gateways—without the bloat.
Whether you're enforcing row-level security in Postgres or securing a complex frontend-backend hybrid, this pure ESM library gives you the granular control you need.
Why Another ACL Library?
Modern Node.js applications need security rules that integrate closely with their specific domain logic. Often, developers reach for generalized, heavy frameworks or end up writing sprawling if/else permission checks scattered across the repository.
@polymech/acl is built on four core principles:
- Zero Dependencies: Beyond an optional
pinologger, it relies entirely on its own pure ESM code. - Role Hierarchies: Define roles that inherit permissions from parents, drastically reducing boilerplate and management overhead.
- Bring Your Own Storage: Includes robust in-memory and file-based backends for dev/testing, and an
IBackendinterface so you can easily plug it into Redis, Postgres, or anything else. - Path-Scoped Precision: Easily support complex, nested, hierarchical resource protections—ideal for Virtual Filesystems (VFS) and multi-tenant platforms.
Core Concepts at a Glance
@polymech/acl uses standard, predictable RBAC definitions:
- User: An identifier (string or number) representing a subject.
- Role: A named group of permissions assigned to users.
- Resource: The object being protected (e.g.
"posts","settings", or a VFS path like"/shared/reports"). - Permission: The action allowed on a resource (
"read","write","delete").
Pro tip: The wildcard "*" grants all permissions on a resource.
Quick Start Example
Here's how quickly you can set up a fully functioning role hierarchy and start verifying user access dynamically.
import { Acl, MemoryBackend } from '@polymech/acl';
const acl = new Acl(new MemoryBackend());
// 1. Define permissions
await acl.allow('viewer', 'posts', 'read');
await acl.allow('editor', 'posts', ['read', 'write', 'delete']);
await acl.allow('admin', 'settings', '*');
// 2. Set up role inheritance
await acl.addRoleParents('admin', 'editor'); // Admin gets everything Editor gets
await acl.addRoleParents('editor', 'viewer'); // Editor gets everything Viewer gets
// 3. Assign roles to users
await acl.addUserRoles('alice', 'editor');
await acl.addUserRoles('bob', 'viewer');
// 4. Check access efficiently
await acl.isAllowed('alice', 'posts', 'write'); // true
await acl.isAllowed('bob', 'posts', 'write'); // false
await acl.isAllowed('bob', 'posts', 'read'); // true
Batch Grants
For enterprise setups with numerous overlapping rights, @polymech/acl lets you define permissions in bulk arrays, eliminating massive configuration walls:
await acl.allow([
{
roles: 'moderator',
allows: [
{ resources: 'posts', permissions: ['read', 'edit', 'flag'] },
{ resources: 'comments', permissions: ['read', 'delete'] },
],
},
]);
Real-World Use Case: The Virtual Filesystem (VFS)
We originally built @polymech/acl to power the Filebrowser tool in the PolyMech ecosystem. It enforces fine-grained, path-scoped access control where each user owns a folder and can explicitly grant nested permissions to their team.
Path-Scoped Resolution
When a user tries to access /shared/reports/q1.pdf, the system walks the resource chain upwards to verify inherited permissions:
vfs:<ownerId>:/shared/reports/q1.pdf(Most Specific)vfs:<ownerId>:/shared/reportsvfs:<ownerId>:/shared(Allows "team" group access here)vfs:<ownerId>:/
Access is granted if any level in the hierarchy explicitly allows the requested operation. It's incredibly fast and easy to reason about.
Ready to secure your Node.js apps?
- Check out the source repository for full VFS integration details and our Supabase/Postgres Row-Level Security guides.
- Install it today:
npm i @polymech/acl