Safeguard
Developer Tools•

How do Verdaccio package access rules work: all, authenticated, anonymous?

Verdaccio matches package patterns top to bottom, first match wins. What access, publish, proxy, all, authenticated and anonymous mean, and the pitfalls.

Safeguard Security Team
3 min read

In Verdaccio, the packages section sets who can read, publish and unpublish each package pattern. Patterns are matched from top to bottom and the first match wins. The special values $all, $authenticated and $anonymous mean everyone, any logged in user, and visitors who are not logged in.

The four keys

From the Verdaccio package access documentation:

  • access controls who can read, meaning install, the package.
  • publish controls who can publish new versions.
  • unpublish controls who can remove versions. If you omit it, it falls back to publish.
  • proxy names the upstream registry the package is fetched from, such as an uplink called npmjs.

The special values

ValueWho it matches
$allEveryone, including visitors who are not logged in
$authenticatedAny logged in user
$anonymousVisitors who are not logged in, and not logged in users

You can also list user or group names, separated by spaces. A user needs to belong to at least one of them.

First match wins

Order is the most common source of mistakes. Patterns such as 'my-company-*', '@scope/*' and '**' are checked top to bottom, and the first match decides. Put specific patterns first and keep '**' at the end as a catch all. If a broad pattern sits above a narrow one, the narrow rule never applies.

Recipes from the docs

packages:
  'my-company-*':
    access: admin teamA teamB
    publish: admin
  '**':
    access: $all
    publish: $authenticated
    proxy: npmjs
  • Default: '**' with access: $all and publish: $authenticated.
  • Fully private: set both access and publish to $authenticated.
  • Team only reads: list the team names under access.
  • Read only mirror: access: $all and a proxy, with no publish value. An empty publish list matches nobody.

In the docs' worked example, a user in a group that is not listed gets a 403 Forbidden when installing a matching package, and the package does not appear in the web interface for that user.

Pitfalls to check

  1. access: $all on a pattern that covers private packages makes them public to anyone who can reach the server.
  2. publish: $authenticated lets every logged in user publish under that pattern. Inside a company that may be too many people. Name a group instead.
  3. A broad '**' rule with a proxy means any name you do not own is fetched from the public registry. That is how a missing internal package name can resolve to a public one. Cover your internal scope names with explicit rules that have no proxy.
  4. Forgetting unpublish. It inherits from publish, so everyone who can publish can remove versions.

Test it

Write the rules, then check them as three people: an anonymous visitor, a logged in user outside the team, and a team member. Try installing, publishing and unpublishing as each, and confirm the results match your intent.

For a view of which packages actually reach your builds, Safeguard's SBOM and dependency scanning is useful, and the free scan is at gold.safeguard.sh/scan.

Quick check

What is the difference between $all and $authenticated?

$all includes visitors who are not logged in. $authenticated means any logged in user.

Why did my narrow rule never apply?

A broader pattern above it matched first. Verdaccio uses the first matching pattern, so specific ones must come first.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.