Skip to content

Userspace TLS logs ~16 debug lines per handshake on the default egress path (no logger passed to makeTLSClient) #478

Description

@moooyo

Heads-up for the maintainer: this issue was investigated and filed by Claude Code (Anthropic's coding agent), running on @moooyo's machine and posting from their account. Everything below was verified directly against the running image and the vendored @reclaimprotocol/tls, not inferred. Happy to adjust or close if I've misread something.

Summary

makeTLSClient(...) is called without a logger in two places, so @reclaimprotocol/tls falls back to its module-level default logger — whose info/debug/warn/error are bare console.* passthroughs with no level gating. Every userspace-TLS handshake therefore writes ~16 lines to the server's stdout.

Because an unset proxy fallback list resolves to DIRECT_CONNECT_ID (packages/gateway/src/dial/fetcher.ts), this is the default egress path — a stock self-hosted deployment gets this output without opting into anything.

Where

Call sites (neither passes logger):

  • packages/http/src/tls.ts — the tlsOptions object literal
  • packages/proxy/src/protocols/reality.ts — the makeTLSClient({ ... }) call

Library side, @reclaimprotocol/tls@0.1.2:

  • lib/types/tls.d.ts:102 — logger?: Logger is a supported option
  • lib/make-tls-client.js:20 — const logger = _logger || LOGGER
  • lib/utils/logger.js — the default LOGGER maps info/debug/warn/error to console.*; only trace is a no-op

DEBUG_USERSPACE_TLS doesn't gate this — it guards only Floway's own teardown line (logTlsTeardownError in packages/http/src/tls.ts).

Sample output

One handshake, verbatim from the container log (timestamps stripped):

{
  cipherSuite: 'TLS_AES_128_GCM_SHA256',
  connTlsVersion: 'TLS1_3',
  selectedAlpn: undefined
} processed server hello
{ keyType: 'SECP256R1' } computed shared keys
received change cipher spec
{ len: 16, extData: {} } received encrypted extensions
{ len: 3 } parsed certificates
verified certificate chain
{ len: 516 } received certificate verify
{ alg: 'RSA_PSS_RSAE_SHA256' } parsed certificate verify
received server finish
server finish verified
{ len: 57 } received session ticket
{ len: 57 } received session ticket

These interleave with the request log, which makes docker logs hard to read for the ~17% of lines that are actually about requests.

Measured impact

From one instance serving Codex Desktop POST /azure-api.codex/responses:

metric value
handshakes observed 301
total log lines 5,796
lines attributable to the TLS debug output 4,866 (83%)
≈ lines per handshake 16
container log growth at 7–10 req/min 30 MB/day
extrapolated at 30–60 req/min 90–180 MB/day

Compounding it slightly: docker/docker-compose.yml sets no logging: options, so with Docker's default json-file driver the log grows unbounded. That's trivial for an operator to cap (and I have), but the default combination — verbose-by-default handshake logging plus unrotated json-file — can fill a small VM's disk over time.

Suggested fix

Pass an explicit logger at both call sites, e.g.

logger: { info() {}, debug() {}, trace() {}, warn: console.warn, error: console.error },

keeping warn/error so genuine failures still surface.

If the handshake detail is worth keeping for debugging, gating it behind the existing DEBUG_USERSPACE_TLS flag would match the pattern already used for teardown errors, and would keep the default quiet.

Happy to open a PR if that'd be useful — just let me know which shape you'd prefer.

Environment

  • Floway 56dddc6d (image tag 20260816.31959732430, ghcr.io/menci/floway-server)
  • Node v22.23.1, linux/arm64
  • @reclaimprotocol/tls 0.1.2 (in both packages/http and packages/proxy)
  • Deployed with docker compose, direct egress (no fallback egress configured)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions