Skip to content

v0.17.0: secure transport — LAN-scoped by design, loud when it matters - #18

Merged
tick10 merged 8 commits into
mainfrom
feature/transport-model
Aug 20, 2026
Merged

tick10 merged 8 commits into
mainfrom
feature/transport-model

Conversation

@tick10

@tick10 tick10 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Phoenix is LAN-scoped by design. Use outside a LAN — internet/WAN exposure, public ingress, hostile-network operation — is out of scope and unsupported. Being in scope is not the same as being trusted: a LAN includes containers on shared bridges, IoT devices, and anything that has compromised one host, so TLS matters whenever a request crosses a wire.

Plaintext on loopback stays the default and is unchanged.

The defect this fixes

The default was safe only because it was loopback, and nothing signalled when that stopped being true. Editing server.listen to 0.0.0.0:9090 — an edit typically made for an unrelated reason — silently moved bearer tokens and secret values onto the wire. The startup log named the address and said nothing about transport.

This fixes the signal, not the default.

Changes

  • First-class tls: config block, independent of mtls:. TLS serving previously existed only inside the mTLS block, so operators had to enable client-certificate authentication just to get HTTPS — docs/lan-deployment.md literally instructed exactly that. "tls": {"enabled": true} now works alone, defaulting to the server leaf --init already generates. mtls: becomes purely the client-certificate layer.
  • Loud startup warning when binding a non-loopback address without TLS, naming what is exposed and the one-line fix. It warns; it never refuses to start.
  • TLS for the MCP HTTP transport (--tls-cert/--tls-key, or PHOENIX_MCP_TLS_CERT/PHOENIX_MCP_TLS_KEY), with the same warning.
  • Client-side warnings in the CLI and Go SDK for non-loopback http://. Warn and proceed.
  • Docs: threat-model.md gains a deployment-envelope section and its mitigations were re-audited against it; lan-deployment.md rewritten onto tls: with mTLS as a separate optional step. No silent non-loopback http:// examples remain.
  • New e2e scenario 19 for transport behavior.

Compatibility

No action required on upgrade. Existing configs behave identically, including auth.mtls.enabled: true, which still implies TLS. Binding non-loopback without TLS now prints a warning — that is the point, and the fix is one line.

Validation

gofmt, go vet, staticcheck, and go test ./... clean. e2e: 18 pass, 2 environmental skips (09 Docker, 12 1Password), including new scenario 19 and scenario 18's v0.13.5 upgrade path.

Verified separately that a real 0.0.0.0 bind both emits the banner and continues to serve — scenario 19 asserts the warning via an unbindable address, which proves the warning fires but not that the server still works, so that was checked by hand.

🤖 Generated with Claude Code

Aaron.ClaudeContainer and others added 8 commits August 19, 2026 22:02
…warning

Add a top-level "tls" config block that serves HTTPS without requiring
mTLS. Cert/key default to the internal CA server leaf written by --init,
so "tls": {"enabled": true} is a one-line fix on any initialized data
directory. auth.mtls remains the optional client-certificate layer on
top and existing mtls-only configs serve TLS exactly as before.

At startup, when server.listen binds a non-loopback address and TLS is
not enabled, log a prominent multi-line INSECURE TRANSPORT warning
naming what is exposed (bearer tokens, secret values, dashboard
cookies when enabled) and the one-line fix. Warn only — never refuse
to start. Plaintext loopback remains the silent, supported default.

New internal/transport package carries loopback detection and the
shared warning text for the server, CLI, and MCP transport.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…intext warnings

MCP Streamable HTTP mode gains --tls-cert/--tls-key flags (env:
PHOENIX_MCP_TLS_CERT/PHOENIX_MCP_TLS_KEY); the --init server leaf works
as-is. On a non-loopback bind without TLS it logs the same INSECURE
TRANSPORT warning as the server and still starts.

The CLI warns on stderr (and proceeds) when PHOENIX_SERVER is plain
http:// to a non-loopback address; the Go SDK does the same once per
distinct server URL. Loopback http:// stays silent — it is the
supported default.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Covers: silent loopback plaintext default, INSECURE TRANSPORT warning
on non-loopback binds (asserted via an unbindable TEST-NET-1 address so
no listener leaves loopback), the dashboard exposure line, tls.enabled
HTTPS with the --init leaf, mtls-only backward compatibility, CLI
warn-but-succeed posture, and MCP HTTP TLS serving plus its warning
and lone --tls-cert rejection.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
threat-model.md gains a deployment-envelope section: Phoenix is
LAN-scoped by design, internet/WAN exposure is out of scope and
unsupported; LAN is in scope but not a trust boundary; plaintext
loopback is the safe default and crossing a wire means enabling TLS.
Mitigation claims re-audited against the envelope: source-IP binding
and per-IP rate limiting are scoped to LAN-envelope addresses, DDoS
resistance is a non-goal, and the dashboard deployment options now
lead with loopback and native TLS instead of requiring mTLS.

lan-deployment.md is rewritten onto the tls: block — HTTPS is one
config line with the --init certificate, and mTLS moves to a clearly
optional step 3 (still required for cert issuance and require_mtls
policies). Client setup now shows the bearer-over-TLS variant, and the
guide documents the startup warning operators will see if they skip
TLS on a non-loopback bind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
configuration.md documents the tls block (fields, --init leaf default,
independence from mtls) and rewrites the transport-security section and
deployment table around it; the Docker note now pairs 0.0.0.0 with TLS
or a loopback port mapping.

authentication.md states up front that transport (tls block) is separate
from authentication and that no auth method requires mTLS for HTTPS;
drops a stale .home hostname from the mTLS example.

integrations.md: MCP HTTP examples bind loopback and document
--tls-cert/--tls-key; https:// examples now carry PHOENIX_CA_CERT; the
Compose example's non-loopback plaintext PHOENIX_SERVER is explicitly
annotated (single-host bridge, no published port) with a TLS variant
alongside — no silent non-loopback http:// remains.

getting-started.md sets the loopback expectation explicitly, walks LAN
setup through tls.enabled, and names the startup warning. dashboard.md
reorders transport options to loopback/native TLS/reverse proxy and
documents the warning. README.md carries the LAN-scoped deployment
envelope where evaluators see it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add PHOENIX_CA_CERT to every https:// client example (multi-agent-setup,
migration-env-to-phoenix, sealed-responses, session-identity) so the
documented enrollment story — CA cert once, https:// URL — actually
validates against the internal CA, and note in multi-agent-setup that
loopback plaintext without a CA cert is the expected single-machine
setup. Replace the two stale phoenix.home hostnames with the generic
service name used elsewhere.

Repo-wide check: the only remaining non-loopback http:// example is the
annotated single-host Compose block in integrations.md; examples/,
docker-compose.yml, and the Dockerfile are loopback-only already.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
New config surface (tls: block), new MCP TLS flags, and new warning
behavior on insecure transport. No breaking change: existing configs,
including auth.mtls.enabled, behave identically.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
staticcheck v0.8.0 requires Go >= 1.26. CI runs Go 1.25 with
GOTOOLCHAIN=local, so it cannot auto-upgrade and `@latest` fails at
install time — breaking every build regardless of the code under test.

It passed locally only because a developer shell defaults to
GOTOOLCHAIN=auto and silently fetches a newer toolchain.

Pin to v0.7.0, verified clean under Go 1.25 with GOTOOLCHAIN=local, and
bump it deliberately alongside go-version.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tick10
tick10 merged commit a4c6496 into main Aug 20, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant