CodeHerderSearch⌘KRequest access →

Self-hosted log retention and access

Set retention and reader permissions for each self-hosted log class, audit receiver, device log and host-administration session record.

Your organization owns log storage, retention and readers. CodeHerder states each log class and the control it offers. Where CodeHerder has no control, you set it in the system that holds the log. For log formats and shipping, see Self-hosted logs.

Log classes

Log class What it holds Data class Who should read it Retention control
Server journald (codeherder.service) Application logs. Errors can quote repo URLs and branch names. customer_code Security team and platform operators None in CodeHerder. Set it in journald and in your log store.
API request line (in server journald) One line for each request: method, path, route, status, duration and request ID. The path can hold workspace and task slugs. It holds no client address. confidential Security team and platform operators None in CodeHerder. Set it in journald and in your log store.
Reverse proxy (Caddy) One line for each proxied request, plus certificate and error logs. It holds the client address. personal_data Security team and platform operators None in CodeHerder. Set it in journald and in your log store.
Host sign-in and kernel audit sshd, sudo, PAM and kernel audit records. They hold user names and addresses. personal_data Security team only None in CodeHerder. Set it in journald and in your log store.
Database logs (PostgreSQL or RDS) Server, connection and slow-query logs. A slow statement logs its bind values, so the log can hold any column, secrets included. The hosted parameter group logs statements slower than one second. secret_material Database administrators and security team None in CodeHerder. Set it in PostgreSQL or in your RDS parameter group and CloudWatch log group.
Instance audit (admin_audit_log, plane = 'instance') Operator actions on the instance plane, with the operator’s email and address. personal_data Security team only None in CodeHerder. A self-hosted server never prunes this table. Set a delete or archive job in PostgreSQL.
Sign-in audit (auth_audit_log) Rejected and accepted credentials, counted for each minute, with the client address. personal_data Security team only None in CodeHerder. A self-hosted server never prunes this table. Set a delete or archive job in PostgreSQL.
Workspace events (events) The activity record of each workspace. confidential Workspace admins, for their own workspace CH_EVENTS_MAX_AGE_DAYS. Default 180. 0 disables the prune. CH_EVENTS_METRICS_RETENTION_DAYS covers device metrics events. Default 7.
Cost events (cost_events) AI spend records. internal Workspace admins and finance CH_COST_EVENTS_MAX_AGE_DAYS. Default 90. 0 disables the prune. CH_COST_RAW_RETENTION_DAYS prunes raw rows after the rollup reconciles. Default 90. 0 disables it.
Audit trace export Trace spans and webhook records sent to your receivers. The exported records hold identifiers only. The local span buffer also holds span attributes and events. confidential Security team None in CodeHerder. Set it at each receiver. See the audit receivers section of the self-hosted log retention guide. The local span buffer (audit_span_open) keeps spans for CH_OTEL_SPAN_MAX_AGE. Default 48 hours.
Webhook delivery records One row for each outbound and inbound webhook delivery. confidential Workspace admins, for their own workspace CodeHerder prunes both tables nightly after 30 days (runWebhookDeliveriesRetentionSweeper, runInboundWebhookDeliveriesRetentionSweeper).

The data class is the strictest data the log may hold. Treat the log with the handling rule of that class. A slow PostgreSQL statement logs its bind values, so the database log can hold secrets. Restrict it the most.

The infrastructure classes have no application control. These are the server journald, API request line, reverse proxy, host sign-in, database logs and audit receivers. The two audit tables have none either.

This is template rule text. Every value is a proposal. Your organization sets the final values.

  • The security team reads infrastructure logs and instance audit records. No other group reads them.
  • Workspace admins read the events and cost records of their own workspaces.
  • Keep security logs off the host for at least one year. Use storage that the host role cannot delete.
  • Keep the workspace events and cost events settings at their defaults, unless a policy needs more.
  • Give each log store a named owner. Review the reader list every quarter.
  • Delete a log at the end of its retention period. Record the deletion.

Audit receivers

You may run two kinds of audit receiver.

  1. An OTLP collector. Set CH_OTEL_TRACES_ENDPOINT to send trace spans. The trace ID is the task UUID.
  2. A webhook receiver. See Webhooks.

The exported records hold identifiers only: UUIDs, event type, timestamps, and workspace (tenant) IDs. They hold no titles, prompts or session text. A webhook in the default metadata_only mode sends the event ID, type, workspace ID, subject type and ID, the acting member ID, and the time. Its payload is null.

You own each receiver. Do these things at each one:

  1. Set a retention period.
  2. Restrict readers to the security team.
  3. Keep metadata_only on every audit webhook. The full mode adds event details. If you choose it, apply the same controls to those details.
  4. Separate tenants if your policy needs it. The records carry workspace IDs, so you can filter or route by workspace.
  5. Alert when a receiver stops accepting records.

Device logs

The people who run a device are responsible for its logs. CodeHerder does not collect them centrally.

A device holds these logs:

  • ~/.codeherder/logs/device-server.log. CodeHerder copies it to one .1 file and truncates it when it passes CH_LOCAL_LOG_BUDGET_MB. Default 8.
  • ~/.codeherder/launches/<launchID>/. CodeHerder removes this folder after the run ends.
  • Service-manager copies: the systemd user journal (journalctl --user -u codeherder-device-server), the launchd file /tmp/codeherder-device-server.log, and Docker container logs. See Running the device server.

CodeHerder caps the size of these logs. It sets no age limit. No central secret scrubber runs on them.

The device operator must do these things:

  1. Restrict the folder to the device user. For example, run chmod 700 ~/.codeherder.
  2. Set an age limit. Use journald MaxRetentionSec, logrotate, or a scheduled delete for the launchd file. For Docker, use --log-opt max-size and max-file.
  3. Do not ship device logs to a shared store without your own scrubber.
  4. Delete the logs when you retire the device.

Accepted residual risk

A secret that a tool prints can reach a device log in clear text. Access and age settings limit who sees it and for how long. They cannot stop the write. Your organization accepts this risk under the decision that device operators own device logs.

Host administration sessions

Log and retain every host-administration session off the host. Use this rule for whichever channel you pick: SSM, an SSH bastion, or a serial console.

The record must show who connected, when, and what they ran. Send it to storage that the host’s own role cannot delete. Review it on a schedule.

Example: AWS Systems Manager

Set these Session Manager preferences in the SSM-SessionManagerRunShell document:

  • cloudWatchLogGroupName: a log group with a retention period.
  • cloudWatchEncryptionEnabled: true.
  • cloudWatchStreamingEnabled: true, to stream the session as it runs.
  • s3BucketName and s3EncryptionEnabled: true. Use a bucket in a separate account, with Object Lock or a deny-delete policy.
  • kmsKeyId: a key that encrypts the session data.
  • runAsEnabled: as your policy needs, so sessions run as a named OS user.

CloudTrail records StartSession and SendCommand. Send Run Command output to S3 or CloudWatch. Use OutputS3BucketName and CloudWatchOutputConfig.

Port-forwarding and SSH-over-SSM sessions log no keystrokes. Log the SSH server on the host as well.

Your emergency host access path needs the same logging.

Last updated

CodeHerder

Round up your herd.

Bring every human and every agent onto one table. Watch the work move. Costs update as it happens.

Try "pricing", "connect a device", or "who reviews the code"

↑↓ move · ↵ open · esc close