PackagePass

Permissions

Understand how PackagePass decides who can publish, install, and manage packages.

PackagePass authorization is built from subjects, resources, actions, grants, and token capabilities. The same model protects the control API and the npm-compatible registry, but each surface accepts different token kinds.

The mental model

A request is allowed when the authenticated principal has a matching token capability or grant for the requested action and resource.

Principal

The authenticated caller. A principal is either the bootstrap admin token, a stored PackagePass token, or anonymous.

Subject

The identity behind a token or grant: user, group, customer, or agent. Agents cover AI workers, services, and CI jobs.

Resource

The thing being protected: tenant, scope, package, or package version.

Action

The operation being requested, such as package:install, package:publish, or grant:manage.

Where permissions are enforced

SurfaceAccepted token kindsPermission check
Control APIapiAPI routes require a valid API-class token. CI and publish tokens are also accepted by the control plane.
npm registryinstallRegistry package and tarball requests authenticate the token, then require package:install on the package or package version.
Bootstrap adminbootstrap admin tokenBypasses normal grants and capabilities. Use it only for initial tenant setup and emergency administration.
Denied registry requestany tokenUnauthorized packument and tarball requests return 404 instead of 403 so package names are not leaked.

Decision flow

How an authorization decision is made

The policy engine evaluates cheap, explicit checks first, then falls back to grants for the subject and resource.

  1. 1

    Authenticate the bearer token

    The request becomes either a bootstrap admin principal, a token principal, or anonymous. Revoked and expired tokens authenticate as anonymous.
  2. 2

    Reject cross-tenant use

    If the token belongs to one tenant and the resource belongs to another, authorization stops with a tenant mismatch.
  3. 3

    Check token capabilities

    Token capabilities are evaluated before grants. A matching inline capability is enough to allow the action.
  4. 4

    Expand the resource

    PackagePass checks the requested resource plus broader parents such as package, scope, and tenant.
  5. 5

    Find matching grants

    A grant must be in the same tenant, not revoked, for the same subject, attached to one candidate resource, and include the requested action.
  6. 6

    Apply enforced conditions

    The current policy engine enforces expiresAt. Other condition fields are stored with the grant but are not enforced by authorization yet.

Resource inheritance

Grants can be attached narrowly or broadly. When PackagePass checks a package version, broader package, scope, and tenant grants can satisfy the request.

Requested resourceChecked grant resourcesMeaning
tenanttenantA tenant grant is tenant-wide.
scopescope, then tenantA tenant grant can satisfy scope checks.
packagepackage, then scope, then tenantA scope or tenant grant can satisfy package checks.
package_versionpackage_version, then package, then scope, then tenantA version-specific grant is narrowest; broader package, scope, or tenant grants still apply.

Actions

ActionAllows
package:readRead package metadata through the control API. A package:install grant also satisfies package:read checks.
package:installRead npm packuments and download tarballs from the registry.
package:publishCreate package metadata and publish release or preview tarballs.
package:deprecateMark a version as deprecated.
package:yankYank a version and remove dist-tags pointing at it.
package:manage_dist_tagsSet and remove dist-tags.
grant:manageCreate grants and manage package metadata such as description, readme, and archive status.
token:mintCreate API, install, or CI tokens.
audit:readRead audit events.

Subject references

ReferenceUse
user:alice@example.comA person identified by email.
customer:globexA customer account or tenant in your product.
agent:code-reviewerA non-human actor such as an AI agent, service, or CI job.
group:platformA team or group.

Grant creation resolves the subject by id, email, or slug. If the user, customer, agent, or group does not exist yet, PackagePass creates it while creating the grant.

Grants and tokens

Know the difference between policy and credentials

Grants define what a subject may do. Tokens prove which subject is making the request and may also carry inline capabilities.

Grant

A stored permission record with subject, resource, actions, and optional conditions. Grants are best for durable customer, agent, user, and group access.

Token capability

A capability embedded in a token. Token capabilities are evaluated before grants and are useful for short-lived install or automation tokens scoped to one package or scope.

API token default

API tokens created without explicit resources receive broad tenant control-plane capabilities for publishing, grants, tokens, audit, deprecation, yanking, and dist-tags.

Browser-approved CLI token

Interactive browser login creates an API token named CLI login token. The intended default lifetime is 30 days; existing tokens keep their original expiry. Revoke it from Dashboard > Tokens when local CLI access is no longer needed.

Install token default

Install tokens are for registry access. Scope them to package install actions, keep them short-lived, and store them in package-manager secret environments.

Condition enforcement

The current policy engine enforces expiresAt. Fields such as versionRange, ipAllowlist, and environment can be stored on grants, but they are not authorization controls yet.

Common permission patterns

grant one customer package installs
packagepass grant add @acme/payments \
  --subject customer:globex \
  --action install

packagepass token create install \
  --package @acme/payments \
  --subject customer:globex \
  --ttl 30d
grant an agent publishing rights
await packagepass.grants.create({
  subject: { type: "agent", slug: "release-bot", kind: "service" },
  resource: { type: "scope", name: "@acme" },
  actions: ["package:publish"]
});
grant a version-specific install
await packagepass.grants.create({
  subject: { type: "agent", slug: "code-reviewer" },
  resource: {
    type: "package_version",
    name: "@acme/payments",
    version: "4.8.0"
  },
  actions: ["package:install"],
  conditions: {
    expiresAt: "2026-05-26T00:00:00.000Z"
  }
});
create a narrowly scoped install token
await packagepass.tokens.createInstallToken({
  name: "code reviewer payments install",
  subject: { type: "agent", slug: "code-reviewer" },
  resources: [{
    type: "package",
    name: "@acme/payments",
    actions: ["package:install"]
  }],
  expiresAt: new Date(Date.now() + 7 * 864e5).toISOString()
});

Debugging

Explain a decision before guessing

Use policy explain when a subject cannot install, publish, or manage a package. It returns allow, matchedGrants, and reason.

CLI
packagepass policy explain @acme/payments \
  --subject customer:globex \
  --action install
SDK
const decision = await packagepass.policy.explain({
  subject: { type: "customer", slug: "globex" },
  action: "package:install",
  resource: { type: "package", name: "@acme/payments" }
});

console.log(decision.allow);
console.log(decision.reason);
console.log(decision.matchedGrants);