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

@ultimat3/policy

v18.0.0

Published

The one authz rule, evaluated identically in every surface

Downloads

5,666

Readme

@ultimat3/policy 🔐

One authz system. Two authz systems — one for HTTP, one for "the API", one for jobs — is how every Meteor-shaped framework died: the surfaces drift, one of them is wrong, and nobody finds out until it is a CVE. This package exists so a second one is never necessary. Every MCP tool, live query, job and route resolves the same policy object through the same evaluate().

export const publishPost = action({
  policy: can('post:publish', ({ input, actor }) => ownsPost(actor, input.postId)),
});

No exception, as of 1.3.0. @ultimat3/auth's requireRole() / requireScope() used to gate routes on the ambient actor without evaluating a policy; they are deleted. They were documented here as "one honest exception" and had zero callers in the framework or in either tracked app — a sanctioned second door nobody walked through, whose own documentation admitted that a route gated that way is invisible to x policy list, to framework.manifest.json and to openapi.json. Gate a route with a Policy: can('admin:access'), which every introspection surface can read. requireActor() and currentActor() remain — those assert authentication, which is what @ultimat3/auth produces.

Shape

A policy is a pure (input, actor, row, ctx) => PolicyDecision.

type PolicyDecision =
  | { allowed: true }
  | { allowed: false; reason: string; code: string };

reason is always safe to log (it names permissions, never row data) and useful to an agent: actor lacks post:publish and post:publish predicate returned false are different problems with different fixes.

One predicate signature, every surface

interface PolicyArgs<I = unknown, R = unknown> {
  input: I;
  actor: Actor | null;
  row: R | null; // required — `null` means "this rule decides on input alone"
  ctx?: Ctx;
}

A predicate is written once and is correct in an HTTP route, a job, an MCP tool and a live query's per-row gate:

// decides on input alone — `row` is null
can<{ orgId: string }>('post:create', ({ actor, input }) => actor?.orgId === input.orgId);

// decides about a row the surface already loaded
can<{ postId: string }, Post>('post:publish', ({ actor, row }) => row?.authorId === actor?.id);

row is required and nullable, not optional. An optional field is how the two shapes drifted apart the first time: the realtime row gate nested the row inside input, so a row rule and an input rule received different objects and nothing caught it.

Callers have it easier — EvaluateArgs.row is optional, and evaluate() normalises a missing row to null. A surface that has no row passes { input, actor, ctx } unchanged.

Combinators

| Builder | Behaviour | |---|---| | can(p, predicate?) | permission first, then the row-level predicate | | allow() / deny(reason) | terminal; allow() is how "public" is said out loud | | and(...) | first denial wins, its reason is the reason; no clauses is X_POLICY_CLAUSE_EMPTY | | or(...) | first allowance wins; otherwise the last denial — except one carrying X_UNAUTHENTICATED, which outranks a later one. No clauses is X_POLICY_CLAUSE_EMPTY | | not(p) | inverts — except X_UNAUTHENTICATED, which propagates unchanged |

not() never turns "there is no actor" into an allow. can() denies a null actor with X_UNAUTHENTICATED, and inverting that would make not(can('order:internal')) — the natural simplification of and(can('order:read'), not(can('order:internal'))) — a public door into the internal one. or() is what makes that true of a whole TREE rather than only of not's direct child: it used to report the LAST denial, so not(or(can('order:internal'), deny('read-only mode'))) allowed an anonymous callerdeny's X_FORBIDDEN had overwritten the code not() had to recognise.

and() and or() refuse an empty clause list at the call that builds them. Nobody writes and(); they write and(...requiredCaps.map(can)) over a config-driven list that filters to nothing — and an empty and() found nothing to deny and allowed everyone, anonymous callers included, on all four surfaces, with no diagnostic beyond a label reading and(). allow('public') and deny('<reason>') are the explicit spellings, so the refusal costs you nothing you cannot say another way.

policy.permissions (or policyPermissions(policy)) is the flattened, deduped, sorted list of every permission a tree references, not() clauses included. It is what a compliance report has to read: label renders a composite as and(post:publish, org:administer), which is a sentence, never a permission.

admitsAnonymous(policy) is the other derived question As of 2026-08, and it is a walk, not a root read: whether an anonymous caller can be allowed at all. policy.kind === 'allow' is the read it replaces, and it answered "needs a session" for or(allow(), can('x:y')) — so an HTTP route 401'd a caller the policy itself allows, while the same policy over MCP or a job let that caller in.

import { admitsAnonymous, allow, and, can, not, or } from '@ultimat3/policy';

admitsAnonymous(or(allow('public'), can('post:publish'))); // true
admitsAnonymous(and(allow('public'), can('post:publish'))); // false
admitsAnonymous(not(can('order:internal'))); // false — X_UNAUTHENTICATED propagates

It is exact for an anonymous caller, not a heuristic: with actor === null, can() short-circuits on the actor check before its predicate runs and allow()/deny() ignore their arguments, so no predicate is ever consulted and the tree alone decides. true never means "unguarded" — it says only that a 401 before the handler is wrong; the surface still calls enforce(). @ultimat3/action and @ultimat3/query derive RouteMeta.auth from it.

Four surfaces, four adapters, one rule

surfaces.ts is the proof. Each adapter evaluates and maps a denial to that surface's error shape; allowed returns undefined.

| Adapter | Denial shape | |---|---| | enforceHttp | 403 + RFC-9457 fields | | enforceLive | close frame 4403 | | enforceJob | failed, retryable: false — the answer will not change on retry | | enforceMcp | isError: true with readable text |

Adding a fifth surface means adding an adapter here and nothing else.

enforce(surface, policy, args) dispatches over that table with Object.hasOwn, and a surface with no adapter is X_POLICY_SURFACE_UNKNOWN. Not a formality: the table is an object literal, so it inherits Object.prototypeenforce('valueOf' as Surface, …) used to call Object.prototype.valueOf with the table as its receiver and return a truthy value, so an authz dispatch failed closed with a SurfaceDenial no caller could read.

Permissions and roles

definePermissions(['post:publish', ...]) gives a typed set; augmenting PermissionRegistry (which x g policy generates) makes a typo a compile error, and can() throws X_PERMISSION_UNKNOWN at declaration time either way. Roles are sugar: defineRoles({ owner: { grants: ['post:delete'], inherits: ['editor'] } }) expands depth-first to a flat set, cycles included. post:* and * are supported.

defineRoles() merges into the app's one role map. A second call in a new feature folder adds roles; it never deletes the first module's. A role two modules define differently is X_ROLE_REDEFINED, naming both declaration sites — and an identical re-declaration is a no-op, so defineRoles({ ...roleDefinitions(), … }) stays legal.

The flattened grant set is memoised per actor and invalidated the moment the role map changes. It is keyed on the actor object, so it lives exactly as long as the request does: @ultimat3/auth re-reads the user row every request, and a revoked role takes effect on the next one.

Traces

evaluate() returns a depth-first trace naming the clause that decided. /_x renders it, explain() logs one line, and policyMatrix() turns actors × policy into an assert-ready table:

owner   allow
editor  deny   post:read predicate returned false
viewer  deny   actor lacks post:publish

Building it is opt-in: on outside production, and in production only once a decision sink is installed. A live query evaluates policy per subscriber on every change event, so an unread TraceEntry[] per evaluation is real allocation on the busiest path there is. Force it with evaluate(policy, args, { trace: true }).

The decision log

setDecisionSink({
  record(event) {
    // { label, allowed, code, reason, actorId, actorKind, orgId, surface, deciding }
  },
});

No-op until installed, emitted from one place — inside evaluate(), so a fifth surface inherits it — and it records the allow as well as the denial, which is the half an access review actually asks for. It never carries row or input: reason is safe to log by construction, and the sink inherits that guarantee. A sink that throws is logged and swallowed; it never turns an allowed request into a 500.

Errors

X_FORBIDDEN · X_POLICY_MISSING · X_PERMISSION_UNKNOWN · X_POLICY_SURFACE_UNKNOWN · X_ROLE_REDEFINED

A missing policy is a type error, not a throw: ActionDef.policy is required, so an action without one does not compile. policyMissing() stays for a declaration site that cannot say it in a type — a config-driven route table, a policy resolved by name.

Boundaries

Tier 2. Imports @ultimat3/core only. Surface error shapes are declared structurally so this package never imports @ultimat3/http (sibling tier) or the tier-3/4 surfaces that import it.