Connecting the VPN should not mean editing npm's registry setting twice a day. A scope entry in a project-level configuration file can send company packages to one registry and everything else to the public one, with no switching and no override left behind on a Friday afternoon. The catch is that this solves naming only; routing, credentials and configuration precedence are three separate things that each fail in their own way.

Two names, two destinations

An npm package name either has a scope or it does not. A scoped name starts with an at-sign and a scope identifier, and that identifier is what the configuration can map. Nothing else about the package matters to the mapping. The registry never sees a request based on whether the package is internal or public; it only sees a name.

So the file below expresses two intentions in three lines: public packages go to the public registry, and packages under the company scope go to the company registry with a token for that host.

registry=https://registry.npmjs.org/
@acme:registry=https://packages.corp.example/
//packages.corp.example/:_authToken=${NPM_TOKEN}

Replace the scope, the host and the variable name with whatever the organisation actually provides. The structure is what matters, not the example values. Two lines of configuration carry the whole policy, which is why they are worth understanding rather than copying.

Keep the mapping and the network in separate compartments in your head. A scope entry cannot make an unreachable host reachable, and a working tunnel cannot tell npm which registry a package belongs to.

Write the file in the right order

Put the project file first in your reasoning even though npm reads several files. A .npmrc in the project directory is the one your colleagues inherit through the repository, so it should express the shared policy rather than your personal token. That file is also the one a reviewer will read. Keep it small enough to review in one glance.

Leave the default registry line pointing at the public registry unless the organisation has decided otherwise. Removing it makes the machine depend on whatever the user-level file says, which is exactly the kind of implicit dependency that produces different results on two laptops.

The scope line is not a wildcard. A name outside the scope keeps using the default, which is the behaviour you want for every dependency you did not write yourself.

Check what the file resolves to before you commit it, using a throwaway package name rather than a real install, so a mistake costs nothing.

Credentials belong to a host, not a project

Authentication entries are keyed to a registry host and, where the registry needs it, a path. That is why the token line starts with the doubled slash and the host rather than the scope. Once you see the host in that position, the rest of the line stops looking like arbitrary punctuation. It is an address, and the token is attached to it.

Using an environment variable keeps the literal token out of the file in the repository. It does not make the token invisible everywhere; it still appears in the process environment, and a command that prints configuration can reveal it.

Provision the variable through whatever local or CI credential mechanism the organisation approves, and never paste a real token into an example that might be copied into documentation.

If two registries are in play, each one gets its own entry. A single unqualified token setting is at best ambiguous and at worst sent to the wrong host.

When the wrong registry wins

Configuration is layered, and the value in effect is not necessarily the value in the file you just edited. Project settings, user settings, global settings and environment variables all participate. That layering is why a fix works on your machine and not in a colleague's container. It is also why the effective value is worth printing before you argue about it.

An environment variable is the usual culprit in CI, where a job sets a registry once and every later step inherits it. The second most common is a user-level file on a developer's machine that overrides what the repository says.

Print the effective configuration and the resolved registry for one scoped and one unscoped name before debugging anything else. If those two answers are what you expect, the problem is the network; if they are not, the problem is configuration and no amount of VPN work will help.

Checklist before you commit

Confirm the scope name matches the packages actually published, that the default registry is explicit, and that the token line names the correct host. A typo in the scope produces the strangest failure of all, because the package resolves from the public registry and returns someone else's module with the same name. Check the name before you debug the token.

Confirm the private registry is reachable from the machine in the context that runs the install, not merely from your browser. That distinction matters when a proxy or a container has different routing from your shell.

Then confirm behaviour on a clean checkout with no cached packages. A fresh install is the only test that shows what a new colleague will experience.

ဆက်စပ်ဆောင်းပါးများ