Document npm registry endpoints and auth gotchas
All checks were successful
continuous-integration/drone/push Build is passing
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:
@@ -753,8 +753,13 @@ pipelines</strong>:</p>
|
||||
the disaster-recovery / replay path.</li>
|
||||
</ol>
|
||||
<p>Secrets: <code>NPM_REPO_URL</code>, <code>NPM_USERNAME</code>, <code>NPM_PASSWORD</code>. Auth is a generated
|
||||
<code>.npmrc</code> (<code>registry=</code> + <code>_auth=</code> base64 of <code>user:pass</code>) written from secrets at
|
||||
runtime — the committed <code>.npmrc</code> in a repo only points at the registry URL.</p>
|
||||
<code>.npmrc</code> written from secrets at runtime. It sets a <strong>scope-scoped</strong> registry,
|
||||
never a global one — <code>@aetoskia:registry=$NPM_REPO_URL</code> — plus a host-scoped
|
||||
auth line <code>//<host>/:_auth=<base64(user:pass)></code>. The scope and host are derived
|
||||
from <code>package.json</code> and <code>NPM_REPO_URL</code> respectively. A global <code>registry=</code> would
|
||||
push public transitive deps through Verdaccio (which has no uplink) and 404
|
||||
<code>npm install</code>; a bare <code>_auth=</code> is rejected by modern npm. See
|
||||
<a href="../93_library_registry/">93 – Library Registry</a>.</p>
|
||||
<ul>
|
||||
<li><strong>Ship as-is.</strong> Name and version come from <code>package.json</code>, so the same file
|
||||
is reused verbatim across every npm package.</li>
|
||||
|
||||
@@ -635,6 +635,39 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<nav class="md-nav" aria-label="Publishing & registry auth">
|
||||
<ul class="md-nav__list">
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#endpoints" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
Endpoints
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#npm-scope-the-registry-never-set-it-globally" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
npm: scope the registry, never set it globally
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#verdaccio-storage-permissions" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
Verdaccio storage permissions
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
@@ -839,6 +872,67 @@ so the CI credentials (<code>PIP_USERNAME</code> / <code>PIP_PASSWORD</code>, <c
|
||||
<code>NPM_PASSWORD</code>) are one and the same account — there is no separate npm user.
|
||||
Verdaccio requires <strong>bcrypt</strong> htpasswd entries; see
|
||||
<a href="../90_drone_pipelines/">90 – Drone Pipelines</a> for the CI wiring.</p>
|
||||
<h3 id="endpoints">Endpoints</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Registry</th>
|
||||
<th>URL</th>
|
||||
<th>Backend</th>
|
||||
<th>CI secret</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>PyPI</td>
|
||||
<td><code>https://pip.aetoskia.com</code></td>
|
||||
<td>pypiserver (<code>server-pi:6007</code>)</td>
|
||||
<td><code>PIP_REPO_URL</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>npm</td>
|
||||
<td><code>https://npm.aetoskia.com</code></td>
|
||||
<td>Verdaccio (<code>server-pi:6008</code>)</td>
|
||||
<td><code>NPM_REPO_URL</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Both are exposed through Traefik on the <code>websecure</code> entrypoint as <strong>bare
|
||||
origins</strong> — no path, no trailing slash. Set the secret values exactly as above.</p>
|
||||
<h3 id="npm-scope-the-registry-never-set-it-globally">npm: scope the registry, never set it globally</h3>
|
||||
<p>Verdaccio here has <strong>no uplink / proxy</strong>, so it can only serve <code>@aetoskia/*</code>
|
||||
packages. A global <code>registry=https://npm.aetoskia.com</code> in <code>.npmrc</code> would route
|
||||
<em>every</em> dependency — including public transitive deps such as
|
||||
<code>zod-to-json-schema</code> (pulled in by <code>@modelcontextprotocol/sdk</code>) — through
|
||||
Verdaccio, which 404s on anything it does not host. Always scope the registry to
|
||||
the account:</p>
|
||||
<div class="language-text highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-1">1</a></span>
|
||||
<span class="normal"><a href="#__codelineno-0-2">2</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1"></a>@aetoskia:registry=https://npm.aetoskia.com
|
||||
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2"></a>//npm.aetoskia.com/:_auth=<base64(user:password)>
|
||||
</span></code></pre></div></td></tr></table></div>
|
||||
<p>Public deps then resolve from <code>registry.npmjs.org</code> and only <code>@aetoskia/*</code> hits
|
||||
the private registry. The auth line must be <strong>scoped to the host</strong>
|
||||
(<code>//host/:_auth=</code>), not a bare <code>_auth=</code> — modern npm rejects the bare form with
|
||||
<code>ERR_INVALID_AUTH</code>. The drone template derives both from the package scope and
|
||||
<code>NPM_REPO_URL</code> automatically; see
|
||||
<a href="../90_drone_pipelines/">90 – Drone Pipelines</a>.</p>
|
||||
<blockquote>
|
||||
<p>The Drone pipeline runs each <code>commands:</code> list item in a <strong>separate shell</strong>, 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 <code>|</code> block.</p>
|
||||
</blockquote>
|
||||
<h3 id="verdaccio-storage-permissions">Verdaccio storage permissions</h3>
|
||||
<p>Verdaccio runs as uid <strong>10001</strong> (<code>verdaccio</code>) inside <code>verdaccio/verdaccio</code>.
|
||||
The <code>storage</code> directory is bind-mounted from the server
|
||||
(<code>/mnt/omnissiah-vault/data/verdaccio/storage</code>); if it is owned by <code>root:root</code>
|
||||
(mode <code>755</code>) the process cannot write to it and every publish fails with a
|
||||
confusing <code>404 no such package available</code> (<code>NotFoundError</code> from
|
||||
<code>local-storage</code>). Ensure the directory — and its <code>plugins</code> subdirectory — are
|
||||
owned by <code>10001:101</code>:</p>
|
||||
<div class="language-sh highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-1-1">1</a></span>
|
||||
<span class="normal"><a href="#__codelineno-1-2">2</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1"></a>sudo<span class="w"> </span>chown<span class="w"> </span>-R<span class="w"> </span><span class="m">10001</span>:101<span class="w"> </span>/mnt/omnissiah-vault/data/verdaccio/storage
|
||||
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2"></a>docker<span class="w"> </span>restart<span class="w"> </span>npm-registry
|
||||
</span></code></pre></div></td></tr></table></div>
|
||||
<h2 id="notes">Notes</h2>
|
||||
<ul>
|
||||
<li><strong>Hexa</strong> is a full library with its own MCP server on <code>:8008</code>: read
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user