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:
accesscontrols who can read, meaning install, the package.publishcontrols who can publish new versions.unpublishcontrols who can remove versions. If you omit it, it falls back topublish.proxynames the upstream registry the package is fetched from, such as an uplink callednpmjs.
The special values
| Value | Who it matches |
|---|---|
$all | Everyone, including visitors who are not logged in |
$authenticated | Any logged in user |
$anonymous | Visitors 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:
'**'withaccess: $allandpublish: $authenticated. - Fully private: set both
accessandpublishto$authenticated. - Team only reads: list the team names under
access. - Read only mirror:
access: $alland aproxy, with nopublishvalue. 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
access: $allon a pattern that covers private packages makes them public to anyone who can reach the server.publish: $authenticatedlets every logged in user publish under that pattern. Inside a company that may be too many people. Name a group instead.- A broad
'**'rule with aproxymeans 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 noproxy. - Forgetting
unpublish. It inherits frompublish, 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.