NPM's certificate creation endpoint rejects letsencrypt_agree and
letsencrypt_email in the meta field (schema validation error). The
LE email is configured globally in NPM settings. Empty meta works.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes:
1. When updating existing proxy host, preserve its certificate_id
and SSL settings instead of resetting to 0
2. Search NPM certificates by domain if proxy host has no cert
assigned (handles manually created certs)
3. Remove invalid 'nice_name' and 'dns_challenge' from LE cert
request payload (caused 400 error on newer NPM versions)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a proxy host already exists in NPM (domain "already in use"),
the code now finds the existing host, updates it, and requests SSL
instead of failing with an error. Also checks if the host already
has a valid certificate before requesting a new one from Let's Encrypt.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The deployment_logs table has a CHECK constraint allowing only
'success', 'error', 'info'. Using 'warning' caused an IntegrityError
that crashed the entire deployment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The HTTP fallback (Step 9b) would rewrite all configs to HTTP when SSL
cert creation failed, but if the user then manually set up SSL in NPM
the dashboard would fail with "Unauthenticated" due to mixed content
(HTTPS page loading HTTP OAuth endpoints). Now keeps HTTPS configs and
logs a warning instead, so manual SSL setup works correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When redeploying a customer without undeploying first, the management
server would crash with FATAL because a new DataStoreEncryptionKey was
generated but the old database (encrypted with the old key) still
existed. Now:
- Reads existing key from management.json if present
- Reuses existing UDP port from deployment record
- Stops old containers before starting new ones
- Updates existing deployment record instead of creating duplicate
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The management container was trying to fetch its own OIDC config via
the external URL (https://domain/oauth2/.well-known/...), which creates
a circular dependency: management -> DNS -> NPM -> Caddy -> management.
This fails because the management container can't reach itself through
the external network during startup.
Changed OIDCConfigEndpoint to http://127.0.0.1:80/oauth2/... (same as
LocalAddress) so the management server accesses its own embedded IdP
directly without going through DNS/NPM/Caddy.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create NPM proxy host WITHOUT SSL initially (ssl_forced=False),
then request Let's Encrypt cert, then enable SSL only after cert
is assigned. Prevents broken proxy when cert fails.
- If SSL cert creation fails, automatically fall back to HTTP mode:
re-render management.json, dashboard.env, relay.env with http://
URLs and recreate containers so dashboard login works.
- Better error logging in _request_ssl with specific timeout hints.
- Use template variables for relay WebSocket protocol (rels/rel)
instead of hardcoded rels:// in management.json.j2 and relay.env.j2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Socket detection inside Docker returns the container IP (172.18.0.x),
not the host IP. Now:
- install.sh detects host IP via hostname -I and stores in .env
- docker-compose.yml passes HOST_IP to the container
- npm_service.py reads HOST_IP from environment
- Increased SSL cert timeout to 120s (LE validation is slow)
- Added better logging for SSL cert creation/assignment
- README updated with HOST_IP in .env example
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- npm_service._get_forward_host() now detects the actual host IP via
UDP socket (works inside Docker containers) instead of using
172.17.0.1 Docker gateway which NPM can't reach
- install.sh uses hostname -I for NPM forward host
- Removed npm_api_url parameter from _get_forward_host()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Changed cp -r ./* to cp -a . so hidden files (.git) are included
when copying to the install directory. Without this, git pull fails
in /opt/netbird-msp because it's not a git repository.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New optional question in Step 4: MSP Appliance domain (e.g. msp.example.com)
- After app startup: auto-creates NPM proxy host forwarding to host IP:8000
- Requests Let's Encrypt SSL cert and assigns it to the proxy host
- Shows HTTPS URL in completion summary when domain is configured
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Forward proxy to host IP + dashboard_port instead of container name
- Remove redundant advanced_config (Caddy handles internal routing)
- Add provider: letsencrypt to SSL certificate request
- Add NPM UDP stream creation/deletion for STUN/TURN relay ports
- Add npm_stream_id to Deployment model with migration
- Fix API docs URL in README (/api/docs)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each customer gets TCP 9000+ID (dashboard) and UDP 3478+ID (relay).
Updated install.sh firewall section, completion summary, and README.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Multi-language support (EN/DE) with i18n engine and language files
- Configurable branding (name, subtitle, logo) in Settings
- Global default language and per-user language preference
- User management router with CRUD endpoints
- Customer status sync on start/stop/restart
- Health check fixes: derive status from container state, remove broken wget healthcheck
- Caddy reverse proxy and dashboard env templates for customer stacks
- Updated README with real hardware specs, prerequisites, and new features
- Removed .claude settings (JWT tokens) and build artifacts from tracking
- Updated .gitignore for .claude/ and Windows artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>