npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dreamtree-org/rbac-authz

v0.4.4-alpha

Published

Dreamtree's reusable Authorization & RBAC-as-a-Service platform — multi-tenant RBAC, policy engine (deny-by-default, explicit-deny-overrides-allow), API keys, BYODB, and a CLI to bootstrap a client project.

Readme

@dreamtree-org/rbac-authz

npm version node license

Multi-tenant authorization that answers one question, consistently and explainably:

"Can this subject perform this action on this resource?"

Deny-by-default · every answer is an explainable decision, never a silent boolean · audit-chained · bring your own database (SQLite / PostgreSQL / MySQL) or none at all.

60-second start

npm install @dreamtree-org/rbac-authz    # + sqlite3 / pg / mysql2 to match your db
import { rbac } from "@dreamtree-org/rbac-authz";

const authz = await rbac({ db: "sqlite:./app.db" }); // rbac() alone → env vars, or in-memory for dev

await authz.define({
  roles: {
    accountant: ["invoice.read.all", "invoice.create.own"],
    admin:      { superadmin: true },
  },
});
await authz.assign("user_123", "accountant");

await authz.require("user_123", "invoice.read.all");   // ✓ passes
await authz.require("user_123", "invoice.delete.all"); // ✗ throws AuthzDeniedError
// e.decision → { allowed: false, reason: "Denied by default: no policy grants invoice.delete.all", … }

That's the whole core. Node ≥ 22.

Pick your integration

| You have | Do this | |---|---| | An existing Express route to protect | authz.guard("invoice.read.all")recipe | | An existing app, any framework | authz.middleware() populates req.authzExpress/Fastify/Hono/Next | | No infra yet, want a mountable API too | add authz.router() alongside authz.middleware()same section | | A UI to click around in | npx rbac-authz-api init/setup/startthe admin console | | A legacy authorizer to retire safely | the shadow gate — recipe |

guard() is the quickest one-liner but Express-only at the front door (other frameworks: /sdk's createRequirePermission); authz.middleware() ships all four wrappers directly.

The four ideas

  1. Permission — a string: resource.action.scope. Scopes: own · team · org · all (broader satisfies narrower). invoice.read.own = read your own invoices.
  2. Role — a named list of permissions. define() is declarative and idempotent: re-running it reconciles each role to exactly what you wrote.
  3. Subject — whoever is acting: an id string, or { type: "service_account", id }.
  4. Decision — every check produces a full AuthzDecision (reason / matchedPolicy / deniedBy) on a tamper-evident audit chain. allowed() is just its boolean projection — nothing is ever a silent boolean.

Recipes

Guard an Express route (Fastify/Hono/Next wrappers live in /sdk):

app.get("/invoices", authz.guard("invoice.read.all"), listInvoices);
// reads req.user.id; custom: authz.guard(perm, { subject: (req) => req.session.uid })
// deny → 403 with the full decision; no subject on the request → 500, fail closed

Bind once per request; ownership scopes:

const user = authz.for(req.user.id);
await user.require("doc.read.own",  { resourceId: "d9", owner: doc.ownerId });
await user.require("doc.read.team", { owner: doc.ownerId, team: teamMemberIds });
if (await user.allowed("report.export.org")) showExportButton();

Narrow one user below their role (direct grants can never widen — REQ-SUB-7):

await authz.grant("temp_hire", "invoice.read.own"); // overrides the role's invoice.read.all
await authz.revoke("temp_hire", "invoice.read.own"); // role grant applies again

Multi-tenant (invisible until you need it):

const acme = await authz.tenant("acme"); // auto-created, fully isolated
await acme.assign("u1", "accountant");   // grants in "acme" exist nowhere else

Gate by subscription plan (narrow-only hook — it can remove grants, never add):

const authz = await rbac({
  db: "postgres://…",
  permissionFilter: (statements, { tenantId }) =>
    statements.filter((s) => planCovers(tenantId, s.permission)),
});

Migrate off a legacy authorizer — zero data migration, one-flag rollback:

import { BillingRbacAdapter, createShadowGate, shadowFlagsFromEnv } from "@dreamtree-org/rbac-authz/migration";

const adapter = new BillingRbacAdapter({ db: { rows: (t, w) => knex(t).where(w) } });
const gate = createShadowGate({
  flags: shadowFlagsFromEnv(), // RBAC_AUTHZ_SHADOW: run both, enforce legacy, log diffs
  legacy: legacyAuthorize,     // RBAC_AUTHZ_ENABLED: cut over
  engine: (await rbac({ db: adapter })).client,
  onDiff: (d) => log.warn("authz shadow diff", d),
});

Manage the data (CRUD, by name — no ids, ever)

await authz.roles.list();                 // [{ name, permissions, superadmin, weight, active }]
await authz.roles.rename("accountant", "finance");
await authz.roles.deactivate("finance");  // soft — grants nothing until .activate()
await authz.roles.remove("finance");      // hard — cascades assignments + permission links

await authz.permissions.list();
await authz.permissions.remove("invoice.export.all"); // cascades out of roles, modules, direct grants

await authz.subjects.get("user_123");     // "what can this subject do, and why?"
// { roles: ["accountant"], grants: ["report.export.own"],
//   permissions: [ …the effective set the engine evaluates ], superadmin: false }

await authz.tenants.list();
await authz.tenants.deactivate("acme");   // the whole tenant denies until reactivated

Creation stays declarative: define() for roles/permissions, assign()/grant() for access, authz.tenant() for tenants — these namespaces are the read/manage half.

Embed it — no separate process

Skip running anything standalone: mount a subject-populating middleware and a tenant-scoped slice of the REST API straight into your own Express/Fastify app.

import { rbac } from "@dreamtree-org/rbac-authz";
const authz = await rbac({ db: "sqlite:./app.db" });

const app = express();
app.use(authz.middleware()); // req.authz = { check, require, allowed } for req.user.id
app.use(authz.router());     // mounts /authz/check, /api/audit, /api/info
app.listen(3000);

Mount authz.router() at the app's root — no path prefix, its routes are already namespaced. It's deliberately narrow: no /admin, no cross-tenant registry, no /api/roles//api/permissions (those need a service key scoped to your own tenant, or the standalone console below). authz.middleware() has .fastify()/.hono()/.next() wrappers too; authz.router() is Express/Fastify only (.fastify()).

The admin console & HTTP API

Prefer clicking? The same package ships a zero-dependency admin console + JSON API:

npx rbac-authz-api init    # scaffolds services/rbac/ + .env.example in your project
# fill in .env:  RBAC_DATABASE_URL=./rbac.sqlite   RBAC_TENANT_NAME=dev
npx rbac-authz-api setup   # migrates (expand-only, safe to rerun), seeds your tenant +
                           # a superadmin, prints a one-time admin password (never stored)
npx rbac-authz-api start   # → http://localhost:4001/admin

The console covers applications, tenants, subjects, roles (+ module bundles), permissions, API keys (shown once, hash-only), the audit log with chain verification, and an authorization playground. The same server exposes the REST API — /authz/check, /api/roles, /api/permissions, /api/applications, /api/tenants, /api/keys, /api/audit. In production set RBAC_ADMIN_PASSWORD (console login) and RBAC_REQUIRE_API_KEY=1 (service endpoints need a Bearer dt_rbac_… key).

Guarantees (they hold under every sugar above)

  • Deny by default; an explicit deny overrides any allow, even for a superadmin.
  • Explainable + audited — every decision carries its reason and lands on a hash-chained audit log (pass auditKey in production).
  • Tenant-isolated at the storage layer; all never means cross-tenant.
  • Narrow-only extension pointspermissionFilter, direct grants, and API-key scopes can reduce access, never widen it; forged or edited grants reject the check.
  • No plaintext secrets — API keys are shown once, stored as hash + prefix + last4.

Going deeper

| You want | Where | |---|---| | Full SDK: client, checker, hooks, principal cache, middleware family | @dreamtree-org/rbac-authz/sdk | | The pure engine: evaluate, permission grammar, API keys, audit chain | root import (advanced exports) | | Your own storage: the RbacDatabaseAdapter port + adapters | @dreamtree-org/rbac-authz/db-adapters | | Legacy migration kit: mapper, compat adapter, shadow gate | @dreamtree-org/rbac-authz/migration | | HTTP API + admin console | npx rbac-authz-api init && npx rbac-authz-api setup && npx rbac-authz-api start | | Teach your AI coding agent this library | npx rbac-authz init --ai claude · MCP: npx @dreamtree-org/rbac-authz mcp | | Architecture, security model, BYODB, requirements | the source repo docs/ |

Versioning

Pre-1.0.0, the minor version is the effective "major"; anything that can flip an allow/deny outcome is never a silent patch. @latest always points at the newest release. Full policy: docs/NPM_PUBLISHING.md in the source repo.

License

MIT © Dreamtree — see LICENSE.