Skip to main content

Audit logs

Streams

StreamStorageRetentionAccess
PostHog api_request eventsPostHog cloudPer project configWorkspace members
Stripe events tablePostgres stripe_eventsPermanentDB readers
CloudWatch / stdout logsCloudWatch (or stdout)30 days (default)AWS IAM principals

What's captured

AuditLogMiddleware enqueues a row per request with:

  • requestId
  • userId, userRole, orgId (if authenticated)
  • method, path, statusCode
  • result (success / failure), errorCode
  • duration_ms
  • userAgent, ip
  • timestamp

No bodies, no query params, no headers beyond User-Agent and X-Forwarded-For.

What's NOT captured

  • Request body — could contain passwords, OAuth codes, etc.
  • Response body — could contain PII.
  • Query string — could contain ?token=.
  • Authorization header — obviously.

If you need to add a sensitive field deliberately, gate it behind an env flag and redact in serialization.

Sensitive event sources

Some operations should produce richer audit rows than the default middleware:

OperationRecommended audit pattern
Admin set-rolelog targetUserId, oldRole, newRole
Admin ban-userlog targetUserId, reason
Agent status changelog agentId, oldStatus, newStatus
Subscription cancellationlog subscriptionId, tierName
Tier quota changelog tierId, metricSlug, oldQuota, newQuota

Use AuditLoggerService.log({ event, properties }) from services rather than relying on the HTTP-level middleware.

Access control

  • PostHog: gate via PostHog's own RBAC. Limit access to security + ops.
  • DB: anyone with READ on the marketplace DB can read stripe_events. Consider a read-only role and rotate which IAM principals can assume it.
  • CloudWatch: IAM-controlled.

Retention

StreamDefaultRecommended
PostHog eventsper project1 year for compliance, longer for forensics if needed
stripe_eventsforeverOK as-is
audit_logs tableunusedIf you start writing here, 1-year retention with archival to S3 Glacier
CloudWatch30 daysBump to 1 year if you don't have a separate forwarder

Investigation playbook

QuestionWhere to look
"Who deleted this agent?"PostHog path = '/agents/<id>' AND method = 'DELETE'
"Why did this user's checkout fail?"PostHog userId = <id> AND path ~ 'checkout'; then cross to stripe_events
"What endpoints did this admin call yesterday?"PostHog userId = <admin> time range
"Was there a 5xx spike?"PostHog result = failure AND statusCode >= 500 time chart

Adding alerts

In PostHog, set up:

  • 5xx error rate alert.
  • errorCode = 'QUOTA_EXCEEDED' rate alert (signals tier limits too tight).
  • Specific path latency alert (e.g. /me/agents/.*/consume > 200ms).

Pipe alerts to PagerDuty / Slack / your tool of choice.

Compliance angle

The audit log:

  • Demonstrates user activity for SOC 2 access review.
  • Supports incident forensics.
  • Provides evidence for billing disputes (combined with stripe_events).

It does NOT:

  • Cover agent-side runtime activity (that's the agent's own audit log).
  • Cover infrastructure-level events (CloudTrail does that).