Skip to main content
Second stores durable audit events in MongoDB so owners and admins can answer what changed in a workspace without reading prompts, source files, secrets, or raw app data. Audit logs are different from the existing realtime systems:
  • Workspace events are Redis invalidation hints for mounted UI.
  • Builder and app-agent streams are ordered user-visible run streams.
  • Audit events are append-only, queryable workspace records.

Permissions

Workspace-wide audit logs require audit:read. Owners and admins have this permission by default; members do not. Audit reads always go through requireWorkspaceContext, then check audit:read, then query audit_events with workspaceId. Looking up an audit event by ID also includes workspaceId, so an event from another workspace returns 404. Exports are not part of the open-source v1 implementation, so there is no audit:export permission yet.

Event shape

Every audit event records: The schema is intentionally close to OpenTelemetry-style log fields, but Second keeps a first-party store so local and on-prem deployments work without an external SIEM.

What is recorded

Open-source v1 records the core governance and platform events: The audit settings page includes an event-explanation catalog with the same coverage map. It also marks enterprise-later rows such as generated-app SDK business events, exports, SIEM export, live audit streaming, advanced analytics, saved views, legal hold, and retention controls.

Redaction

Audit metadata is sanitized before storage. The recorder strips control characters, caps metadata depth/size, truncates long strings and arrays, and redacts sensitive keys. Never store these in audit events:
  • source files, compiled artifacts, sourcemaps
  • prompts, assistant messages, reasoning, full transcripts
  • full app documents or full app-data documents
  • integration secret values, OAuth tokens, API keys, cookies, sessions
  • raw request headers, request bodies, or provider responses
  • internal tokens, connection strings, private keys
For app data writes, audit events store collection name, document ID, operation, source version, changed field names, and before/after hashes. They do not store the document body. For custom tools, audit events store the tool name, integration name/domain, mock-vs-live mode, outcome class, and HTTP status. They do not store headers, secret-injected URLs, request bodies, or response bodies. For OAuth, audit events store safe facts such as provider key, provider config ID, integration ID, connected account ID, requested tool name, outcome, and scope names. They do not store authorization codes, OAuth client secrets, refresh tokens, access tokens, Vault references, token endpoint request bodies, provider response bodies, email bodies, calendar event bodies, or request headers.

Performance model

Audit history is not loaded on app lists, sidebar navigation, settings shells, chat routes, or access checks. The audit settings page uses bounded read APIs and manual refresh. Open-source v1 does not add an audit EventSource route. If live audit streaming is added later, it should use compact projections and must still authorize the connection with audit:read. Builder chat and app-agent streaming paths use nonblocking audit writes where waiting on audit storage would make stream startup or completion feel slower. The durable run/app mutation remains the source of truth if an audit insert fails.

Tenant isolation

Every stored event has workspaceId. Every audit read includes workspaceId. Target IDs are never used alone for lookup. Browser-origin data is never trusted for actor, workspace, app, or source trust. The server sets those fields from authenticated workspace context or from validated internal worker context.