LogoRevali

HTTPS in Development

Run your server over HTTPS using a local certificate

Revali can bind an HttpServer with TLS, backed by dart:io's SecurityContext. This is most useful in development when a client (a physical mobile device, a browser enforcing secure-context APIs, etc.) requires HTTPS even against localhost.

There are two ways to enable it — --cert/--key on revali dev (no code changes, the easiest path for local testing) or AppConfig.secure (programmatic, for when you need more control). Both need a certificate and key file, so start there either way.

Generate a Local Certificate with mkcert#

mkcert creates locally-trusted certificates without the browser warnings a self-signed cert would trigger.

# Install mkcert (macOS)
brew install mkcert

# Install the local CA into your system/browser trust stores (once per machine)
mkcert -install

# Generate a cert + key for localhost
mkcert -key-file certificates/localhost-key.pem -cert-file certificates/localhost.pem localhost 127.0.0.1 ::1

This produces two files — add certificates/ to .gitignore, since these are local-machine credentials, not something to ship or share.

Quick Start: --cert / --key#

Pass the generated files directly to revali dev — no code changes needed, even if your AppConfig uses the plain (non-secure) constructor:

dart run revali dev --cert certificates/localhost.pem --key certificates/localhost-key.pem

Your server is now reachable over HTTPS at whatever host/port your AppConfig already specifies (e.g. https://localhost:8080/api/). --cert and --key must be passed together — providing only one is an error.

Advanced: AppConfig.secure#

Reach for AppConfig.secure instead of --cert/--key when you need to build the SecurityContext yourself — for example, loading a certificate from somewhere other than two PEM files, switching it conditionally based on --flavor, or requesting a client certificate for mutual TLS (requestClientCertificate, not available via the CLI flags).

routes/main_app.dart
import 'dart:io';

import 'package:revali_router/revali_router.dart';

@App()
final class MainApp extends AppConfig {
  MainApp()
      : super.secure(
          host: 'localhost',
          port: 8443,
          securityContext: SecurityContext()
            ..useCertificateChain('certificates/localhost.pem')
            ..usePrivateKey('certificates/localhost-key.pem'),
        );
}

Run it the same way as always, with no --cert/--key flags needed:

dart run revali dev

If both are present — an AppConfig.secure app and --cert/--key on the command line — the CLI flags win, since they're a more specific, explicit request for that particular run.

Available Options#

AppConfig.secure accepts everything the default constructor does (host, port, prefix, workers, backlog), plus:

ParameterDescription
securityContext (required) The dart:io SecurityContext holding the certificate chain and private key.
requestClientCertificate Whether to request a client certificate (mutual TLS). Defaults to false.

Connecting from a Physical Device#

If you need a different device on your network to trust the certificate (not just this machine's browser), install the mkcert root CA on that device too — mkcert prints the CA's location with mkcert -CAROOT. Alternatively, generate the certificate for your machine's LAN IP instead of localhost (mkcert -key-file ... -cert-file ... 192.168.1.50) so the hostname the device connects to matches what's on the certificate.

Next Steps#