Skip to content

Private npm Registry (CI/CD Setup)

The @forgejo-proxy/* packages are published to the private Verdaccio registry at https://npm.registry.hochguertel.work/. This guide explains the authentication architecture and how to configure CI/CD pipelines to publish and consume these packages.

┌─────────────────────────────────────────────────────────────────┐
│ npm.registry.hochguertel.work │
│ │
│ ┌─────────────────┐ ┌──────────────────────────────┐ │
│ │ Web UI │ │ npm CLI / CI API requests │ │
│ │ (browser) │ │ (/-/*, /pkg, PUT publish) │ │
│ └────────┬────────┘ └──────────────┬───────────────┘ │
│ │ │ │
│ Authelia OIDC SSO BYPASS Authelia │
│ (^/$ and ^/-/web) (all non-web paths in Traefik) │
│ │ │ │
│ └────────────┬───────────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Verdaccio │ │
│ │ (registry-npm)│ │
│ │ │ │
│ │ Auth methods: │ │
│ │ - OIDC (web) │ │
│ │ - htpasswd │ │
│ │ (CLI/CI) │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

Key insight: Traefik bypasses Authelia for all npm API paths (everything except the root / and /-/web/* paths). This means CI tools authenticate directly with Verdaccio’s htpasswd plugin — no SSO required.

A dedicated CI service account forgejo-ci is configured in both systems:

SystemAuth MethodGroup
Verdaccio htpasswdBasic Auth → JWT token$authenticated
Authelia users_database.ymlargon2id passworddevelopers

The developers group membership in Authelia is needed for web UI access (optional for CI). For CI/CD, only the Verdaccio htpasswd entry is required.

The @forgejo-proxy/* scope in Verdaccio config:

'@forgejo-proxy/*':
access: $all # Anyone can install (public read)
publish: $authenticated # Any authenticated user can publish
unpublish: $authenticated
# No proxy — local-only packages (not forwarded to npmjs)

This means:

  • Install: No auth required (anyone can npm install @forgejo-proxy/sdk)
  • Publish: Requires the Verdaccio JWT token (NPM_REGISTRY_TOKEN secret)

The following secrets are configured on repos that publish or consume @forgejo-proxy/* packages:

SecretValueRepos
NPM_REGISTRY_TOKENVerdaccio JWT (30d expiry)forgejo-proxy-api-spec, forgejo-proxy-cli
NPM_REGISTRY_URLhttps://npm.registry.hochguertel.work/Same repos

For publishing, add this to your CI workflow:

- name: Configure npm registry
run: |
echo "@forgejo-proxy:registry=https://npm.registry.hochguertel.work/" >> ~/.npmrc
echo "//npm.registry.hochguertel.work/:_authToken=${NPM_REGISTRY_TOKEN}" >> ~/.npmrc
env:
NPM_REGISTRY_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
name: Publish SDK
on:
push:
tags: ['v*']
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: '1.3.0'
- name: Install dependencies
run: bun install
- name: Configure npm registry
run: |
echo "@forgejo-proxy:registry=${{ secrets.NPM_REGISTRY_URL }}" >> ~/.npmrc
echo "//${NPM_REGISTRY_URL#https://}:_authToken=${{ secrets.NPM_REGISTRY_TOKEN }}" >> ~/.npmrc
env:
NPM_REGISTRY_URL: ${{ secrets.NPM_REGISTRY_URL }}
- name: Build
run: bun run build
- name: Publish
run: npm publish --access public
env:
NPM_REGISTRY_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}

Add .npmrc to your project root:

@forgejo-proxy:registry=https://npm.registry.hochguertel.work/

No token needed for installation — the @forgejo-proxy/* scope is publicly accessible.

The JWT token expires after 30 days. To renew:

  1. Get the credentials from Bitwarden: forgejo-ci npm registry (npm.registry.hochguertel.work)
  2. Request a new token:
    Terminal window
    NEW_TOKEN=$(curl -s -X PUT https://npm.registry.hochguertel.work/-/user/org.couchdb.user:forgejo-ci \
    -H "Content-Type: application/json" \
    -H "Authorization: Basic $(echo -n 'forgejo-ci:PASSWORD' | base64)" \
    -d '{"name":"forgejo-ci","password":"PASSWORD","type":"user"}' | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')
  3. Update the Forgejo secrets on affected repos
  4. Update the Bitwarden entry with the new token and expiry date
  • The forgejo-ci htpasswd password is stored in Bitwarden (vault: vault.hochguertel.work)
  • JWT tokens expire every 30 days (configured in Verdaccio security.api.jwt.sign.expiresIn)
  • The htpasswd file on the VPS: /opt/services/registries/npm/conf/htpasswd
  • Authelia config: /opt/services/authelia/users_database.yml
  • All npm API paths bypass Authelia — this is by design for CLI/CI tooling compatibility