You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We can already block common SSH file transfer entry points with CODER_AGENT_BLOCK_FILE_TRANSFER (scp, sftp, rsync, and nc), but when transfers are allowed we don't record what moved. SFTP runs in-process, so a file pull produces a generic SSH connection-log row without any file details.
For incident response we need to answer "what files were uploaded to or downloaded from this workspace?" This belongs in the connection log, not the audit log.
Proposal
We should add per-file SFTP records, including SCP clients using SFTP, under the existing SSH connection. Each record should show the workspace path, direction, byte counts, timestamps, and result. Expose these through expandable connection-log rows and a paginated API.
Legacy SCP, rsync, and nc should get command-level classification only. That classification is not a substitute for the per-file SFTP work.
Implementation Notes
Pass the session/channel UUID from agent/agentssh/agentssh.go into transfer reporting. This is not the shared SSH transport ID or an authenticated Coder user identity.
pkg/sftp.NewServer has no per-file observer today. Add an opt-in upstream observer and pin the dependency rather than replacing the filesystem backend for logging.
Emit one terminal summary per open attempt, not per packet, including failed opens and interrupted handles. Keep read/write counters separate.
Direction is relative to the workspace. Counts are observed I/O, not unique file size; a clean close does not prove a complete copy. Record requested paths, not assumed canonical or client-local paths.
Give transfers separate IDs and batching. Existing connection-only deduplication would collapse multiple file records.
Schema Updates
Add connection_log_file_transfers, rather than storing an unbounded file list on connection_logs:
Columns
Type / purpose
connection_log_id, transfer_id
UUIDs, composite primary key; transfer ID generated by the agent
organization_id
UUID, derived by the server
protocol, path, direction
Text; direction is upload, download, or both
bytes_read, bytes_written
Nonnegative bigint counters
started_at, ended_at
Timestamptz
result, error_code
Text: closed, failed, or interrupted; nullable bounded error code
Reference connection_logs.id with cascading deletion and index (connection_log_id, started_at, transfer_id) for pagination. Add nullable transfer-method classification to SSH connection metadata without changing its connection type. Coordinate with #27412.
Considerations
Coverage
Legacy SCP, rsync, and nc invocations do not provide trustworthy per-file details. Record the recognized utility, not raw arguments or inferred filenames. Interactive-shell transfers, arbitrary network traffic, and the agent's HTTP file APIs are outside this first pass.
Ordering and delivery
Transfer batches must carry the original session envelope. In one transaction, upsert the parent using (connection_id, workspace_id, agent_name), resolve its database ID, and insert transfers idempotently. Preserve existing disconnect fields. This handles transfers arriving before the connection row and missing initial connection reports.
Delivery remains best effort. Use a separate bounded queue and batches so file events cannot starve connection reports or block file I/O. Expose dropped-event counts and mark incomplete detail where possible.
Retention
Use existing connection-log retention and authorization. Retention currently uses connection start time, even for active sessions: recent transfers on an old session share that cutoff. Test this behavior and prevent late transfer reports from recreating expired parents. The default remains unlimited retention.
Other Requirements
Keep the existing connection-log entitlement and enable/disable behavior. Do not emit audit events.
Record no contents or raw commands. Safely serialize unusual paths and escape them on display; bound payloads and flag truncation.
Negotiate reporting support with older agents/servers; unsupported logging must not break SSH or SFTP.
Preserve transfer blocking, SFTP filesystem semantics, working-directory behavior, and Windows support.
Implementation
Each step should follow red-green-refactor: add failing regression tests, implement, then simplify without changing behavior.
Extend TestAgent_SFTP, TestAgent_SCP, and TestAgent_FileTransferBlocked: both directions, multiple/empty files, directory-only sessions, bidirectional handles, normal EOF, failures, interruptions, and unchanged blocking.
Add the SFTP observer dependency and instrument agent/agentssh. Ensure transfer-producing sessions have a parent even when JetBrains session reporting would be suppressed.
Extend agent/proto, agent reporting, coderd/agentapi/connectionlog.go, and enterprise batching. Add migrations, SQLC queries and SDK types. Test ordering, retries, retention, queue saturation and mixed versions.
Add organization-scoped GET /api/v2/organizations/{organization}/connectionlog/{connectionlog}/file-transfers, authorized through the parent. Add lazy-loaded, paginated ConnectionLogRow details, behavior tests and stories. Test cross-organization denial and loading/error states.
Run generation, targeted Go/race tests, migration/RBAC/retention tests, frontend checks and frontend-review. Validate a multi-file upload/download end to end, including blocked and interrupted transfers, with no new audit entries.
Problem
We can already block common SSH file transfer entry points with
CODER_AGENT_BLOCK_FILE_TRANSFER(scp,sftp,rsync, andnc), but when transfers are allowed we don't record what moved. SFTP runs in-process, so a file pull produces a generic SSH connection-log row without any file details.For incident response we need to answer "what files were uploaded to or downloaded from this workspace?" This belongs in the connection log, not the audit log.
Proposal
We should add per-file SFTP records, including SCP clients using SFTP, under the existing SSH connection. Each record should show the workspace path, direction, byte counts, timestamps, and result. Expose these through expandable connection-log rows and a paginated API.
Legacy SCP, rsync, and nc should get command-level classification only. That classification is not a substitute for the per-file SFTP work.
Implementation Notes
agent/agentssh/agentssh.gointo transfer reporting. This is not the shared SSH transport ID or an authenticated Coder user identity.pkg/sftp.NewServerhas no per-file observer today. Add an opt-in upstream observer and pin the dependency rather than replacing the filesystem backend for logging.Schema Updates
Add
connection_log_file_transfers, rather than storing an unbounded file list onconnection_logs:connection_log_id,transfer_idorganization_idprotocol,path,directionbytes_read,bytes_writtenstarted_at,ended_atresult,error_codeReference
connection_logs.idwith cascading deletion and index(connection_log_id, started_at, transfer_id)for pagination. Add nullable transfer-method classification to SSH connection metadata without changing its connection type. Coordinate with #27412.Considerations
Coverage
Legacy SCP, rsync, and nc invocations do not provide trustworthy per-file details. Record the recognized utility, not raw arguments or inferred filenames. Interactive-shell transfers, arbitrary network traffic, and the agent's HTTP file APIs are outside this first pass.
Ordering and delivery
Transfer batches must carry the original session envelope. In one transaction, upsert the parent using
(connection_id, workspace_id, agent_name), resolve its database ID, and insert transfers idempotently. Preserve existing disconnect fields. This handles transfers arriving before the connection row and missing initial connection reports.Delivery remains best effort. Use a separate bounded queue and batches so file events cannot starve connection reports or block file I/O. Expose dropped-event counts and mark incomplete detail where possible.
Retention
Use existing connection-log retention and authorization. Retention currently uses connection start time, even for active sessions: recent transfers on an old session share that cutoff. Test this behavior and prevent late transfer reports from recreating expired parents. The default remains unlimited retention.
Other Requirements
Implementation
Each step should follow red-green-refactor: add failing regression tests, implement, then simplify without changing behavior.
TestAgent_SFTP,TestAgent_SCP, andTestAgent_FileTransferBlocked: both directions, multiple/empty files, directory-only sessions, bidirectional handles, normal EOF, failures, interruptions, and unchanged blocking.agent/agentssh. Ensure transfer-producing sessions have a parent even when JetBrains session reporting would be suppressed.agent/proto, agent reporting,coderd/agentapi/connectionlog.go, and enterprise batching. Add migrations, SQLC queries and SDK types. Test ordering, retries, retention, queue saturation and mixed versions.GET /api/v2/organizations/{organization}/connectionlog/{connectionlog}/file-transfers, authorized through the parent. Add lazy-loaded, paginatedConnectionLogRowdetails, behavior tests and stories. Test cross-organization denial and loading/error states.frontend-review. Validate a multi-file upload/download end to end, including blocked and interrupted transfers, with no new audit entries.