How is the connection between server and client secured?

Two independent layers:

In short: TLS encrypts and authenticates the server; the connect token authenticates the client. (This is server-auth TLS + token auth, not mutual-TLS.)

Is an HTTP frontend end-to-end encrypted from the visitor to my local app?

No — like ngrok, for HTTP frontends TLS terminates at the server (it must, to answer ACME challenges and route by Host). The path is: visitor →(public TLS)→ server →(tunnel TLS)→ client →(plaintext, on your machine)→ local app. The server can see request content; the only unencrypted hop is client-to-localhost on your own machine. Run the server on infrastructure you trust. (TCP frontends are different — see below.)

I tunnel a TCP service with its own TLS (e.g. Postgres SSL). Is that end-to-end encrypted?

Yes. A TCP frontend is a raw passthrough — the server never terminates or inspects the connection, it just forwards bytes. So the application's own TLS handshake (Postgres SSL, an MQTT/AMQP TLS, etc.) happens directly between your client and your actual service; seikan only ever sees opaque ciphertext (double-encrypted on the server↔client hop, since the tunnel itself is TLS):

psql ──TLS (Postgres SSL, end-to-end)───────────────────────────► postgres
       carried as opaque bytes: public TCP port → server →(tunnel TLS)→ client → localhost:5432

For real end-to-end safety, connect with sslmode=verify-full so the client validates Postgres's own certificate (the cert presented is Postgres's, passed through untouched), and make sure the cert's SAN matches how you connect.

How are secrets stored?

API keys (sk_…) and connect tokens (sk_t_…) are 256-bit random and stored only as SHA-256 hashes in the server's bbolt database; raw values are shown once at creation. Validation is constant-time. Repeated bad credentials trigger a temporary per-IP lockout (brute-force protection).

How is the admin API protected?

It's served only on the reserved admin host over HTTPS and requires Authorization: Bearer <sk_…>. Failed attempts are rate-limited/locked out per IP.

Can I give other people scoped access (multi-tenancy)?

Yes, with sub-tokens. The admin key has two roles. The root key — printed on first boot — can issue sub-tokens with seikan apikey create; each is a scoped tenant credential.

A tenant sets up their client with the sub-token exactly like the root user (seikan init --server admin.example.com --api-key sk_…); their serve/list/rm are automatically scoped to their own frontends. Per-frontend connect tokens (sk_t_…) and the data plane are unchanged — ownership is purely an admin-API concern.

Can one tunnel serve several domains, or a wildcard?

Yes. An HTTP frontend can carry multiple domains and wildcards: seikan serve app.example.com,www.example.com 3000, or seikan serve '*.example.com' 3000. A wildcard matches a single label (a.example.com, not a.b.example.com). Certificates for wildcard frontends are issued per concrete subdomain on demand (TLS-ALPN-01/HTTP-01) the first time each subdomain is hit — so every subdomain you actually use must have DNS pointing at the server. There's no single *.example.com certificate (that would need DNS-01).

Is the metrics / pprof endpoint exposed?

No — it binds to 127.0.0.1:9090 by default (not public). Scrape it locally or over an SSH tunnel, or set SEIKAN_METRICS_ADDR="" to disable it.

What happens if two clients connect with the same token / domain?

It's a feature, not a conflict — behavior depends on the frontend type:

What should I avoid in production?

Don't use --insecure / SEIKAN_INSECURE (it disables server-cert verification). Keep ACME staging off once issuance works. Treat the admin API key and connect tokens like passwords; rotate by creating a new key and deleting the old.