Document npm registry endpoints and auth gotchas
All checks were successful
continuous-integration/drone/push Build is passing

Add the exact pip/npm registry endpoints and secret names, the
scope-scoped (not global) registry requirement given Verdaccio's missing
uplink, the host-scoped _auth rule, the per-item Drone shell caveat, and
the Verdaccio storage uid/permissions gotcha behind the confusing
"404 no such package available" error on publish.
This commit is contained in:
2026-10-21 01:31:07 +05:30
parent da1e46a921
commit 322741e00c
7 changed files with 161 additions and 7 deletions

View File

@@ -72,8 +72,13 @@ pipelines**:
the disaster-recovery / replay path.
Secrets: `NPM_REPO_URL`, `NPM_USERNAME`, `NPM_PASSWORD`. Auth is a generated
`.npmrc` (`registry=` + `_auth=` base64 of `user:pass`) written from secrets at
runtime — the committed `.npmrc` in a repo only points at the registry URL.
`.npmrc` written from secrets at runtime. It sets a **scope-scoped** registry,
never a global one — `@aetoskia:registry=$NPM_REPO_URL` — plus a host-scoped
auth line `//<host>/:_auth=<base64(user:pass)>`. The scope and host are derived
from `package.json` and `NPM_REPO_URL` respectively. A global `registry=` would
push public transitive deps through Verdaccio (which has no uplink) and 404
`npm install`; a bare `_auth=` is rejected by modern npm. See
[93 – Library Registry](93_library_registry.md).
- **Ship as-is.** Name and version come from `package.json`, so the same file
is reused verbatim across every npm package.

View File

@@ -64,6 +64,56 @@ so the CI credentials (`PIP_USERNAME` / `PIP_PASSWORD`, `NPM_USERNAME` /
Verdaccio requires **bcrypt** htpasswd entries; see
[90 – Drone Pipelines](90_drone_pipelines.md) for the CI wiring.
### Endpoints
| Registry | URL | Backend | CI secret |
|---|---|---|---|
| PyPI | `https://pip.aetoskia.com` | pypiserver (`server-pi:6007`) | `PIP_REPO_URL` |
| npm | `https://npm.aetoskia.com` | Verdaccio (`server-pi:6008`) | `NPM_REPO_URL` |
Both are exposed through Traefik on the `websecure` entrypoint as **bare
origins** — no path, no trailing slash. Set the secret values exactly as above.
### npm: scope the registry, never set it globally
Verdaccio here has **no uplink / proxy**, so it can only serve `@aetoskia/*`
packages. A global `registry=https://npm.aetoskia.com` in `.npmrc` would route
*every* dependency — including public transitive deps such as
`zod-to-json-schema` (pulled in by `@modelcontextprotocol/sdk`) — through
Verdaccio, which 404s on anything it does not host. Always scope the registry to
the account:
```
@aetoskia:registry=https://npm.aetoskia.com
//npm.aetoskia.com/:_auth=<base64(user:password)>
```
Public deps then resolve from `registry.npmjs.org` and only `@aetoskia/*` hits
the private registry. The auth line must be **scoped to the host**
(`//host/:_auth=`), not a bare `_auth=` — modern npm rejects the bare form with
`ERR_INVALID_AUTH`. The drone template derives both from the package scope and
`NPM_REPO_URL` automatically; see
[90 – Drone Pipelines](90_drone_pipelines.md).
> The Drone pipeline runs each `commands:` list item in a **separate shell**, so
> shell variables defined in one item do not survive into the next. Keep the
> variable definitions and the commands that use them in the same `|` block.
### Verdaccio storage permissions
Verdaccio runs as uid **10001** (`verdaccio`) inside `verdaccio/verdaccio`.
The `storage` directory is bind-mounted from the server
(`/mnt/omnissiah-vault/data/verdaccio/storage`); if it is owned by `root:root`
(mode `755`) the process cannot write to it and every publish fails with a
confusing `404 no such package available` (`NotFoundError` from
`local-storage`). Ensure the directory — and its `plugins` subdirectory — are
owned by `10001:101`:
```sh
sudo chown -R 10001:101 /mnt/omnissiah-vault/data/verdaccio/storage
docker restart npm-registry
```
## Notes
- **Hexa** is a full library with its own MCP server on `:8008`: read