Guides, API reference, and versioned release history.
Nexspence Documentation
Guides, configuration reference, and versioned changelog for Nexspence — the free, open-source universal artifact repository manager and drop-in alternative to commercial artifact repository managers.
Nexspence manages Maven, npm, PyPI, Docker, Go modules, NuGet, Cargo, Helm, Apt, Yum/RPM, Conan, Raw, Conda and Terraform artifacts from a single self-hosted registry. Repositories can be Hosted (your published artifacts), Proxy (cached remote registries) or Group (multiple repositories under one URL).
This documentation covers Quick Start, Installation, Repositories, per-format guides, Browse & Search, Users, Roles & Privileges (RBAC), Cleanup policies, Webhooks, Migration from Nexus, Monitoring, Build Promotion, Replication, and the full REST API reference.
Get started in under two minutes with Docker Compose: download the latest release, set a strong admin_password and jwt_secret in config.yaml, then run docker compose up -d and open the UI at http://localhost:8081.
Quick Start
Run Nexspence locally in under 2 minutes using Docker Compose.
01
Download the latest release
Go to GitHub Releases, download the Source code (zip) archive and unpack it.
02
Configure
Copy config.yaml.example → config.yaml and set a strong admin_password and jwt_secret.
03
Start the stack
docker compose up -d — waits for PostgreSQL, runs migrations, serves the UI at http://localhost:8081.
shell
# Unpack the release zip
unzip nexspence-*.zip && cd nexspence-*
# Config
cp config.yaml.example config.yaml
# Edit config.yaml: set admin_password + jwt_secret# Launch
docker compose up -d
# Open UI
open http://localhost:8081 # default login: admin / admin123
Note: Since v1.18.0 the server refuses to start with the shipped defaults (admin123 / the example jwt_secret) unless auth.allow_insecure_defaults: true is set. The dev compose sets this so the quick-start boots; for production set a real admin_password and jwt_secret (e.g. via NEXSPENCE_AUTH_JWT_SECRET) instead.
Repositories
Nexspence organises artifacts into repositories. Each repository has a type (Hosted, Proxy, or Group) and a format (Maven, Docker, npm, etc.).
Repositories view — format badges, type filters, and per-repo storage usage.
Repository Types
H
Hosted
Stores artifacts you publish directly. Use for internal builds, release binaries, private packages. Supports upload via CI/CD pipelines.
P
Proxy
Caches artifacts from a remote registry (Maven Central, npmjs, PyPI, Docker Hub, etc.). First request fetches from upstream; subsequent requests serve from cache. Reduces bandwidth and provides availability even if upstream is down.
G
Group
Aggregates multiple repositories (hosted + proxy) under one URL. Clients use a single endpoint; Nexspence searches members in order and returns the first match. Ideal for giving developers one URL for all dependencies.
Creating a Repository
01
Open the Repositories page
Navigate to Repositories in the sidebar. Click the + Create button in the top-right corner.
02
Choose type and format
Select the repository type (Hosted / Proxy / Group) and format (Maven, npm, Docker, etc.) in the wizard. The name must be unique and URL-safe.
03
Configure format-specific settings
For Proxy: enter the upstream Remote URL (e.g. https://registry.npmjs.org). For Group: select member repositories in order. For all types: optionally assign a blob store and cleanup policy.
04
Use the repository URL
After creation the URL is shown on the repository card: http://localhost:8081/repository/{name}/. Point your build tool at this URL.
Repository URL Patterns
Format
URL pattern
Notes
Maven
/repository/{name}/
Use as <url> in pom.xml or settings.xml
npm
/repository/{name}/
Use as registry in .npmrc
PyPI
/repository/{name}/simple/
Use as index-url in pip.conf
Docker
localhost:5000/{name}
Registry port 5000, image name includes repo
Helm
/repository/{name}/
Add with helm repo add
Go
/repository/{name}/
Set as GOPROXY
NuGet
/repository/{name}/index.json
v3 flat container endpoint
Cargo
/repository/{name}/
Sparse index endpoint
Raw
/repository/{name}/
PUT/GET any path
Anonymous Access
Enable Allow anonymous access on a repository to let unauthenticated users download artifacts. Write operations always require authentication. Configure globally in Admin → Security → Anonymous Access and per-repository in the repo settings.
Format Setup Guides
How to configure your build tools and package managers to use Nexspence repositories. Replace localhost:8081 with your server URL and {repo} with your repository name.
Maven / Gradle
Add Nexspence as a mirror in ~/.m2/settings.xml to route all dependency downloads through your proxy repository.
# Set GOPROXY
go env -w GOPROXY=http://localhost:8081/repository/go-proxy/|direct
go env -w GONOSUMCHECK=localhost:8081
# Use in project
go get github.com/some/module@v1.2.3
Find, inspect, and download artifacts stored in Nexspence repositories.
Browse
01
Open Browse
Click Browse in the sidebar. Select a repository from the dropdown on the left.
02
Navigate the tree
For most formats a file tree is shown. For Docker repositories a two-column view shows image names on the left and tags on the right.
03
Inspect a component
Click any file or component to open a detail panel with metadata (size, SHA-256, upload date), download link, and usage examples for common tools.
Browse — file tree on the left, component metadata and download panel on the right.
Search
01
Open Search
Click Search in the sidebar. Enter a keyword in the search box — matches component names, group IDs, and artifact IDs.
02
Filter by format or repository
Use the Format and Repository dropdowns to narrow results. You can also filter by tag.
03
Use the API
Search is also available via the REST API for CI/CD integration.
Search — keyword results with format badges, repository filter, and tag chips.
curl — search API
# Search by keyword
curl -u admin:admin123 \
"http://localhost:8081/service/rest/v1/search?q=mylib"
# Filter by format and repository
curl -u admin:admin123 \
"http://localhost:8081/service/rest/v1/search?format=maven2&repository=maven-releases&q=spring"
# List all components in a repository
curl -u admin:admin123 \
"http://localhost:8081/service/rest/v1/components?repository=npm-hosted"
Users & API Tokens
Manage user accounts, passwords, and programmatic API tokens for CI/CD pipelines.
User Management
01
Create a user
Go to Admin → Security → Users. Click + Create User. Fill in username, email, first/last name, and initial password. Assign roles immediately or later.
02
Assign roles
Open a user and click Assign Roles. Select roles from the transfer list. The built-in nx-admin role grants full access; custom roles control per-repository access via privileges.
03
LDAP / Active Directory
Configure LDAP in config.yaml under the ldap section. Users are provisioned on first login (JIT). Groups map to Nexspence roles via role_mappings.
Open your profile (click your username in the bottom-left). Go to API Tokens tab. Click Generate Token. Set an optional expiry. The token is shown once — copy it immediately.
02
Use the token
Tokens start with nxs_. Use as Bearer token or as the password in HTTP Basic auth (username = your username, password = token).
Token expiry: Maximum lifetime is controlled by auth.token_max_days in config.yaml (default 180 days). Tokens are hashed (SHA-256) — the plaintext is never stored.
Roles & Privileges
Nexspence uses a three-layer access control model: Content Selectors → Privileges → Roles → Users.
Security → Roles — each role lists its granted privileges as chips.
Access Control Model
1
Content Selector
A CEL expression that defines which artifacts the rule applies to. Variables available: format, path, repository. Example: format == "maven2" && path.startsWith("/com/example/")
2
Privilege
Binds a Content Selector to a permission. In Nexspence all user-created privileges are of type repository-content-selector. Create in Admin → Security → Privileges.
3
Role
A named set of privileges. Assign roles to users. The built-in nx-admin role grants full access to everything.
CEL Expression Examples
What it matches
CEL expression
All Maven artifacts
format == "maven2"
Specific Maven group
format == "maven2" && path.startsWith("/com/example/")
All Docker images
format == "docker"
Specific repository
repository == "npm-releases"
All npm packages
format == "npm"
PyPI in specific repo
format == "pypi" && repository == "pypi-releases"
Exclude snapshots
format == "maven2" && !path.contains("SNAPSHOT")
Setting Up Access Control
01
Create a Content Selector
Admin → Security → Content Selectors → + Create. Enter a name and a CEL expression. The preview shows matching artifacts.
02
Create a Privilege
Admin → Security → Privileges → + Create. Select the Content Selector you just created.
03
Create a Role
Admin → Security → Roles → + Create. Add the privilege to the role.
04
Assign the Role to a User
Admin → Security → Users. Open the user, click Assign Roles, add the role.
Cleanup Policies
Automatically remove old or unused artifacts to reclaim storage space.
Cleanup Policies — policy cards with the create wizard (criteria step) open.
Cleanup Criteria
Criterion
Description
Published before
Remove artifacts published more than N days ago.
Last downloaded before
Remove artifacts not downloaded in the last N days.
Retain N versions
Keep only the N newest versions of each component. All older versions are eligible for removal.
01
Create a policy
Go to Admin → Cleanup Policies. Click + Create. Choose format scope (* for all formats or a specific one), set criteria, and optionally set a cron schedule.
02
Attach to repositories
Open a repository settings (gear icon) and select one or more cleanup policies. Only artifacts in attached repositories are affected.
03
Run now or wait for schedule
On the policy card click Run Now to trigger an immediate dry-run (preview) or full run. The default global cron is 0 2 * * * (2 AM daily).
Dry run: The first run shows what would be deleted without actually removing anything. Review the preview before enabling full deletion.
Blob Garbage Collection
Cleanup policies delete asset records. Blob GC then reclaims the underlying blobs that are no longer referenced by any asset — orphans left behind by deletions, failed uploads, or migrations.
Admin → Blob Stores. Open a store to reach the Garbage collection panel (Dry run / Compact).
01
Grace period
Only orphans older than gc.min_age (default 24h) are collected, so an in-flight upload whose asset row is not yet committed is never removed.
02
Run on demand
Open Admin → Blob Stores, click a store, then use Dry run to preview orphans or Compact to reclaim them. A scheduled run (gc.schedule, weekly by default) sweeps every store automatically.
API & CLI
# Preview orphans (dry run) via REST
curl -u admin:admin123 -X POST \
"http://localhost:8081/api/v1/blobstores/default/compact?dry_run=true"
# Reclaim now with the nxs CLI
nxs blobstore compact default --dry-run
nxs blobstore compact default
Webhooks
Receive HTTP POST callbacks when repository events occur. Use for CI/CD triggers, Slack notifications, audit systems, and more.
Events
Event
When
artifact.published
Artifact pushed to hosted or cached by proxy
artifact.deleted
Artifact deleted
repo.created
Repository created
repo.updated
Repository configuration updated
repo.deleted
Repository deleted
proxy.error
Proxy failed to fetch from upstream
Setting Up a Webhook
01
Create a webhook
Admin → Security → Webhooks → + Create. Enter a URL, select events to subscribe to, and optionally set a secret for HMAC-SHA256 signature verification.
02
Test delivery
Click the ⚡ button on the webhook card to send a test event. The response status and latency are shown inline.
03
Verify the signature
If a secret is set, each request carries an X-Nexspence-Signature header (HMAC-SHA256 of the body). Verify it on your receiver to reject forged requests.
Delivery: Webhooks are fired asynchronously (fire-and-forget). If your endpoint is unavailable, the event is not retried. Use a queue or durable receiver for critical integrations.
Migration from Nexus
Migrate repositories, users, roles, and artifacts from a live Nexus OSS or Pro instance without downtime.
What Gets Migrated
Item
Detail
Repositories
Hosted, proxy, and group repo definitions (format, type, configuration)
Users & Roles
Local Nexus users, roles, and privileges
Artifacts
All components streamed via Nexus REST API — large repos pause/resume
Cleanup policies
Imported and attached to the corresponding repositories
01
Open the Migration tab
Go to Admin → System → Migration. Enter the source Nexus URL, admin username, and password.
02
Select scope
Choose what to migrate: Repositories, Users, Cleanup Policies, Artifacts (blobs). You can run each separately.
03
Start and monitor
Click Start Migration. Progress is shown in the job history table. Large artifact migrations can be paused and resumed.
No downtime: Migration reads from a live Nexus instance via its REST API. Your existing Nexus continues serving traffic during migration. Switch DNS/clients to Nexspence when ready.
Monitoring
Nexspence exposes a Prometheus metrics endpoint and ships a pre-built Grafana dashboard with 8 panels.
Available Metrics
Metric
Description
nexspence_requests_total
Total HTTP requests (by method, path, status)
nexspence_request_duration_seconds
Request latency histogram (p50, p95, p99)
nexspence_artifacts_total
Total artifacts stored
nexspence_bytes_stored_bytes
Total storage used in bytes
nexspence_downloads_total
Total artifact downloads
nexspence_artifacts_deleted_total
Total artifacts deleted
nexspence_goroutines
Active Go goroutines
nexspence_memory_alloc_bytes
Heap memory in use
01
Create an API token for Prometheus
In the UI go to your profile → API Tokens → Generate Token. Paste the nxs_* token into deploy/monitoring/prometheus-token.
02
Start the monitoring stack
Run docker compose --profile monitoring up -d. Prometheus scrapes /metrics every 10 seconds. The Grafana dashboard loads automatically.
03
Open the UI Charts tab
In the Nexspence UI, go to Admin → Monitoring. The Charts tab shows request rate, error rate, and storage growth without needing Grafana.
Promote artifacts through environments (e.g. staging → production) with optional vulnerability scan gates and approval workflows.
How It Works
01
Create a Promotion Rule
Admin → Promotion → + Create Rule. Set source repository, target repository, an optional CEL path filter, and whether a passing vulnerability scan is required before promotion.
02
Request promotion
In Browse, select an artifact and click Promote. Or use the REST API. A promotion request is created in the queue.
03
Approve or reject
Admins review the request queue in Admin → Promotion → Requests. Approve to copy the artifact blob to the target repository (no data duplication — shared blob key).
Scan gate: If Require scan pass is enabled on the rule, components with HIGH or CRITICAL vulnerabilities are blocked from promotion until the issues are resolved.
Content Replication
Automatically push artifacts from one Nexspence instance to another on a schedule — for geo-distribution, DR, or environment promotion.
01
Create a Replication Rule
Admin → Replication → + Create. Set: source repository, target Nexspence URL, target repository name, credentials for the remote instance, and a cron schedule.
02
Test the connection
Click Test Connection on the rule card. Nexspence sends a probe request to the remote URL and reports success or error.
03
Monitor replication history
The history table on each rule shows past runs: timestamp, artifacts synced, errors. Click Run Now to trigger an immediate sync.
Differential sync: Each run compares asset paths between source and target and only pushes artifacts that are missing or changed on the remote. Credentials are stored AES-256-GCM encrypted.
Manage Nexspence as code with the official Terraform provider — repositories, blob stores, users, roles, content selectors, and privileges, all version-controlled and reproducible. Published on the Terraform Registry as nexspence/nexspence.
Quick Start
Declare the provider, point it at your Nexspence server, and define resources. Run terraform init, then terraform apply.
Authenticate with an nxs_* API token (preferred) or username + password. Set credentials in the provider block or via environment variables.
shell — environment variables
# API token (preferred)
export NEXSPENCE_URL="https://nexspence.example.com"
export NEXSPENCE_TOKEN="nxs_xxxxxxxxxxxxxxxx"# or HTTP Basic auth
export NEXSPENCE_USERNAME="admin"
export NEXSPENCE_PASSWORD="admin123"
Token: Generate an nxs_* token in Security → API Tokens and pass it via the token argument or NEXSPENCE_TOKEN. Never commit secrets to version control — use a Terraform variable or a secrets manager.
Resources10
Each resource maps to a Nexspence object and supports create, read, update, delete, and terraform import.
Name
Manages
nexspence_repository
Hosted, proxy, and group repositories for any format
Read existing Nexspence state into Terraform without managing it.
Name
Returns
nexspence_repository
Look up a single existing repository
nexspence_repositories
List all repositories, optionally filtered by format or type
data.tf
data "nexspence_repositories""all_docker" {
format = "docker"
}
output "docker_repo_names" {
value = [for r in data.nexspence_repositories.all_docker.repositories : r.name]
}
Examples & local development
A full manifest exercising every resource and data source ships at deploy/terraform-example/. You can define local or S3 blob stores as code, too.
Blob Stores (local & S3)
Define local or S3-compatible blob stores. The S3 endpoint is dialed by the Nexspence server, so use an address the server can reach (e.g. an in-network hostname) — not your workstation's localhost.
blobstores.tf
resource "nexspence_blobstore""main" {
name = "main"
type = "local"
path = "./data/blobs/main"
}
resource "nexspence_blobstore""s3" {
name = "s3-store"
type = "s3"
s3 = {
bucket = "nexspence-blobs"
region = "us-east-1"
endpoint = "http://minio:9000"
access_key = "minioadmin"
secret_key = var.s3_secret_key
force_path_style = true
}
}
Local development override
To hack on the provider itself, point Terraform at your locally built binary with a dev override in ~/.terraformrc.
Docker image: ghcr.io/nexspence/nexspence:latest — linux/amd64 and linux/arm64. Download the release zip from GitHub Releases.
01
Download & configure
Unpack the release zip. Copy config.yaml.example → config.yaml. Set auth.jwt_secret (min 32 chars) and bootstrap.admin_password.
02
Start
PostgreSQL starts first, Nexspence waits for it, runs migrations automatically, then serves the UI.
shell
cp config.yaml.example config.yaml
# edit config.yaml — set jwt_secret and admin_password
docker compose up -d
Service
URL
Default credentials
Web UI & REST API
http://localhost:8081
admin / admin123
Docker registry
localhost:5000
same
PostgreSQL
localhost:5437
nexspence / nexspence
MinIO console
http://localhost:9001
minioadmin / minioadmin
Note: Change the admin password immediately after first login via Admin → Security → Users.
Requires Redis enabled in config.yaml: redis.enabled: true and redis.addr: "redis:6379". Two stateless Nexspence nodes behind nginx (least_conn) — shared state in PostgreSQL, Redis, and MinIO.
Pre-configured Keycloak dev instance with the nexspence realm imported. "Sign in with Keycloak" appears on the login page automatically. Supports Keycloak, Google, Entra ID, and Okta.
shell
OIDC_ENABLED=true docker compose --profile keycloak up -d
# Nexspence: http://localhost:8081 (admin / admin123)# Keycloak admin: http://localhost:8180 (admin / admin)# Test SSO user: testuser / testpass → nx-admin role
Full Stack — HA Cluster + Keycloak SSO + Prometheus/Grafana monitoring, all in one command.
Requirements: Helm 3.x · Kubernetes ≥ 1.26. Extract the release zip — the chart is at deploy/helm/nexspence/. Run helm dependency update first to fetch the PostgreSQL sub-chart.
Every key can be overridden via env var: NEXSPENCE_<SECTION>_<KEY> (uppercase, underscore-separated).
Key
Default
Description
auth.jwt_secret
—
Required. JWT signing key — min 32 chars
auth.encryption_key
—
Optional base64 32-byte key for replication credentials — decouples them from jwt_secret; existing rows re-encrypt automatically at startup. Generate: openssl rand -base64 32
bootstrap.admin_password
admin123
Change before production. Auto-created admin password
http.addr
:8081
Listen address
http.base_url
http://localhost:8081
Public URL used in download links
auth.jwt_expiry_hours
24
JWT token lifetime
auth.token_max_days
180
Max lifetime for user API tokens (nxs_*)
auth.anonymous_enabled
true
Allow unauthenticated read on public repos
storage.default_type
local
local or s3
storage.s3.endpoint
—
S3 endpoint URL (e.g. http://minio:9000)
storage.s3.bucket
—
S3 bucket name (required when type=s3)
storage.s3.force_path_style
true
Required for MinIO / non-AWS S3
redis.enabled
false
Enable Redis (required for HA)
redis.addr
localhost:6379
Redis address
cleanup.default_schedule
0 2 * * *
Default cron for cleanup policies
gc.enabled
true
Run scheduled blob garbage collection
gc.schedule
0 3 * * 0
Cron for blob GC (weekly by default)
gc.min_age
24h
Grace period — only collect orphans older than this
audit.retention_days
90
Audit log partition retention
http.cors_origins
[]
Allowed CORS origins; empty = same-origin / wildcard dev default
http.max_body_mb
1024
Max request body in MB; bodies above it get 413 (artifact/upload routes exempt)
auth.allow_insecure_defaults
false
Allow dev-default secrets; must be false in production or the server refuses to start
auth.rate_limit_enabled
false
Enable per-IP / per-user rate limiting
Native Install (no Docker)
Run Nexspence directly on Linux, macOS, or Windows. The single binary embeds the web UI and requires only an external PostgreSQL — no Docker needed.
Prerequisites
PostgreSQL ≥ 13 is required. The binary auto-runs migrations on first start. Ensure the DB user has CREATE and CONNECT privileges on the target database.
Linux — .deb / .rpm
01
Download the package
Go to GitHub Releases and download the .deb (Debian/Ubuntu) or .rpm (RHEL/Fedora/SUSE) package for your architecture (amd64 or arm64).
02
Install
Install with your package manager. The package creates a nexspence system user and installs a systemd unit.
03
Configure and start
Edit /etc/nexspence/config.yaml, then enable and start the service.
shell — Debian/Ubuntu
sudo dpkg -i nexspence_*_linux_amd64.deb
sudo nano /etc/nexspence/config.yaml # set jwt_secret, admin_password, db DSN
sudo systemctl enable --now nexspence
Tip: The .deb / .rpm packages can ship this unit pre-installed — then you only run systemctl enable --now nexspence.
macOS
01
Download and unpack
Download nexspence_*_darwin_amd64.tar.gz (Intel) or nexspence_*_darwin_arm64.tar.gz (Apple Silicon) from the release page. Extract and move the binary to /usr/local/bin/.
02
Install the launchd service
The archive includes a com.nexspence.server.plist launchd agent. Load it with launchctl.
Heads up: a bare console binary isn't a real Windows service — use the bundled script, NSSM, or sc.exe so it survives logoff, auto-starts on boot, and restarts on crash.
Networking & Production (any OS)
Reverse Proxy (nginx / Caddy)
Recommended for production. Place nginx or Caddy in front of Nexspence to terminate TLS, set the X-Forwarded-* headers, and serve a clean public URL. Set http.base_url in config.yaml to the public HTTPS URL so download links are correct.
Stateless nodes. All state lives in PostgreSQL and the blob store (local path on a shared NFS/NAS, or S3-compatible storage). Run multiple Nexspence processes pointing at the same DB and blob store, then load-balance with nginx upstream or a cloud LB. Enable redis.enabled: true for distributed session caching.
nginx upstream block
upstream nexspence {
least_conn;
server 10.0.0.1:8081;
server 10.0.0.2:8081;
}
server {
listen 443 ssl;
server_name nexspence.example.com;
# ... ssl certs ...
location / { proxy_pass http://nexspence; }
}
Docker Registry Subdomain Connector
To pull and push Docker images via per-repository subdomains (<repo>.registry.example.com) you need wildcard DNS pointed at your server, a wildcard TLS certificate, and a reverse proxy that forwards the original Host header so the subdomain connector can route the request to the correct repository. Enable the connector in config.yaml:
After enabling the connector and restarting Nexspence, docker login repo-name.registry.example.com targets that hosted repository directly — no port or path prefix needed.
Supported Formats
14 artifact formats — each supports Hosted, Proxy, and Group repository types.
Format
Protocol
Hosted
Proxy
Group
Maven 2/3
Maven HTTP
✓
✓
✓
npm
npm registry v1
✓
✓
✓
PyPI
Simple index + twine
✓
✓
✓
Docker / OCI
OCI Distribution v2
✓
✓
✓
Go Modules
GOPROXY v2
✓
✓
✓
NuGet
v2 OData + v3 flat
✓
✓
✓
Helm
chart index.yaml
✓
✓
✓
Cargo
Sparse index
✓
✓
✓
Apt
Debian Packages index
✓
✓
✓
Yum / RPM
repomd.xml
✓
✓
✓
Conan
Conan v1
✓
✓
✓
Raw
HTTP PUT/GET
✓
✓
✓
Conda
conda index
✓
✓
✓
Terraform
Registry protocol v1
✓
✓
✓
API Reference
Nexspence exposes two API surfaces.
Base Path
Purpose
/service/rest/v1/
Nexus OSS v1 REST API — 100% compatible, drop-in for existing pipelines
/service/rest/beta/
Nexus beta endpoints — partial support
/api/v1/
Native Nexspence API — migration, extended system info, monitoring
Full OpenAPI 3.1 spec is included in the release zip as docs/api-spec.yaml.
🔑
Authentication
JWT Bearer token (from POST /api/v1/auth/login), API tokens (nxs_* prefix via Bearer or Basic password), or HTTP Basic with username + password.
Security & Hardening
Nexspence ships secure by default. The server refuses to start with shipped dev secrets, blocks server-side request forgery, revokes JWTs on account changes, and runs as a non-root, capability-dropped container.
Fail-Closed Defaults
The server will not start if auth.jwt_secret is the shipped dev default, or if bootstrap.admin_password is still admin123. Production must set a real 32+ character jwt_secret and a strong admin password. To boot anyway for local development only, set auth.allow_insecure_defaults: true.
The dev Docker Compose stack sets NEXSPENCE_AUTH_ALLOW_INSECURE_DEFAULTS=true so the quick-start still boots out of the box — never use that override in production.
Secrets via Environment
Never commit real secrets to config.yaml. Inject them through environment variables instead — they override the matching config keys at startup.
Each user carries a tokens_valid_after cutoff. It is bumped whenever the user is disabled, changes or resets their password, or has their roles changed. Any JWT issued before the cutoff is rejected immediately — no waiting for the 24-hour expiry window.
SSRF Protection
Outbound requests to user-configured URLs — webhooks, proxy upstreams, and replication targets — are blocked from reaching internal addresses: loopback, RFC 1918 private ranges, and link-local / cloud metadata (169.254.169.254). This prevents a misconfigured or malicious URL from probing your internal network.
HTTP Hardening
Control
Detail
Security headers
Every response carries X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and Referrer-Policy: no-referrer.
CORS allow-list
Restrict cross-origin callers via http.cors_origins. An empty list is the wildcard dev default.
Body size limit
http.max_body_mb rejects oversized bodies with 413. Artifact and upload routes are exempt so large packages still flow.
Timeouts & TLS
ReadHeaderTimeout guards against slow-loris; when TLS is enabled the minimum version is TLS 1.2.
Rate Limiting
Opt-in per-IP / per-user throttling. Disabled by default — enable it with auth.rate_limit_enabled: true and tune rate_limit_rps / rate_limit_burst.
Container Hardening
The official image runs as non-root uid 1000 with dropped Linux capabilities. The Helm chart sets readOnlyRootFilesystem, drops all capabilities, and sets allowPrivilegeEscalation: false.
Supply Chain
Area
Detail
Dependency scanning
CI runs govulncheck as a blocking gate; Dependabot keeps dependencies current.
Release artifacts
Releases publish an SBOM and build provenance, and the image is scanned with Trivy.
Pinned Actions
All GitHub Actions are pinned to commit SHAs to prevent tag-hijack supply-chain attacks.
Self-Service Password Change
Any non-admin user can rotate their own password without an administrator. Changing the password bumps the JWT cutoff, so existing sessions are invalidated.
Notes for moving an existing deployment to a newer Nexspence release.
Upgrading to v1.18.0 — Blob Ownership Fix
One-time step for existing single-node Docker Compose deployments. The v1.18.0 image now runs as non-root uid 1000, but blob volumes created by older root-running images are owned by root. Fix ownership once before starting the new image, otherwise Nexspence cannot write to its blob store.