Manual contentsCLI ReferenceBrowse 103 chapters
Manual 4 min read

Named HTTPS Dev URLs (suprnova dev:tls)

By default suprnova serve serves your backend on a raw http://127.0.0.1:8765. That's fine for most development - but some browser features only work over HTTPS on a named host:

  • Passkeys / WebAuthn - require a secure context and a stable origin.
  • Secure cookies and SameSite=None - only set over HTTPS.
  • Service workers - only register over HTTPS (or localhost).
  • OAuth/OIDC redirect URIs - providers often reject raw IP/port hosts.

portless gives every local app a stable https://<name>.localhost URL behind a single TLS proxy on port 443. suprnova dev:tls wires Suprnova to portless and - the part that's easy to get wrong - trusts portless's local CA in every browser certificate store on your machine, with no sudo on Linux.

Strictly opt-in. portless is never required. suprnova serve works with no portless installed. You opt in when you scaffold (suprnova new <name> --with-portless) or by adding portless.json later. If you never run dev:tls, you never touch portless.

Install portless

portless is a Node tool:

npm install -g portless

Then install its always-on 443 proxy once (this is a system-level, sudo-required step that belongs to portless, not Suprnova):

portless service install

Per project

You have two ways to opt a project in.

Scaffold with the flag - writes portless.json up front:

suprnova new myapp --frontend svelte --with-portless

That emits a portless.json at the project root:

{
  "name": "myapp",
  "appPort": 8765
}

appPort is your backend's fixed SERVER_PORT. It tells portless the app binds a known port (instead of portless assigning one via $PORT), so the named URL routes straight to it.

Add it to an existing project - write that same portless.json by hand (or run portless alias myapp 8765), using your SERVER_PORT.

Then, on each machine that will run the app, do the one-time trust + route registration:

cd myapp
suprnova dev:tls

This:

  1. Checks portless is on your PATH.
  2. Resolves the name (--name, else Cargo.toml [package].name) and port (--port, else SERVER_PORT, else 8765).
  3. Registers the route myapp.localhost → 127.0.0.1:8765 (skip with --no-alias).
  4. Trusts portless's CA in your browsers' certificate stores.
  5. Prints the next steps.

Flags:

Flag Effect
--name <name> Override the URL name. Default: Cargo.toml package name.
--port <port> / -p Override the routed port. Default: SERVER_PORT, else 8765.
--no-alias Only trust the CA; don't touch the portless route.
--yes Skip the confirmation before modifying your certificate stores. Ignored when the CA's fingerprint changed since the last run - that always asks.

Why step 4 asks first

Trusting a CA means every certificate it signs is accepted by your browser, silently, for every site. That is worth one deliberate keystroke.

The CA is resolved only from portless's own state, never from anything the project directory can influence - a checked-out repo cannot point dev:tls at a CA of its choosing. The command prints the fingerprint it is about to trust and waits for you to confirm. If the fingerprint differs from the one trusted previously, it asks even under --yes: a changed CA is either a portless reinstall or something you want to look at, and only you can tell which.

Run

suprnova serve

Open https://myapp.localhost.

The backend binds 8765 by default; the Vite dev server rides along on 5765 over http://localhost. A page served from the HTTPS origin can reference http://localhost assets because browsers treat localhost as a secure context - it is not blocked as mixed content.

Hot Module Reload over HTTPS is best-effort. Vite's HMR websocket connects back to the dev server; whether that succeeds cleanly over the HTTPS origin depends on your Vite/browser versions. If live updates stop working under https://, point Vite at an HTTPS dev-server origin via the INERTIA_VITE_DEV_SERVER environment variable. Page loads and the rest of the flow are unaffected.

Multiple apps

portless owns 443 and multiplexes by subdomain. Register each app with its own name and port:

suprnova dev:tls --name app-one --port 8765
suprnova dev:tls --name app-two --port 8766

Never bind 443 from an app directly - that's portless's job.

Troubleshooting

ERR_CERT_AUTHORITY_INVALID after running dev:tls. Your browser wasn't fully restarted. Browsers read their certificate store once at launch; a tab reload is not enough. Type chrome://restart (or fully quit and relaunch).

502 Bad Gateway. The proxy is up but your backend isn't. Run suprnova serve in the project directory.

portless trust says "A terminal is required to authenticate". That's portless's own command needing a real TTY for sudo. suprnova dev:tls sidesteps it entirely on Linux: it installs the CA straight into your browsers' NSS stores, which need no sudo.

A Flatpak browser is still untrusted. Flatpak browsers keep their NSS database under ~/.var/app/<id>/.pki/nssdb. dev:tls covers those - re-run it and fully restart that browser.

certutil: command not found. Install NSS tools:

Distro Command
Debian/Ubuntu sudo apt install libnss3-tools
Fedora/RHEL sudo dnf install nss-tools
Arch sudo pacman -S nss

portless CA not found at ~/.portless/ca.pem. portless generates its CA when the proxy first runs. Start it once (systemctl start portless, or portless proxy start), then re-run suprnova dev:tls.

Platform notes

The browser-NSS path above is the Linux mechanism. On macOS and Windows, browsers read the OS keychain / certificate store, so dev:tls delegates CA trust to portless trust, which targets those native stores.