pkg.hanzo.ai serves packages; oci.hanzo.ai serves images and charts. Both sit on the same hanzoai/s3 object store.
3.9 KiB
hanzoai/pkg — pkg.hanzo.ai, the self-hosted npm registry
verdaccio 5.33.0 + verdaccio-aws-s3-storage 10.3.3, the npm twin of
hanzoai/registry (oci.hanzo.ai, containers). Same estate pattern:
stock battle-tested server, config baked into the image, storage on
hanzoai/s3, secrets from KMS.
Shape
Dockerfile— verdaccio + the S3 store plugin under/verdaccio/plugins.conf/config.yaml— the whole registry policy:- store: bucket
pkgonhttp://s3.hanzo.svc:9000(path-style). Zero PVC. - uplink
npmjs-> registry.npmjs.org, 30m metadata cache; installs of anything not published here fall through and the tarball is cached in S3 (supply-chain buffer). - auth: bcrypt htpasswd at
/verdaccio/auth/htpasswd,max_users: -1(no self-registration; the file is KMS-synced, KMS path/pkg, orghanzo, envprod, keyshtpasswd+publish-password). - publish requires auth; reads are open.
- store: bucket
Branding — both marks are ABSOLUTE PATHS, and neither lives in the theme
web.logo / web.logoDark / web.favicon name files under /verdaccio/web/.
Copying an asset into the theme's static/ dir and naming it by basename looks
right and does nothing, in two different ways:
- logo: verdaccio runs
path.posix.resolve(logo)against the process CWD (/opt/verdaccio). A basename resolves to a file that is not there, and an unreadable logo is dropped toundefined— the page renderslogo:"", no mark, and logs nothing. Given a path that exists it reads the file itself and republishes it at/-/static/<basename>. - favicon:
app.get('/-/static/favicon.ico', serveFavicon(config))is registered on the app ahead of the theme router, so the theme dir is never consulted. It streamsweb.favicon, else the stock icon inside verdaccio.
Check branding by reading window.__VERDACCIO_BASENAME_UI_OPTIONS on / —
logo must be a URL, primaryColor #000000 (vendor default is #4b5e40).
/-/static/* sits behind Cloudflare, so purge that URL after a change or the
edge keeps serving the old icon against a correct origin.
Deploy
Fleet app hanzo-pkg: universe/charts/app/values/hanzo/pkg.yaml.
.hanzo/workflows/deploy.ymlis the build lane — the forge reads.hanzo/workflows/, and GitHub Actions is off account-wide, so.github/workflows/builds nothing. A push tomainderives the next patch from the tags published in GHCR (authenticated:ghcr.io/hanzoai/pkgis private, and an anonymous read returns an empty list that reads like a fresh repository), proves the tag is unused, builds linux/amd64, proves the pushed image resolves, then callsuniverse/charts/app/pin.sh pkg <version>so cd.hanzo.ai reconciles. Tags carry a leadingv; pin.sh takes the bare version and picks the prefix up from the values file.- The forge's Actions unit must stay enabled on the repo. With it off a workflow file is inert — no run is created and nothing reports a failure.
- env:
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYfrom the existings3-credentialssecret;VERDACCIO_PUBLIC_URL=https://pkg.hanzo.ai. - ingress: host
pkg.hanzo.ai(hanzoai/ingress class, cert-manager letsencrypt-prod, existing secretpkg-hanzo-ai-tls), path-split:/v1/packagesstays onhanzo-git(the forge's 25-ecosystem package surface),/is the npm protocol -> servicepkg:4873.
Publish
max_users: -1 also closes the interactive npm login endpoint (verdaccio
routes login through adduser), so auth is Basic _auth in .npmrc:
registry=https://pkg.hanzo.ai/
//pkg.hanzo.ai/:_auth=<base64 of "hanzo:<KMS hanzo/prod /pkg/publish-password>">
Verified end to end: npm ping PONG, npm whoami -> hanzo,
@hanzo/[email protected] published + installed back, @hanzo/[email protected] installed
THROUGH the uplink with its tarball cached into the S3 bucket.
Repo .npmrc cutover is a separate decision — nothing points here yet.