Problem
The connection log records which Coder user opened a workspace app or forwarded
a port (user_id is populated for workspace_app / port_forwarding rows),
but it cannot attribute SSH / VS Code / JetBrains sessions to a user. Those
rows are reported by the agent via ReportConnection, and the agent does not
know which Coder user is on the other end of the tunnel —
coderd/agentapi/connectionlog.go sets UserID: uuid.NullUUID{Valid: false}
with the comment:
It's not possible to tell which user connected. Once we have the capability,
this may be reported by the agent.
For enterprise auditors this leaves a gap: the connection log can answer "who
opened the web terminal on workspace X" but not "who SSH'd into workspace X".
Where the identity is available
Every SSH/IDE session rides a tailnet tunnel that the client opens via
GET /api/v2/workspaceagents/{id}/coordinate (used by coder ssh,
coder port-forward, the VS Code extension, etc. — anything that calls
workspacesdk.DialAgent). That request is authenticated with the user's API
key, so at tunnel-establishment time coderd has user_id, api_key_id,
client IP (via the ExtractRealIP middleware) and User-Agent.
Proposal
Write a connection-log row from workspaceAgentClientCoordinate when the
WebSocket is accepted, under a new connection_type value (working name
tailnet). The row carries user_id, ip, user_agent, workspace_id,
agent_name and renders via WebInfo so the connecting user appears in the
dashboard Connection Log. PR: #27005.
Open questions
- Naming.
tailnet vs tunnel — happy to rename.
api_key_id. connection_logs has no column for it today. Would it be
reasonable to add one in the same migration so these rows (and potentially
workspace_app / port_forwarding rows) can also record which key was used?
- Debounce. The workspace-app path debounces via
workspace_app_audit_sessions; this proposal currently writes one row per
handshake. If reconnect volume is a concern the same debounce could be
applied here.
/api/v2/tailnet. Coder Desktop uses the user-scoped tailnet endpoint
instead. It has no single target workspace, so it doesn't map cleanly onto
connection_logs; left out of the initial PR.
Problem
The connection log records which Coder user opened a workspace app or forwarded
a port (
user_idis populated forworkspace_app/port_forwardingrows),but it cannot attribute SSH / VS Code / JetBrains sessions to a user. Those
rows are reported by the agent via
ReportConnection, and the agent does notknow which Coder user is on the other end of the tunnel —
coderd/agentapi/connectionlog.gosetsUserID: uuid.NullUUID{Valid: false}with the comment:
For enterprise auditors this leaves a gap: the connection log can answer "who
opened the web terminal on workspace X" but not "who SSH'd into workspace X".
Where the identity is available
Every SSH/IDE session rides a tailnet tunnel that the client opens via
GET /api/v2/workspaceagents/{id}/coordinate(used bycoder ssh,coder port-forward, the VS Code extension, etc. — anything that callsworkspacesdk.DialAgent). That request is authenticated with the user's APIkey, so at tunnel-establishment time coderd has
user_id,api_key_id,client IP (via the
ExtractRealIPmiddleware) andUser-Agent.Proposal
Write a connection-log row from
workspaceAgentClientCoordinatewhen theWebSocket is accepted, under a new
connection_typevalue (working nametailnet). The row carriesuser_id,ip,user_agent,workspace_id,agent_nameand renders viaWebInfoso the connecting user appears in thedashboard Connection Log. PR: #27005.
Open questions
tailnetvstunnel— happy to rename.api_key_id.connection_logshas no column for it today. Would it bereasonable to add one in the same migration so these rows (and potentially
workspace_app/port_forwardingrows) can also record which key was used?workspace_app_audit_sessions; this proposal currently writes one row perhandshake. If reconnect volume is a concern the same debounce could be
applied here.
/api/v2/tailnet. Coder Desktop uses the user-scoped tailnet endpointinstead. It has no single target workspace, so it doesn't map cleanly onto
connection_logs; left out of the initial PR.