usulnet

May 15, 2026 · View on GitHub

Self-hosted Docker management plane with built-in privacy tooling. Single Go binary, AGPL-3.0, one build — every feature in this repo ships in the standard binary, no edition gating, no call-home.

Go License Release

usulnet runs container lifecycle, security scanning, backups, reverse-proxy configuration, monitoring, multi-node orchestration, host infrastructure tooling (firewall, crontab, WireGuard mesh, docker-engine config, image builder), and an opt-in privacy module (OSINT recon plus file metadata hygiene) from a single process on hardware you own.

v26.5.1 Beta — released 2026-05-15. Bug reports: issues. Release notes: CHANGELOG.md and docs/v26.5/release-notes-v26.5.1.md.

Dashboard

Quick start

curl -fsSL https://raw.githubusercontent.com/fr4nsys/usulnet/main/deploy/install.sh | sudo bash

The installer pulls the production compose file, generates the database password, JWT secret, AES-256 data-encryption key, and a self-signed TLS cert. It prints the access URL when ready.

Installer terminal output

Open https://<host>:7443, log in as admin / usulnet, then change the password from the profile page.

Login page

Manual Docker Compose install, standalone binary install, and offline procedures live in docs/installation.md.

What's new in v26.5.1

Eleven modules previously gated behind the Business edition now ship in the standard AGPL build, plus a proxy-extended enhancement and the opt-in local-services TLS dispatcher. Per-feature detail in docs/v26.5/release-notes-v26.5.1.md; full per-migration audit in [v26.5.1] of CHANGELOG.md.

Sidebar — v26.5.1 modules

ModuleWhat it does
CalendarOperations calendar with manual events plus read-only aggregation of backup runs and scheduled jobs; RFC 5545 .ics export.
Proxy extendedAccess lists, dead hosts, locations, redirections, streams. Authoritative state in PostgreSQL; backend feature matrix surfaced via GET /api/v1/proxy/support.
DNSProvider plugins for Cloudflare, AWS Route 53, DigitalOcean, RFC 2136. ACME DNS-01 state machine that survives restarts. Credentials AES-256-GCM at rest.
CrontabManaged shell / docker / http cron jobs scheduled via robfig/cron/v3. WebSocket live tail, paginated executions, body cap.
FirewallUFW / nftables / iptables rule management over the existing host-management SSH transport.
SSL observatoryIn-process TLS scanning, certificate health grading, per-target alert thresholds, SNI virtual-host scans, daily sweep.
Backup verificationScheduled + on-demand restore tests in the sandboxed recon-launcher container. Real checksum + extract via the backup-service Verify pipeline.
Image builderLocal Dockerfile pipeline with live log streaming (Redis pub/sub + WebSocket/SSE), 256 MiB context cap, seven AGPL-compatible starter templates, optional cosign hook.
Automated rollbackDetects failed deploys via event-driven changes.Service.Subscribe and reverts to the last known-good stack version. Per-stack mutex, cooldown, dry-run preview, append-only Postgres-trigger audit log.
WireGuardPeer + interface manager extended into a master→agent mesh over NATS. Real Curve25519 keys, AES-256-GCM at rest, one-time QR with 5-min TTL, per-agent mesh status page.
MarketplaceCurated app catalog baked into the binary via go:embed. Zero outbound HTTP at runtime. Local-only reviews. Installs wire through the stack service.

Also in v26.5.1: the docker-engine config editor (atomic daemon.json writes with snapshot history and reload-with-rollback — no migration), the sidebar edition cleanup (all isEditionAvailable / RequireFeature / limitProvider callsites removed; license.CELimits() collapsed into license.OpenLimits()), the bootstrap restructure (the 2,700-line startStandalone split into phased init_*.go files), and opt-in local-services TLS via USULNET_TLS_LOCAL_SERVICES=true for in-cluster Postgres / Redis / NATS.

Features

Docker control plane

AreaCapability
ContainersFull lifecycle (create / start / stop / restart / pause / kill / remove), bulk operations, real-time stats, exec terminal, log viewer, filesystem browser.
ImagesPull, inspect, prune, layer history; Docker Hub and private registries with encrypted credentials.
Image builderLocal Dockerfile build pipeline with live log streaming (Redis pub/sub + WebSocket/SSE), capped context uploads (256 MiB default), AGPL-compatible starter templates, optional cosign signing hook.
Volumes and networksCreate, inspect, prune, file browser; bridge / overlay / macvlan; connect and disconnect containers.
StacksDocker Compose deployments from YAML, Git repositories, or built-in catalogue.
MarketplaceOffline curated app catalog baked into the binary via go:embed; local-only reviews; install action drives an existing stack via templated docker-compose.
SwarmCluster init, node management, services, replica scaling, standalone-to-swarm conversion.
Multi-nodestandalone, master, and agent modes; agent ↔ master traffic over NATS with mTLS; agent deploy from the UI via SSH.
Reverse proxyCaddy and Nginx Proxy Manager adapters; Let's Encrypt with auto-renewal; TCP/UDP stream proxying. Extended state (access lists, dead hosts, locations, redirections, streams) authoritative in PostgreSQL with explicit backend support matrix.
DNS automationProvider plugins for Cloudflare, AWS Route 53, DigitalOcean, and RFC 2136 nameservers; ACME DNS-01 state machine that survives restarts; credentials AES-256-GCM at rest.
BackupsContainer / volume / stack targets; cron schedules with retention; gzip or zstd; local, S3, MinIO, Azure Blob, GCS, B2, SFTP.
Backup verificationScheduled + on-demand restore tests in a sandboxed container (read-only rootfs, dropped caps, no-new-privileges, default seccomp); real checksum + extract via the backup-service Verify pipeline.
Automated rollbackDetects failed deploys via event-driven changes subscription and reverts to the last known-good stack version; per-stack mutex prevents racing manual deploys; cooldown window; dry-run preview; append-only audit log via Postgres trigger.
SSL observatoryIn-process TLS scanning, certificate health grading, per-target alert thresholds, SNI virtual-host scans, daily scheduled sweep, expiry alerts routed via the notification service.
MonitoringPer-container and per-host CPU / RAM / network / disk; threshold alerts with OK → Pending → Firing → Resolved state machine; 11 notification channels (Email, Slack, Discord, Telegram, Gotify, ntfy, PagerDuty, Opsgenie, Microsoft Teams, generic webhook, custom).
Logs and eventsAggregated container logs with search; Docker event stream with filtering; packet capture per network interface.
VulnerabilitiesTrivy CVE scans for images and filesystems; 0–100 security score per container and infrastructure-wide; SBOM in CycloneDX and SPDX; Docker CIS Benchmark checks.
CalendarOperations calendar with manual events and read-only aggregation of backup runs + scheduled jobs; RFC 5545 .ics export.

Marketplace browse

Host & infrastructure tooling

AreaCapability
FirewallUFW / nftables / iptables rule management over the existing SSH host transport. Web UI + REST API; closed-enum validation; audit log of every apply.
CrontabManaged cron jobs (shell, docker, http types) parsed with robfig/cron/v3; paginated executions; WebSocket live tail; HTTP body capped at 64 KiB.
Docker engine config/etc/docker/daemon.json editor with atomic writes (temp + fsync + rename), snapshot history with rotation, and reload-with-rollback under a hard 60 s timeout. Monaco diff editor + history page.
WireGuardPeer + interface manager extended into a master→agent mesh over the NATS gateway. Real Curve25519 keys, private/preshared keys AES-256-GCM at rest, one-time QR endpoint with 5-min TTL, per-agent mesh status page.

Firewall rules WireGuard peers

Capability requirements

The modules in the previous table reach the host through the existing host-management transport or a bind mount. Capability requirements:

ModuleNeeded
firewallNET_ADMIN on the agent process; ufw / nft / iptables available on the host.
WireGuardWireGuard kernel module + wg / wg-quick on each agent host; NET_ADMIN on the agent process; USULNET_ENCRYPTION_KEY set on the master.
docker-engine configHost /etc/docker bind-mounted into the usulnet container as :rw.
image builderDocker socket mount (already required by the platform).
backup verificationDocker socket mount (already required).
DNS providersOutbound egress to each provider's API endpoint; USULNET_ENCRYPTION_KEY set.

All other v26.5.1 modules (crontab, ssl-observatory, calendar, marketplace, rollback, proxy-extended) need nothing beyond the default compose stack. Full detail in docs/installation.md.

Privacy and recon (opt-in)

Off by default — every recon route returns 404 until an admin sets USULNET_RECON_ENABLED=true and records the legal-notice acknowledgement (POST /api/v1/recon/_ack). Full design and threat model in docs/recon.md; signed review at docs/v26.5/security-review-checklist.md.

Recon dashboard

AreaCapability
OSINT reconSpiderFoot-driven passive scans against emails, domains, phones, IPs, usernames. Ownership is verified (DNS TXT, e-mail link, RDAP, admin-attest, self-assert) before a scan can start.
Scan profilesFour built-in profiles plus full CRUD for user-defined profiles. Built-in rows are immutable; the module catalogue is closed.
ReportsPer-scan JSON, CSV, and paginated A4 PDF at /api/v1/recon/scans/{id}/report.{json,csv,pdf}. PDF is pure Go and byte-deterministic.
Metadata hygienemat2 strip plus exiftool / pdfid / oletools extract. Each job runs in a fresh container with read-only rootfs, all Linux capabilities dropped, seccomp default, and PID / memory caps.
Sandbox networkDedicated usulnet-recon Docker network with a strict egress allow-list (DNS, 80, 443 by default).
HIBP connectorOptional Have-I-Been-Pwned integration. Credentials AES-256-GCM at rest in recon_connectors; keys are never returned through the API or logs.
RetentionDaily prune of findings, scans, and audit log past the per-tenant TTL (default 90 days). Two-phase delete on metadata artifacts.
AuditAppend-only recon_audit_log records every state-changing action with actor, target, and request hash.

Auth, RBAC, platform

AreaCapability
AuthenticationJWT with configurable expiry; X-API-KEY for programmatic access; TOTP 2FA with backup codes; LDAP / Active Directory; OAuth2 / OIDC (GitHub, Google, Microsoft, custom).
AuthorisationRBAC with 44+ granular permissions, custom roles, team-based resource scoping.
SecretsAES-256-GCM at rest for every sensitive value; bcrypt password hashing; configurable password complexity; account lockout.
TransportTLS with auto-generated self-signed certs (or BYO); mTLS for inter-node messaging; configurable rate limiting; CSRF; secure cookie defaults. Opt-in local-services TLS for in-cluster Postgres / Redis / NATS via USULNET_TLS_LOCAL_SERVICES=true.
AuditPer-user action log persisted to PostgreSQL with IP, timestamp, detail; CSV export.
ObservabilityPrometheus /metrics (admin-auth) including Go runtime and process metrics; OpenTelemetry instrumentation.
APIREST under /api/v1; OpenAPI 3.0 at /api/v1/openapi.json; Swagger UI at /docs/api; WebSocket streams for logs, exec, stats, events, metrics, packet capture, and nvim.

Developer tools

Web-based terminal hub (xterm.js) with container exec and host SSH. Monaco editor and Neovim with lazy.nvim for in-browser file editing. Filesystem browsers for containers, hosts, and SFTP. Snippets and cheat sheets. Outgoing webhooks on container events with retry. Auto-deploy rules triggered by Git push. Runbooks for multi-step operations. Cron-scheduled jobs for backups, scans, metrics, update checks, recon retention, and cleanup.

Architecture

usulnet ships as a single binary that runs in one of three modes:

  • standalone — one Docker host, all services local. NATS not required.
  • masterstandalone plus a NATS gateway server for remote agents.
  • agent — connects to a master via NATS. No web UI; executes Docker operations against its local host.

PostgreSQL stores domain state across 56 migrations (users, RBAC, connections, backups, scans, alerts, recon, audit log, plus the v26.5.1 ported modules: calendar, proxy-extended, DNS, crontab, firewall, SSL observatory, backup verification, image builder, automated rollback, WireGuard, marketplace). Redis backs session storage and JWT blacklisting. NATS with JetStream carries inter-node traffic with persistence. Full component diagram, request flow, and topology in docs/architecture.md.

Multi-node setup

# On the control plane host
export USULNET_MODE=master AGENT_TOKEN=<secure-token>
docker compose up -d

# On each remote host you want to manage
docker run -d --name usulnet-agent --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  usulnet/usulnet-agent:latest \
  --gateway nats://<master-ip>:4222 \
  --token <secure-token>

Master ↔ agent traffic flows over NATS with mTLS on the usulnet-backend Docker network. Full agent protocol, NATS subject map, and TLS provisioning at docs/agents.md.

Multi-node topology

Stack

LayerComponent
LanguageGo 1.25.10
HTTP routerChi v5
TemplatesTempl (compile-time HTML)
CSSTailwind via the standalone CLI (no Node.js)
BrowserAlpine.js, HTMX, xterm.js, Monaco
DatabasePostgreSQL 16 via pgx/v5 and sqlx
Cache / sessionsRedis 7
MessagingNATS 2.10 with JetStream
AuthJWT, OAuth2 / OIDC, LDAP, TOTP
Vulnerability scannerTrivy
PDFgofpdf (pure Go)
Schedulingrobfig/cron v3
DNS (provider plugins)net/http (Cloudflare, DigitalOcean), aws-sdk-go-v2/route53, miekg/dns (RFC 2136 + TSIG)
WireGuardgolang.org/x/crypto/curve25519 + agent-side wg / wg-quick

Configuration

config.yaml plus environment variables prefixed USULNET_ (nested keys joined by _). The Viper loader treats environment overrides as canonical.

USULNET_SERVER_PORT=9090
USULNET_DATABASE_URL=postgres://usulnet:secret@db/usulnet?sslmode=disable
USULNET_SECURITY_JWT_SECRET=<64-hex>
USULNET_ENCRYPTION_KEY=<64-hex>
USULNET_RECON_ENABLED=true
USULNET_MODE=standalone

# Opt-in TLS for in-cluster Postgres / Redis / NATS (v26.5.1)
USULNET_TLS_LOCAL_SERVICES=true

# Module knobs
USULNET_IMAGE_BUILDER_MAX_CONTEXT_BYTES=268435456   # 256 MiB
USULNET_SSL_OBSERVATORY_PER_TARGET_CONCURRENCY=4

Full reference, defaults, and the production compose template are in docs/installation.md.

CLI

usulnet serve              # run the server
usulnet migrate up         # apply pending migrations
usulnet migrate status     # show migration state
usulnet migrate down [N]   # roll back N migrations (default 1)
usulnet config check       # validate configuration
usulnet config show        # display config with secrets masked
usulnet admin reset-password   # reset the admin password
usulnet recon …            # opt-in recon subcommand tree
usulnet meta …             # opt-in metadata hygiene subcommand tree
usulnet version            # print build info

The agent binary (usulnet-agent) is documented in docs/agents.md.

usulnet version output

API examples

REST under /api/v1, authenticated with X-API-KEY or Authorization: Bearer <jwt>. Full OpenAPI at /api/v1/openapi.json, Swagger UI at /docs/api. Reference: docs/api.md.

# List running containers
curl -fsSL -H "X-API-KEY: $USULNET_API_KEY" \
  https://usulnet.example.com:7443/api/v1/containers?status=running | jq

# Browse the embedded marketplace catalog
curl -fsSL -H "X-API-KEY: $USULNET_API_KEY" \
  https://usulnet.example.com:7443/api/v1/marketplace/apps | jq '.[].slug'

# Trigger an SSL observatory scan against every enabled target
curl -fsSL -X POST -H "X-API-KEY: $USULNET_API_KEY" \
  https://usulnet.example.com:7443/api/v1/ssl/scan

# Apply a firewall rule on a host through the agent transport
curl -fsSL -X POST -H "X-API-KEY: $USULNET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"host_id":"<uuid>","chain":"INPUT","action":"ACCEPT","protocol":"tcp","port":22,"comment":"ssh"}' \
  https://usulnet.example.com:7443/api/v1/firewall/rules

# Start a backup verification run
curl -fsSL -X POST -H "X-API-KEY: $USULNET_API_KEY" \
  https://usulnet.example.com:7443/api/v1/backup-verify/runs \
  -d '{"backup_id":"<uuid>","method":"extract"}'

WebSocket endpoints for live container logs / exec / stats / image build output / crontab execution tail follow the same path scheme with the Upgrade: websocket header.

Documentation

TopicPath
Installation and deploymentdocs/installation.md
Capability requirements (v26.5.1 modules)docs/installation.md#capability-requirements-v2651-modules
Development setup and workflowdocs/development.md
REST and WebSocket APIdocs/api.md
Architecturedocs/architecture.md
Recon and metadata modulesdocs/recon.md
Multi-node agentsdocs/agents.md
Licensingdocs/licensing.md
Signed security review (recon, v26.5)docs/v26.5/security-review-checklist.md
Security review (v26.5.1 hardening audit)docs/v26.5/security-review-v26.5.1.md
Release notes (v26.5.1)docs/v26.5/release-notes-v26.5.1.md
Ported modules status boarddocs/v26.5/v26.5.1-ported-modules.md
Full changelogCHANGELOG.md
Screenshotsdocs/screenshots/

Development

git clone https://github.com/fr4nsys/usulnet.git
cd usulnet
make dev-up      # postgres, redis, nats, minio
make build       # templ generate + tailwind compile + go build
make run

make quality runs the full gate: golangci-lint, go vet, scripts/check-naming.sh, and the coverage threshold check (15 % interim, 40 % target). make publish-public produces the curated public-mirror tree under build/public/ for the release workflow. The full developer guide — workflow, hooks, profiling, debugging — is in docs/development.md.

Commits follow Conventional Commits.

License

AGPL-3.0-or-later. Self-hosted use is free in perpetuity. One AGPL build, all features — no edition gating, no closed-source extension point, no feature gated by a paid license tier. Optional commercial support contracts are documented in docs/licensing.md; they do not unlock additional runtime features.

License page — AGPL banner

Security

Report vulnerabilities to security@usulnet.com. Do not open public issues for security findings.

Security posture:

  • External port surface: only HTTPS (7443) is published by default. HTTP (8080) and NATS (4222) stay internal to the usulnet-backend Docker network.
  • No call-home. No outbound request to any usulnet-controlled endpoint at runtime. Every outbound call (registry, DNS provider, OAuth issuer, HIBP) is operator-configured.
  • Container user usulnet:1000 (non-root). Recon sandbox runs as nobody:nogroup (65534) with read-only rootfs and all Linux capabilities dropped.
  • AES-256-GCM at rest for every sensitive value (passwords are bcrypt; WireGuard keys, DNS provider credentials, HIBP keys, raw recon payloads, JWT signing keys, OAuth client secrets).
  • govulncheck in CI on every push and PR. v26.5.1 dependency baseline covers Go 1.25.10, pgx 5.9.2, NATS 1.52.0, go-redis 9.19.0, go-chi 5.2.5, jwt 5.3.1, go-oidc 3.18.0, go-ldap 3.4.13, docker 28.5.2; two Moby false positives (GO-2026-4883, GO-2026-4887) are in the allowlist with inline justification.

Full v26.5.1 hardening audit: docs/v26.5/security-review-v26.5.1.md.