Authentication
NORA supports htpasswd-based authentication with API tokens for programmatic access. Authentication is disabled by default and must be explicitly enabled.
Enabling Authentication
Section titled “Enabling Authentication”Set the NORA_AUTH_ENABLED environment variable or configure it in config.toml:
# Environment variableexport NORA_AUTH_ENABLED=true[auth]enabled = truehtpasswd_file = "users.htpasswd"token_storage = "data/tokens"htpasswd Setup
Section titled “htpasswd Setup”NORA uses Apache-compatible htpasswd files for user management. Create a password file using htpasswd (from apache2-utils) or any compatible tool:
Creating the htpasswd file
Section titled “Creating the htpasswd file”# Install htpasswd (Debian/Ubuntu)apt-get install apache2-utils
# Create file with first userhtpasswd -Bc users.htpasswd admin
# Add additional usershtpasswd -B users.htpasswd developerhtpasswd -B users.htpasswd ci-botThe -B flag uses bcrypt hashing, which is the recommended algorithm.
Mount the file
Section titled “Mount the file”Docker:
docker run -d \ --name nora \ -p 4000:4000 \ -v /data/nora:/data \ -v /etc/nora/users.htpasswd:/app/users.htpasswd:ro \ -e NORA_AUTH_ENABLED=true \ -e NORA_AUTH_HTPASSWD_FILE=/app/users.htpasswd \ ghcr.io/getnora-io/nora:latestKubernetes:
apiVersion: v1kind: Secretmetadata: name: nora-htpasswdtype: OpaquestringData: users.htpasswd: | admin:$2y$05$... ci-bot:$2y$05$...---apiVersion: apps/v1kind: Deploymentmetadata: name: noraspec: template: spec: containers: - name: nora env: - name: NORA_AUTH_ENABLED value: "true" - name: NORA_AUTH_HTPASSWD_FILE value: /etc/nora/users.htpasswd volumeMounts: - name: htpasswd mountPath: /etc/nora readOnly: true volumes: - name: htpasswd secret: secretName: nora-htpasswdAnonymous Read Mode
Section titled “Anonymous Read Mode”When NORA_AUTH_ANONYMOUS_READ=true, unauthenticated users can pull/download artifacts, but authentication is still required for push/upload operations.
export NORA_AUTH_ENABLED=trueexport NORA_AUTH_ANONYMOUS_READ=true[auth]enabled = trueanonymous_read = trueThis is useful for organizations that want open read access (e.g., shared libraries) while restricting who can publish artifacts.
| Operation | Anonymous Read = false | Anonymous Read = true |
|---|---|---|
| Pull / Download | Auth required | No auth needed |
| Push / Upload | Auth required | Auth required |
| Delete / Admin | Auth required | Auth required |
API Tokens
Section titled “API Tokens”API tokens provide programmatic access without exposing htpasswd credentials. Tokens are prefixed with nra_ for easy identification and use Argon2 hashing.
Token Roles
Section titled “Token Roles”| Role | Permissions |
|---|---|
read | Pull and download artifacts only |
write | Pull, push, and download artifacts |
admin | Full access including token management |
Creating a Token
Section titled “Creating a Token”curl -X POST http://localhost:4000/api/tokens \ -H "Content-Type: application/json" \ -d '{ "username": "admin", "password": "your-password", "role": "write", "ttl_days": 90, "description": "CI/CD pipeline token" }'Response:
{ "token": "nra_a1b2c3d4e5f6...", "expires_in_days": 90}Save the token value immediately — it is only shown once at creation time.
Listing Tokens
Section titled “Listing Tokens”curl -X POST http://localhost:4000/api/tokens/list \ -H "Content-Type: application/json" \ -d '{ "username": "admin", "password": "your-password" }'Response:
{ "tokens": [ { "hash_prefix": "a1b2c3", "created_at": 1714200000, "expires_at": 1721976000, "last_used": 1714300000, "description": "CI/CD pipeline token", "role": "write" } ]}Revoking a Token
Section titled “Revoking a Token”Use the hash_prefix from the list response:
curl -X POST http://localhost:4000/api/tokens/revoke \ -H "Content-Type: application/json" \ -d '{ "username": "admin", "password": "your-password", "hash_prefix": "a1b2c3" }'Docker Login
Section titled “Docker Login”NORA supports standard Docker authentication. When auth is enabled, use docker login before push/pull operations:
# Login with htpasswd credentialsdocker login localhost:4000# Username: admin# Password: ****
# Login with API token (use token as password, any username)docker login localhost:4000 -u token -p nra_a1b2c3d4e5f6...For automated workflows, use --password-stdin:
echo "nra_a1b2c3d4e5f6..." | docker login localhost:4000 -u token --password-stdinCI/CD Integration
Section titled “CI/CD Integration”GitHub Actions
Section titled “GitHub Actions”name: Build and Pushon: push: branches: [main]
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Login to NORA run: | echo "${{ secrets.NORA_TOKEN }}" | \ docker login registry.example.com -u token --password-stdin
- name: Build and Push run: | docker build -t registry.example.com/myapp:${{ github.sha }} . docker push registry.example.com/myapp:${{ github.sha }}For non-Docker registries (npm, PyPI, Cargo, etc.), use the token in the appropriate client configuration:
# npm - name: Publish npm package env: NORA_TOKEN: ${{ secrets.NORA_TOKEN }} run: | echo "//registry.example.com/:_authToken=${NORA_TOKEN}" > .npmrc npm publish --registry=https://registry.example.com
# PyPI (twine) - name: Publish Python package env: NORA_TOKEN: ${{ secrets.NORA_TOKEN }} run: | twine upload --repository-url https://registry.example.com/pypi/ \ -u token -p "${NORA_TOKEN}" dist/*GitLab CI
Section titled “GitLab CI”stages: - build - publish
variables: NORA_REGISTRY: registry.example.com
build: stage: build image: docker:latest services: - docker:dind before_script: - echo "$NORA_TOKEN" | docker login $NORA_REGISTRY -u token --password-stdin script: - docker build -t $NORA_REGISTRY/myapp:$CI_COMMIT_SHA . - docker push $NORA_REGISTRY/myapp:$CI_COMMIT_SHA
publish-maven: stage: publish image: maven:3.9 script: - > mvn deploy -DaltDeploymentRepository=nora::https://${NORA_REGISTRY}/maven2 -Dserver.username=token -Dserver.password=${NORA_TOKEN}Store NORA_TOKEN as a masked CI/CD variable in GitLab project settings.
Token Security Best Practices
Section titled “Token Security Best Practices”- Use scoped tokens. Create
readtokens for pull-only workloads andwritetokens only for pipelines that publish. - Set TTL. Always specify
ttl_dayswhen creating tokens. Rotate tokens regularly. - Do not commit tokens. Use CI/CD secrets (GitHub Secrets, GitLab CI Variables) to inject tokens at runtime.
- Revoke on compromise. If a token is leaked, revoke it immediately using the API.
- Use anonymous read when possible. If your artifacts are not sensitive, enable
NORA_AUTH_ANONYMOUS_READ=trueto reduce token management overhead.
See Also
Section titled “See Also”- Configuration Reference — all environment variables
- Curation — package access control
- Production Deployment — TLS and proxy setup