diff --git a/cli/testdata/coder_server_--help.golden b/cli/testdata/coder_server_--help.golden index c83d3aa2730cf..37605fc610d78 100644 --- a/cli/testdata/coder_server_--help.golden +++ b/cli/testdata/coder_server_--help.golden @@ -717,6 +717,12 @@ that data type. How long connection log entries are retained. Set to 0 to disable (keep indefinitely). + --workspace-agent-logs-retention duration, $CODER_WORKSPACE_AGENT_LOGS_RETENTION (default: 7d) + How long workspace agent logs are retained. Logs from non-latest + builds are deleted if the agent hasn't connected within this period. + Logs from the latest build are always retained. Set to 0 to disable + automatic deletion. + TELEMETRY OPTIONS: Telemetry is critical to our ability to improve Coder. We strip all personal information before sending data to our servers. Please only disable telemetry diff --git a/cli/testdata/server-config.yaml.golden b/cli/testdata/server-config.yaml.golden index 36ab491274256..5ab70962643aa 100644 --- a/cli/testdata/server-config.yaml.golden +++ b/cli/testdata/server-config.yaml.golden @@ -761,3 +761,8 @@ retention: # an expired key. Set to 0 to disable automatic deletion of expired keys. # (default: 7d, type: duration) api_keys: 168h0m0s + # How long workspace agent logs are retained. Logs from non-latest builds are + # deleted if the agent hasn't connected within this period. Logs from the latest + # build are always retained. Set to 0 to disable automatic deletion. + # (default: 7d, type: duration) + workspace_agent_logs: 168h0m0s diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 658bda7c1e8ae..796852563f86d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -17745,6 +17745,10 @@ const docTemplate = `{ "connection_logs": { "description": "ConnectionLogs controls how long connection log entries are retained.\nSet to 0 to disable (keep indefinitely).", "type": "integer" + }, + "workspace_agent_logs": { + "description": "WorkspaceAgentLogs controls how long workspace agent logs are retained.\nLogs are deleted if the agent hasn't connected within this period.\nLogs from the latest build are always retained regardless of age.\nDefaults to 7 days to preserve existing behavior.", + "type": "integer" } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 1c3976a96b948..56b13cfa9c45f 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -16207,6 +16207,10 @@ "connection_logs": { "description": "ConnectionLogs controls how long connection log entries are retained.\nSet to 0 to disable (keep indefinitely).", "type": "integer" + }, + "workspace_agent_logs": { + "description": "WorkspaceAgentLogs controls how long workspace agent logs are retained.\nLogs are deleted if the agent hasn't connected within this period.\nLogs from the latest build are always retained regardless of age.\nDefaults to 7 days to preserve existing behavior.", + "type": "integer" } } }, diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 0ad8e7ab666a6..94473c4899e3b 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1784,9 +1784,9 @@ func (q *querier) DeleteOldTelemetryLocks(ctx context.Context, beforeTime time.T return q.db.DeleteOldTelemetryLocks(ctx, beforeTime) } -func (q *querier) DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time) error { +func (q *querier) DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time) (int64, error) { if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceSystem); err != nil { - return err + return 0, err } return q.db.DeleteOldWorkspaceAgentLogs(ctx, threshold) } diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 1075ed3bfd595..53a51edaab3eb 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -3227,7 +3227,7 @@ func (s *MethodTestSuite) TestSystemFunctions() { })) s.Run("DeleteOldWorkspaceAgentLogs", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) { t := time.Time{} - dbm.EXPECT().DeleteOldWorkspaceAgentLogs(gomock.Any(), t).Return(nil).AnyTimes() + dbm.EXPECT().DeleteOldWorkspaceAgentLogs(gomock.Any(), t).Return(int64(0), nil).AnyTimes() check.Args(t).Asserts(rbac.ResourceSystem, policy.ActionDelete) })) s.Run("InsertWorkspaceAgentStats", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) { diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go index 68cc143593709..bffa55a94d7f7 100644 --- a/coderd/database/dbmetrics/querymetrics.go +++ b/coderd/database/dbmetrics/querymetrics.go @@ -445,11 +445,11 @@ func (m queryMetricsStore) DeleteOldTelemetryLocks(ctx context.Context, periodEn return r0 } -func (m queryMetricsStore) DeleteOldWorkspaceAgentLogs(ctx context.Context, arg time.Time) error { +func (m queryMetricsStore) DeleteOldWorkspaceAgentLogs(ctx context.Context, arg time.Time) (int64, error) { start := time.Now() - r0 := m.s.DeleteOldWorkspaceAgentLogs(ctx, arg) + r0, r1 := m.s.DeleteOldWorkspaceAgentLogs(ctx, arg) m.queryLatencies.WithLabelValues("DeleteOldWorkspaceAgentLogs").Observe(time.Since(start).Seconds()) - return r0 + return r0, r1 } func (m queryMetricsStore) DeleteOldWorkspaceAgentStats(ctx context.Context) error { diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index d52c33547078d..263ea59b799a2 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -826,11 +826,12 @@ func (mr *MockStoreMockRecorder) DeleteOldTelemetryLocks(ctx, periodEndingAtBefo } // DeleteOldWorkspaceAgentLogs mocks base method. -func (m *MockStore) DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time) error { +func (m *MockStore) DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteOldWorkspaceAgentLogs", ctx, threshold) - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 } // DeleteOldWorkspaceAgentLogs indicates an expected call of DeleteOldWorkspaceAgentLogs. diff --git a/coderd/database/dbpurge/dbpurge.go b/coderd/database/dbpurge/dbpurge.go index 7f953633bf64d..8e7357d8baba1 100644 --- a/coderd/database/dbpurge/dbpurge.go +++ b/coderd/database/dbpurge/dbpurge.go @@ -18,8 +18,7 @@ import ( ) const ( - delay = 10 * time.Minute - maxAgentLogAge = 7 * 24 * time.Hour + delay = 10 * time.Minute // Connection events are now inserted into the `connection_logs` table. // We'll slowly remove old connection events from the `audit_logs` table. // The `connection_logs` table is purged based on the configured retention. @@ -66,9 +65,14 @@ func New(ctx context.Context, logger slog.Logger, db database.Store, vals *coder return nil } - deleteOldWorkspaceAgentLogsBefore := start.Add(-maxAgentLogAge) - if err := tx.DeleteOldWorkspaceAgentLogs(ctx, deleteOldWorkspaceAgentLogsBefore); err != nil { - return xerrors.Errorf("failed to delete old workspace agent logs: %w", err) + var purgedWorkspaceAgentLogs int64 + workspaceAgentLogsRetention := vals.Retention.WorkspaceAgentLogs.Value() + if workspaceAgentLogsRetention > 0 { + deleteOldWorkspaceAgentLogsBefore := start.Add(-workspaceAgentLogsRetention) + purgedWorkspaceAgentLogs, err = tx.DeleteOldWorkspaceAgentLogs(ctx, deleteOldWorkspaceAgentLogsBefore) + if err != nil { + return xerrors.Errorf("failed to delete old workspace agent logs: %w", err) + } } if err := tx.DeleteOldWorkspaceAgentStats(ctx); err != nil { return xerrors.Errorf("failed to delete old workspace agent stats: %w", err) @@ -148,6 +152,7 @@ func New(ctx context.Context, logger slog.Logger, db database.Store, vals *coder } logger.Debug(ctx, "purged old database entries", + slog.F("workspace_agent_logs", purgedWorkspaceAgentLogs), slog.F("expired_api_keys", expiredAPIKeys), slog.F("aibridge_records", purgedAIBridgeRecords), slog.F("connection_logs", purgedConnectionLogs), diff --git a/coderd/database/dbpurge/dbpurge_test.go b/coderd/database/dbpurge/dbpurge_test.go index 5b8bb35d8969f..5b8ab92a0dca5 100644 --- a/coderd/database/dbpurge/dbpurge_test.go +++ b/coderd/database/dbpurge/dbpurge_test.go @@ -246,7 +246,11 @@ func TestDeleteOldWorkspaceAgentLogs(t *testing.T) { // After dbpurge completes, the ticker is reset. Trap this call. done := awaitDoTick(ctx, t, clk) - closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{}, clk) + closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{ + Retention: codersdk.RetentionConfig{ + WorkspaceAgentLogs: serpent.Duration(7 * 24 * time.Hour), + }, + }, clk) defer closer.Close() <-done // doTick() has now run. @@ -392,6 +396,90 @@ func mustCreateAgentLogs(ctx context.Context, t *testing.T, db database.Store, a require.NotEmpty(t, agentLogs, "agent logs must be present") } +func TestDeleteOldWorkspaceAgentLogsRetention(t *testing.T) { + t.Parallel() + + now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC) + + testCases := []struct { + name string + retentionConfig codersdk.RetentionConfig + logsAge time.Duration + expectDeleted bool + }{ + { + name: "RetentionEnabled", + retentionConfig: codersdk.RetentionConfig{ + WorkspaceAgentLogs: serpent.Duration(7 * 24 * time.Hour), // 7 days + }, + logsAge: 8 * 24 * time.Hour, // 8 days ago + expectDeleted: true, + }, + { + name: "RetentionDisabled", + retentionConfig: codersdk.RetentionConfig{ + WorkspaceAgentLogs: serpent.Duration(0), + }, + logsAge: 60 * 24 * time.Hour, // 60 days ago + expectDeleted: false, + }, + + { + name: "CustomRetention30Days", + retentionConfig: codersdk.RetentionConfig{ + WorkspaceAgentLogs: serpent.Duration(30 * 24 * time.Hour), // 30 days + }, + logsAge: 31 * 24 * time.Hour, // 31 days ago + expectDeleted: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + ctx := testutil.Context(t, testutil.WaitShort) + clk := quartz.NewMock(t) + clk.Set(now).MustWait(ctx) + + oldTime := now.Add(-tc.logsAge) + + db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure()) + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}) + org := dbgen.Organization(t, db, database.Organization{}) + user := dbgen.User(t, db, database.User{}) + _ = dbgen.OrganizationMember(t, db, database.OrganizationMember{UserID: user.ID, OrganizationID: org.ID}) + tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{OrganizationID: org.ID, CreatedBy: user.ID}) + tmpl := dbgen.Template(t, db, database.Template{OrganizationID: org.ID, ActiveVersionID: tv.ID, CreatedBy: user.ID}) + + ws := dbgen.Workspace(t, db, database.WorkspaceTable{Name: "test-ws", OwnerID: user.ID, OrganizationID: org.ID, TemplateID: tmpl.ID}) + wb1 := mustCreateWorkspaceBuild(t, db, org, tv, ws.ID, oldTime, 1) + wb2 := mustCreateWorkspaceBuild(t, db, org, tv, ws.ID, oldTime, 2) + agent1 := mustCreateAgent(t, db, wb1) + agent2 := mustCreateAgent(t, db, wb2) + mustCreateAgentLogs(ctx, t, db, agent1, &oldTime, "agent 1 logs") + mustCreateAgentLogs(ctx, t, db, agent2, &oldTime, "agent 2 logs") + + // Run the purge. + done := awaitDoTick(ctx, t, clk) + closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{ + Retention: tc.retentionConfig, + }, clk) + defer closer.Close() + testutil.TryReceive(ctx, t, done) + + // Verify results. + if tc.expectDeleted { + assertNoWorkspaceAgentLogs(ctx, t, db, agent1.ID) + } else { + assertWorkspaceAgentLogs(ctx, t, db, agent1.ID, "agent 1 logs") + } + // Latest build logs are always retained. + assertWorkspaceAgentLogs(ctx, t, db, agent2.ID, "agent 2 logs") + }) + } +} + //nolint:paralleltest // It uses LockIDDBPurge. func TestDeleteOldProvisionerDaemons(t *testing.T) { // TODO: must refactor DeleteOldProvisionerDaemons to allow passing in cutoff diff --git a/coderd/database/querier.go b/coderd/database/querier.go index db748129ae4ca..22abe565a118d 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -120,10 +120,10 @@ type sqlcQuerier interface { DeleteOldProvisionerDaemons(ctx context.Context) error // Deletes old telemetry locks from the telemetry_locks table. DeleteOldTelemetryLocks(ctx context.Context, periodEndingAtBefore time.Time) error - // If an agent hasn't connected in the last 7 days, we purge it's logs. + // If an agent hasn't connected within the retention period, we purge its logs. // Exception: if the logs are related to the latest build, we keep those around. // Logs can take up a lot of space, so it's important we clean up frequently. - DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time) error + DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time) (int64, error) DeleteOldWorkspaceAgentStats(ctx context.Context) error DeleteOrganizationMember(ctx context.Context, arg DeleteOrganizationMemberParams) error DeleteProvisionerKey(ctx context.Context, id uuid.UUID) error diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index d102c3283db07..69184be52af84 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -17846,7 +17846,7 @@ func (q *sqlQuerier) UpdateVolumeResourceMonitor(ctx context.Context, arg Update return err } -const deleteOldWorkspaceAgentLogs = `-- name: DeleteOldWorkspaceAgentLogs :exec +const deleteOldWorkspaceAgentLogs = `-- name: DeleteOldWorkspaceAgentLogs :execrows WITH latest_builds AS ( SELECT @@ -17889,12 +17889,15 @@ WITH DELETE FROM workspace_agent_logs WHERE agent_id IN (SELECT id FROM old_agents) ` -// If an agent hasn't connected in the last 7 days, we purge it's logs. +// If an agent hasn't connected within the retention period, we purge its logs. // Exception: if the logs are related to the latest build, we keep those around. // Logs can take up a lot of space, so it's important we clean up frequently. -func (q *sqlQuerier) DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time) error { - _, err := q.db.ExecContext(ctx, deleteOldWorkspaceAgentLogs, threshold) - return err +func (q *sqlQuerier) DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteOldWorkspaceAgentLogs, threshold) + if err != nil { + return 0, err + } + return result.RowsAffected() } const deleteWorkspaceSubAgentByID = `-- name: DeleteWorkspaceSubAgentByID :exec diff --git a/coderd/database/queries/workspaceagents.sql b/coderd/database/queries/workspaceagents.sql index b60d1f2c88455..da6c34a761b85 100644 --- a/coderd/database/queries/workspaceagents.sql +++ b/coderd/database/queries/workspaceagents.sql @@ -199,10 +199,10 @@ INSERT INTO -- name: GetWorkspaceAgentLogSourcesByAgentIDs :many SELECT * FROM workspace_agent_log_sources WHERE workspace_agent_id = ANY(@ids :: uuid [ ]); --- If an agent hasn't connected in the last 7 days, we purge it's logs. +-- If an agent hasn't connected within the retention period, we purge its logs. -- Exception: if the logs are related to the latest build, we keep those around. -- Logs can take up a lot of space, so it's important we clean up frequently. --- name: DeleteOldWorkspaceAgentLogs :exec +-- name: DeleteOldWorkspaceAgentLogs :execrows WITH latest_builds AS ( SELECT diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 464ae7ab2b497..d44c729271ace 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -829,6 +829,11 @@ type RetentionConfig struct { // Keys are only deleted if they have been expired for at least this duration. // Defaults to 7 days to preserve existing behavior. APIKeys serpent.Duration `json:"api_keys" typescript:",notnull"` + // WorkspaceAgentLogs controls how long workspace agent logs are retained. + // Logs are deleted if the agent hasn't connected within this period. + // Logs from the latest build are always retained regardless of age. + // Defaults to 7 days to preserve existing behavior. + WorkspaceAgentLogs serpent.Duration `json:"workspace_agent_logs" typescript:",notnull"` } type NotificationsConfig struct { @@ -3420,6 +3425,17 @@ Write out the current server config as YAML to stdout.`, YAML: "api_keys", Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"), }, + { + Name: "Workspace Agent Logs Retention", + Description: "How long workspace agent logs are retained. Logs from non-latest builds are deleted if the agent hasn't connected within this period. Logs from the latest build are always retained. Set to 0 to disable automatic deletion.", + Flag: "workspace-agent-logs-retention", + Env: "CODER_WORKSPACE_AGENT_LOGS_RETENTION", + Value: &c.Retention.WorkspaceAgentLogs, + Default: "7d", + Group: &deploymentGroupRetention, + YAML: "workspace_agent_logs", + Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"), + }, { Name: "Enable Authorization Recordings", Description: "All api requests will have a header including all authorization calls made during the request. " + diff --git a/docs/admin/setup/data-retention.md b/docs/admin/setup/data-retention.md index 68409ad93f866..0dd9533565b90 100644 --- a/docs/admin/setup/data-retention.md +++ b/docs/admin/setup/data-retention.md @@ -1,8 +1,8 @@ # Data Retention Coder supports configurable retention policies that automatically purge old -Audit Logs, Connection Logs, and API keys. These policies help manage database -growth by removing records older than a specified duration. +Audit Logs, Connection Logs, Workspace Agent Logs, and API keys. These policies +help manage database growth by removing records older than a specified duration. ## Overview @@ -16,7 +16,8 @@ Retention policies help you: > [!NOTE] > Retention policies are disabled by default (set to `0`) to preserve existing -> behavior. The only exception is API keys, which defaults to 7 days. +> behavior. The exceptions are API keys and workspace agent logs, which default +> to 7 days. ## Configuration @@ -25,11 +26,12 @@ a YAML configuration file. ### Settings -| Setting | CLI Flag | Environment Variable | Default | Description | -|-----------------|-------------------------------|-----------------------------------|----------------|--------------------------------------| -| Audit Logs | `--audit-logs-retention` | `CODER_AUDIT_LOGS_RETENTION` | `0` (disabled) | How long to retain Audit Log entries | -| Connection Logs | `--connection-logs-retention` | `CODER_CONNECTION_LOGS_RETENTION` | `0` (disabled) | How long to retain Connection Logs | -| API Keys | `--api-keys-retention` | `CODER_API_KEYS_RETENTION` | `7d` | How long to retain expired API keys | +| Setting | CLI Flag | Environment Variable | Default | Description | +|----------------------|------------------------------------|----------------------------------------|----------------|-----------------------------------------| +| Audit Logs | `--audit-logs-retention` | `CODER_AUDIT_LOGS_RETENTION` | `0` (disabled) | How long to retain Audit Log entries | +| Connection Logs | `--connection-logs-retention` | `CODER_CONNECTION_LOGS_RETENTION` | `0` (disabled) | How long to retain Connection Logs | +| API Keys | `--api-keys-retention` | `CODER_API_KEYS_RETENTION` | `7d` | How long to retain expired API keys | +| Workspace Agent Logs | `--workspace-agent-logs-retention` | `CODER_WORKSPACE_AGENT_LOGS_RETENTION` | `7d` | How long to retain workspace agent logs | ### Duration Format @@ -48,7 +50,8 @@ Go duration units (`h`, `m`, `s`): coder server \ --audit-logs-retention=365d \ --connection-logs-retention=90d \ - --api-keys-retention=7d + --api-keys-retention=7d \ + --workspace-agent-logs-retention=7d ``` ### Environment Variables Example @@ -57,6 +60,7 @@ coder server \ export CODER_AUDIT_LOGS_RETENTION=365d export CODER_CONNECTION_LOGS_RETENTION=90d export CODER_API_KEYS_RETENTION=7d +export CODER_WORKSPACE_AGENT_LOGS_RETENTION=7d ``` ### YAML Configuration Example @@ -66,6 +70,7 @@ retention: audit_logs: 365d connection_logs: 90d api_keys: 7d + workspace_agent_logs: 7d ``` ## How Retention Works @@ -100,6 +105,17 @@ ago. Active keys are never deleted by the retention policy. Keeping expired keys for a short period allows Coder to return a more helpful error message when users attempt to use an expired key. +### Workspace Agent Logs Behavior + +Workspace agent logs are deleted based on when the agent last connected, not the +age of the logs themselves. **Logs from the latest build of each workspace are +always retained** regardless of when the agent last connected. This ensures you +can always debug issues with active workspaces. + +For non-latest builds, logs are deleted if the agent hasn't connected within the +retention period. Setting `--workspace-agent-logs-retention=7d` deletes logs for +agents that haven't connected in 7 days (excluding those from the latest build). + ## Best Practices ### Recommended Starting Configuration @@ -111,6 +127,7 @@ retention: audit_logs: 365d connection_logs: 90d api_keys: 7d + workspace_agent_logs: 7d ``` ### Compliance Considerations @@ -150,9 +167,10 @@ To keep data indefinitely for any data type, set its retention value to `0`: ```yaml retention: - audit_logs: 0s # Keep audit logs forever - connection_logs: 0s # Keep connection logs forever - api_keys: 0s # Keep expired API keys forever + audit_logs: 0s # Keep audit logs forever + connection_logs: 0s # Keep connection logs forever + api_keys: 0s # Keep expired API keys forever + workspace_agent_logs: 0s # Keep workspace agent logs forever ``` ## Monitoring diff --git a/docs/reference/api/general.md b/docs/reference/api/general.md index 39793443cadc3..b110c9004182c 100644 --- a/docs/reference/api/general.md +++ b/docs/reference/api/general.md @@ -466,7 +466,8 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "retention": { "api_keys": 0, "audit_logs": 0, - "connection_logs": 0 + "connection_logs": 0, + "workspace_agent_logs": 0 }, "scim_api_key": "string", "session_lifetime": { diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index f58285a584844..4e499fbae1470 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -3150,7 +3150,8 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "retention": { "api_keys": 0, "audit_logs": 0, - "connection_logs": 0 + "connection_logs": 0, + "workspace_agent_logs": 0 }, "scim_api_key": "string", "session_lifetime": { @@ -3671,7 +3672,8 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "retention": { "api_keys": 0, "audit_logs": 0, - "connection_logs": 0 + "connection_logs": 0, + "workspace_agent_logs": 0 }, "scim_api_key": "string", "session_lifetime": { @@ -7523,17 +7525,19 @@ Only certain features set these fields: - FeatureManagedAgentLimit| { "api_keys": 0, "audit_logs": 0, - "connection_logs": 0 + "connection_logs": 0, + "workspace_agent_logs": 0 } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -|-------------------|---------|----------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `api_keys` | integer | false | | Api keys controls how long expired API keys are retained before being deleted. Keys are only deleted if they have been expired for at least this duration. Defaults to 7 days to preserve existing behavior. | -| `audit_logs` | integer | false | | Audit logs controls how long audit log entries are retained. Set to 0 to disable (keep indefinitely). | -| `connection_logs` | integer | false | | Connection logs controls how long connection log entries are retained. Set to 0 to disable (keep indefinitely). | +| Name | Type | Required | Restrictions | Description | +|------------------------|---------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_keys` | integer | false | | Api keys controls how long expired API keys are retained before being deleted. Keys are only deleted if they have been expired for at least this duration. Defaults to 7 days to preserve existing behavior. | +| `audit_logs` | integer | false | | Audit logs controls how long audit log entries are retained. Set to 0 to disable (keep indefinitely). | +| `connection_logs` | integer | false | | Connection logs controls how long connection log entries are retained. Set to 0 to disable (keep indefinitely). | +| `workspace_agent_logs` | integer | false | | Workspace agent logs controls how long workspace agent logs are retained. Logs are deleted if the agent hasn't connected within this period. Logs from the latest build are always retained regardless of age. Defaults to 7 days to preserve existing behavior. | ## codersdk.Role diff --git a/docs/reference/cli/server.md b/docs/reference/cli/server.md index a173c043b0983..3f0a7550c0e64 100644 --- a/docs/reference/cli/server.md +++ b/docs/reference/cli/server.md @@ -1803,3 +1803,14 @@ How long connection log entries are retained. Set to 0 to disable (keep indefini | Default | 7d | How long expired API keys are retained before being deleted. Keeping expired keys allows the backend to return a more helpful error when a user tries to use an expired key. Set to 0 to disable automatic deletion of expired keys. + +### --workspace-agent-logs-retention + +| | | +|-------------|----------------------------------------------------| +| Type | duration | +| Environment | $CODER_WORKSPACE_AGENT_LOGS_RETENTION | +| YAML | retention.workspace_agent_logs | +| Default | 7d | + +How long workspace agent logs are retained. Logs from non-latest builds are deleted if the agent hasn't connected within this period. Logs from the latest build are always retained. Set to 0 to disable automatic deletion. diff --git a/enterprise/cli/testdata/coder_server_--help.golden b/enterprise/cli/testdata/coder_server_--help.golden index ec8f4b8cd61ff..94796825d624e 100644 --- a/enterprise/cli/testdata/coder_server_--help.golden +++ b/enterprise/cli/testdata/coder_server_--help.golden @@ -718,6 +718,12 @@ that data type. How long connection log entries are retained. Set to 0 to disable (keep indefinitely). + --workspace-agent-logs-retention duration, $CODER_WORKSPACE_AGENT_LOGS_RETENTION (default: 7d) + How long workspace agent logs are retained. Logs from non-latest + builds are deleted if the agent hasn't connected within this period. + Logs from the latest build are always retained. Set to 0 to disable + automatic deletion. + TELEMETRY OPTIONS: Telemetry is critical to our ability to improve Coder. We strip all personal information before sending data to our servers. Please only disable telemetry diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 400ad8bb79e6b..b1d8c2bb44601 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -4181,6 +4181,13 @@ export interface RetentionConfig { * Defaults to 7 days to preserve existing behavior. */ readonly api_keys: number; + /** + * WorkspaceAgentLogs controls how long workspace agent logs are retained. + * Logs are deleted if the agent hasn't connected within this period. + * Logs from the latest build are always retained regardless of age. + * Defaults to 7 days to preserve existing behavior. + */ + readonly workspace_agent_logs: number; } // From codersdk/roles.go