Reverse Proxy
In both network modes, norsk-ctl runs an nginx reverse proxy (norsk-proxy) as the single entry point for browser traffic. It terminates TLS, enforces authentication, and routes requests to the right instance’s containers by matching the instance ID in the URL path. Every instance’s UI is fronted by the proxy, whatever product it comes from; the modes differ only in how the proxy reaches an instance’s media container (over the bridge in docker, via the host in hybrid).
What it gives you
Section titled “What it gives you”- Single external port — all instances reachable through one URL (default
:443) instead of one port per instance - Path-based routing — each instance lives at
/instance/<id>/… - Authentication — HTTP Basic Auth (htpasswd) by default; OAuth2 (via
oauth2-proxy) for IdP-backed deployments - TLS termination — HTTPS at the edge, plain HTTP to upstreams on
norsk-net - Zero-downtime reload — adding instances or users doesn’t drop live connections
See Architecture for a diagram of how proxied and direct (ingest) traffic flow together.
URL shape
Section titled “URL shape”Each running instance surfaces several paths under /instance/<id>/:
| Path | Upstream | Purpose |
|---|---|---|
/instance/<id>/ | the template’s runtime-screen service (Studio: <id>-studio-1:8000) | Instance runtime UI |
/instance/<id>/studio/ | <id>-studio-1:8000 | Studio UI (with WebSocket upgrade) |
/instance/<id>/media/ | <id>-media-1:8080 | Media HTTP API |
/instance/<id>/visualiser/ | <id>-media-1 (visualiser port) | Pipeline visualiser |
/instance/<id>/uvis/ | <id>-media-1 (visualiser port) | Unified visualiser (WebSocket) |
/instance/<id>/status | <id>-media-1 (visualiser port) | Instance status endpoint |
/products/<name>/ | host.docker.internal:8333 (daemon forwards to the product’s backend) | Registered product’s config UI |
/api/ | host.docker.internal:8333 | norsk-ctl daemon API |
/mcp | host.docker.internal:8333 | MCP HTTP endpoint (loopback bypass for local stdio bridges) |
/ | host.docker.internal:8333 | norsk-ctl web UI |
Default proxy port is 443 (HTTPS) across all network modes. Port 80 is bound for an HTTP→HTTPS redirect plus certbot HTTP-01 validation; pass --no-http-redirect to skip (then certbot is no longer available — it needs port 80). Override the HTTPS port with --proxy-port N at install / init, or proxyPort: in config.yaml. See Ports for the full picture.
The legacy 9443 / 9080 docker-mode defaults are gone — TLS is mandatory and 443 is the unified default.
Commands
Section titled “Commands”norsk-ctl proxy start # Start the nginx container and generate config from running instancesnorsk-ctl proxy stop # Stop and remove the proxy containernorsk-ctl proxy reload # Regenerate config and reload nginx (zero-downtime)norsk-ctl proxy status # Show proxy container status and active upstreamsnorsk-ctl proxy pull # Pre-pull the nginx (and oauth2-proxy, if applicable) imagesnorsk-ctl proxy logs # View access or error logs (--type access|error, -n, -f)norsk-ctl init starts the proxy automatically in docker and hybrid modes. Subsequent launches trigger a proxy reload, so you rarely run these commands by hand.
Authentication
Section titled “Authentication”HTTP Basic Auth (default)
Section titled “HTTP Basic Auth (default)”Users live in an nginx htpasswd file managed via the user subcommands.
# Add or update (prompts for password, or pipe via stdin)norsk-ctl user set alice
# Remove a usernorsk-ctl user delete aliceChanges take effect immediately — the daemon writes the htpasswd file and tells nginx to reload without dropping connections. No manual proxy reload needed.
OAuth2
Section titled “OAuth2”For deployments fronted by an identity provider (Google, Okta, etc.), norsk-ctl can run oauth2-proxy alongside nginx. Enable at init by passing OAuth2 credentials — the proxy then redirects unauthenticated users through the IdP before forwarding to the upstream.
MCP loopback bypass
Section titled “MCP loopback bypass”/mcp is special-cased: it bypasses proxy auth when the request originates from the loopback interface. That lets norsk-ctl mcp (the local stdio bridge) talk to the daemon’s HTTP MCP endpoint without juggling auth credentials. External requests to /mcp over the public proxy URL are blocked.
TLS is mandatory; the insecure HTTP mode was removed. nginx adds an HTTPS server block listening on proxyPort (default 443) with:
- TLSv1.3 only
- Post-quantum cipher group
X25519MLKEM768+TLS_AES_256_GCM_SHA384 - HTTP/2 enabled
Plain HTTP requests to port 80 are redirected to HTTPS (unless --no-http-redirect was passed at install).
Certificate provenance is tracked via certSource in config.yaml — mkcert for local dev, self-signed for IP-only servers, user for a bring-your-own cert, certbot for auto-renewed Let’s Encrypt. See Security for the picker guide.
Viewing logs
Section titled “Viewing logs”# Tail access lognorsk-ctl proxy logs --type access -f
# Last 200 lines of the error lognorsk-ctl proxy logs --type error -n 200Both also appear in the Logs → Proxy view in the web UI.