Skip to content

feat: correlate coder CLI workspace sessions with client_session_id - #28313

Merged
aqandrew merged 14 commits into
mainfrom
devex-664-ssh-client-session-id
Sep 2, 2026
Merged

aqandrew merged 14 commits into
mainfrom
devex-664-ssh-client-session-id

Conversation

@aqandrew

@aqandrew aqandrew commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements DEVEX-664: the coder CLI commands that open a workspace connection now generate a client session ID and attach it to their requests, logs, and existing network telemetry, per the connection-log RFC.

Commands opt in explicitly with a client_session_id annotation. The opted-in set is coder ssh, coder start, coder stop, and coder update. Long-running daemon commands (coder server, agent, provisionerd, and so on) deliberately stay out, so they never carry a meaningless session ID in their logs, request baggage, or telemetry.

  • Generate / resolve: a 16-byte lowercase-hex session ID is generated per opted-in invocation. When CODER_TRACE_SESSION_ID is set (so Toolbox and the VS Code plugin can correlate the CLI they spawn), that value is used verbatim; a warning is logged if it is not the canonical 32-char lowercase hex form, since coderd/agent middleware drop non-canonical values.
  • Requests: the ID is attached to every request as client_session_id W3C baggage via a transport wrapper, independent of whether tracing is enabled. The already-merged coderd tracing middleware (feat(coderd/tracing): correlate request logs and spans by client_session_id #27671) reads it.
  • Coordinate websocket: the ID is also set explicitly as baggage on the tailnet coordinate handshake in workspacesdk.DialAgent, so coderd's /api/v2/workspaceagents/{id}/coordinate endpoint logs it (it does not rely on the CLI transport for that dial).
  • Logs: the ID is added to the command's log context.
  • Existing telemetry: the ID is added to tailnet TelemetryEvent (client_session_id, new field 20; proto minor version bumped) and plumbed from the CLI through workspacesdk.DialAgent.

Scope is the coder CLI client only (RFC glossary + requirement 5), and within the CLI only the opted-in workspace-connecting commands above. This PR is independent of #28039 (the agent middleware that consumes the baggage); it depends only on tracing.SessionIDBaggageKey, which is already on main via #27671.

Out of scope

RFC requirements 7-16 (connection-state change logging, connection_logs columns, log buffering, routing/disconnect-reason logs, coordination-protocol plumbing, agentssh/reconnectingpty command logging, support-bundle log capture) and requirements 4.2/4.3 (VS Code telemetry, Prometheus stats) are separate tickets. IDE plugins, Toolbox, and the daemon commands are also out of scope.

Testing

  • go test ./coderd/tracing/... ./cli/... ./tailnet/... ./codersdk/workspacesdk/... (unit tests for the generator, the env/generate resolver, per-command opt-in, the baggage transport wrapper, the coordinate-dial baggage helper, and the telemetry field).
  • go build ./..., go vet, and golangci-lint pass on the touched packages.
  • make tailnet/proto/tailnet.pb.go regenerated the proto; the proto minor version was bumped and postStartup now connects at the current agent API version so TestWorkspaceAgent_Startup/OK tracks it.

Note: the pre-commit enterprise/cli/.gen-golden step could not run in this environment (no Postgres: open postgres connection: EOF). cli/testdata/.gen-golden passed; this change adds no serpent flags, so there is no CLI help/golden drift.

Implementation plan

Scope mapping (RFC -> DEVEX-664)

In scope (the CLI workspace-connecting subset of RFC phase 2):

  • Req 1: generate a 16-byte session ID as 32-char lowercase hex.
  • Req 2: add the session ID to every log the command makes for the session.
  • Req 3: attach the session ID to every API request via client_session_id baggage, including the coordinate websocket handshake. (The query-param fallback is browser-only; not applicable to the CLI.)
  • Req 4.1: attach the session ID to CLI network telemetry.
  • Req 5 / 5.3: support CODER_TRACE_SESSION_ID; when set, use it instead of generating.

Resolved decisions

  • Scope: opt-in per command via a client_session_id annotation; opted-in set is coder ssh, coder start, coder stop, coder update. Daemon commands are excluded.
  • CODER_TRACE_SESSION_ID: when set, use the value verbatim; warn if it is not canonical 32-char lowercase hex. When unset, generate a fresh ID.
  • Existing telemetry = CLI network telemetry (tailnet TelemetryEvent); requires a new proto field + plumbing and a proto minor version bump.
  • Coordinate endpoint: set the session ID as baggage on the coordinate handshake explicitly in DialAgent (header, not query param) so it is logged server-side without relying on the CLI transport.
  • Shared helper: add tracing.NewSessionID() and export the validator as tracing.ValidSessionID for a single source of truth.

Implementation

  1. coderd/tracing: add NewSessionID() (16 random bytes, hex-encoded); export ValidSessionID.
  2. cli: resolve the ID from CODER_TRACE_SESSION_ID or generate; attach client_session_id baggage to every request via wrapTransportWithSessionIDHeader, gated on the annotationClientSessionID opt-in; add it to the command log context.
  3. tailnet + codersdk/workspacesdk: add client_session_id to TelemetryEvent, an Options.ClientSessionID/Conn.clientSessionID set in newTelemetryEvent, a DialAgentOptions.ClientSessionID plumbed from the opted-in commands, and the coordinate-handshake baggage in DialAgent.

This PR was created by Coder Agents on behalf of @aqandrew.

Generate a 16-byte lowercase-hex client session ID for the ssh
sub-command (honoring CODER_TRACE_SESSION_ID when set) and attach it to
every request as client_session_id W3C baggage, to the command's log
context, and to CLI network telemetry, per the connection-log RFC.

- tracing: add NewSessionID and export ValidSessionID.
- codersdk/cli: attach client_session_id baggage to all ssh requests via
  a transport wrapper.
- cli: resolve the ID from CODER_TRACE_SESSION_ID or generate one; add it
  to the ssh log context.
- tailnet: add client_session_id to TelemetryEvent and plumb it from the
  CLI through workspacesdk DialAgent.
@linear-code

linear-code Bot commented Aug 19, 2026

Copy link
Copy Markdown

DEVEX-664

@aqandrew aqandrew changed the title feat(cli): correlate coder ssh sessions with client_session_id feat: correlate coder CLI ssh sessions with client_session_id Aug 19, 2026
@aqandrew
aqandrew marked this pull request as ready for review August 20, 2026 03:31
Comment thread cli/ssh.go Outdated
Comment thread tailnet/proto/tailnet.proto
Comment thread codersdk/workspacesdk/workspacesdk.go
Resolve the client session ID once per invocation in a global serpent
middleware installed via cmd.Walk, instead of only in coder ssh. The
middleware stores the ID on the invocation context both as a slog field
(so any log written with the invocation context carries client_session_id
regardless of which logger emits it) and under a private key for non-log
consumers.

createHTTPClient now reads the ID from the invocation context to attach
W3C baggage, and ssh reads it from the context to forward as tailnet
telemetry. This removes the RootCmd.clientSessionID shared-state field.

Based on Spike's review on #28313.

Generated by Coder Agents.
Long-running daemon commands (coder server, agent, provisionerd, etc.)
should not carry a client session ID: it is meaningless for them and
prepending client_session_id to their logs broke log-volume and
log-format expectations (TestServer/SpammyLogs, TestServer/Prometheus).

Gate clientSessionIDMiddleware on a new annotationClientSessionID opt-in
annotation. Commands that establish a client session opt in: coder ssh,
start, stop, and update. Daemons never opt in, so their logs, request
baggage, and telemetry stay free of a session ID.

Generated by Coder Agents.
Bumping the shared tailnet/agent API to 2.12 (for the client_session_id
TelemetryEvent field) made agentproto.CurrentVersion 2.12, but
postStartup connected via the version-pinned ConnectRPC211WithRole, so
TestWorkspaceAgent_Startup/OK recorded APIVersion 2.11 and failed the
equality assertion. Connect at proto.CurrentVersion so the recorded
version tracks future bumps.

Generated by Coder Agents.
Set the client session ID as W3C baggage on the coordinate websocket
handshake headers in DialAgent, mirroring how the session token is
attached. coderd's tracing middleware reads client_session_id from
baggage, so the /api/v2/workspaceagents/{id}/coordinate request is now
logged with the session ID.

Setting it explicitly at the dial site (rather than relying on the CLI's
baggage-injecting transport) makes propagation intentional and works for
non-CLI callers that construct a plain codersdk client. Prefer the header
over a query param since a Go client can set headers and a query param
would spread the ID into request URLs and access logs across hops.
@aqandrew
aqandrew requested a review from spikecurtis August 26, 2026 22:45
@aqandrew aqandrew changed the title feat: correlate coder CLI ssh sessions with client_session_id feat: correlate coder CLI workspace sessions with client_session_id Aug 27, 2026
Agent HTTP API calls go through a separate per-request HTTP client that
does not run the CLI's baggage transport, so client_session_id never
reached the agent. Attach it to the agent connection's extra headers in
DialAgent (when ClientSessionID is set) so every agent API request
carries the baggage and the agent's tracing middleware logs it.

Extract clientSessionIDBaggage as the shared source of the baggage value
for both the coordinate handshake and the agent extra headers, and add a
unit test for it.

@code-asher code-asher left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! Just had some tiny nits about comments and a couple test file names.

Comment thread cli/root.go Outdated
Comment thread cli/session_id_internal_test.go Outdated
Comment thread cli/ssh.go Outdated
Comment thread coderd/tracing/httpmw.go Outdated
Comment thread coderd/tracing/httpmw.go Outdated
Comment thread tailnet/telemetry_session_id_internal_test.go Outdated
@aqandrew
aqandrew enabled auto-merge (squash) September 2, 2026 19:17
@aqandrew
aqandrew merged commit 3aeb7f8 into main Sep 2, 2026
28 checks passed
@aqandrew
aqandrew deleted the devex-664-ssh-client-session-id branch September 2, 2026 19:31
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants