Feature Description
Introduce a structured audit log that records security-relevant actions performed in the platform: who did what, when, against which target, with what outcome. The default implementation writes to a dedicated PostgreSQL table. The recording API is the same regardless of how the underlying storage is implemented, so future work (immutable storage, external SIEM forwarding) can replace the backend without changing callsites.
Problem/Use Case
Logtide is used to monitor production systems, often by teams with compliance obligations. Today there is no record of administrative actions inside Logtide itself: who created or rotated an API key, who modified a Sigma rule, who accessed a specific log range, who deleted a project. Operators asking "did anyone change this rule recently?" have no answer.
Beyond compliance, an audit log is a good debugging tool: many "weird state" issues are explained by "someone changed a setting last Thursday".
Proposed Solution
A single recording API:
auditLog.record({
action: AuditAction,
actor: { type: 'user' | 'apiKey' | 'system'; id: string },
target: { type: string; id: string },
outcome: 'success' | 'failure',
metadata?: Record<string, unknown>,
})
The canonical action namespace (initial set, lives in one file):
org.created, org.updated, org.deleted
project.created, project.updated, project.deleted
apikey.created, apikey.rotated, apikey.revoked
user.invited, user.removed, user.role_changed
rule.created, rule.updated, rule.deleted
pipeline.created, pipeline.updated, pipeline.deleted
dashboard.created, dashboard.updated, dashboard.deleted
auth.login_succeeded, auth.login_failed, auth.session_revoked
data.exported, data.deleted
Actions are recorded synchronously inside the same transaction as the action itself, where possible. Where not (e.g. login attempts that don't have a transaction), they're recorded asynchronously with at-least-once semantics.
A dashboard view shows the audit log filtered by org, action type, actor, time range — basically a pre-built saved search over the audit table.
Alternatives Considered
- Use Logtide's own log ingestion as the audit log. Tempting, but problematic: it mixes audit data with application logs, makes retention policies awkward (audit usually needs longer retention than logs), and creates a circular dependency where the audit of the system is stored in the system.
- External audit log only (forward to SIEM). Doesn't help operators without an external SIEM, and they're a minority of Logtide users.
- Skip until requested by a paying customer. Adding audit logging retroactively means going through every mutation path and inserting calls. Doing it once at v1.0 is cheaper.
Implementation Details (Optional)
- Schema:
audit_events table with columns id, organization_id, actor_type, actor_id, action, target_type, target_id, outcome, metadata (JSONB), created_at. Indexes on (organization_id, created_at) and (organization_id, action, created_at).
- Recording uses the current
RequestContext to populate organization_id and (where available) actor. Callsites only specify the action and target.
- Action names are a TypeScript string literal union — typos fail to compile.
- The audit log is append-only by convention in v1.0 — no UI to delete entries. True immutable storage (WORM) is a downstream concern, gated behind the
audit.immutable capability and tackled separately.
- Retention is configurable but defaults to no automatic deletion. Operators with strict GDPR requirements can configure a retention window per org.
- A simple
GET /api/audit-events endpoint with pagination and filters. No exotic query language needed.
Priority
Target Users
- Teams with compliance requirements (SOC 2, ISO 27001, GDPR) needing a record of administrative actions
- Operators debugging "who changed this setting" issues
- Multi-user organizations wanting accountability and visibility into team actions
Contribution
Feature Description
Introduce a structured audit log that records security-relevant actions performed in the platform: who did what, when, against which target, with what outcome. The default implementation writes to a dedicated PostgreSQL table. The recording API is the same regardless of how the underlying storage is implemented, so future work (immutable storage, external SIEM forwarding) can replace the backend without changing callsites.
Problem/Use Case
Logtide is used to monitor production systems, often by teams with compliance obligations. Today there is no record of administrative actions inside Logtide itself: who created or rotated an API key, who modified a Sigma rule, who accessed a specific log range, who deleted a project. Operators asking "did anyone change this rule recently?" have no answer.
Beyond compliance, an audit log is a good debugging tool: many "weird state" issues are explained by "someone changed a setting last Thursday".
Proposed Solution
A single recording API:
The canonical action namespace (initial set, lives in one file):
org.created,org.updated,org.deletedproject.created,project.updated,project.deletedapikey.created,apikey.rotated,apikey.revokeduser.invited,user.removed,user.role_changedrule.created,rule.updated,rule.deletedpipeline.created,pipeline.updated,pipeline.deleteddashboard.created,dashboard.updated,dashboard.deletedauth.login_succeeded,auth.login_failed,auth.session_revokeddata.exported,data.deletedActions are recorded synchronously inside the same transaction as the action itself, where possible. Where not (e.g. login attempts that don't have a transaction), they're recorded asynchronously with at-least-once semantics.
A dashboard view shows the audit log filtered by org, action type, actor, time range — basically a pre-built saved search over the audit table.
Alternatives Considered
Implementation Details (Optional)
audit_eventstable with columnsid,organization_id,actor_type,actor_id,action,target_type,target_id,outcome,metadata(JSONB),created_at. Indexes on (organization_id,created_at) and (organization_id,action,created_at).RequestContextto populateorganization_idand (where available)actor. Callsites only specify the action and target.audit.immutablecapability and tackled separately.GET /api/audit-eventsendpoint with pagination and filters. No exotic query language needed.Priority
Target Users
Contribution