Local-First Audit Events
1. Purpose and Scope
Cosmosh records security-core actions into a local-first audit stream so operators can trace sensitive operations without shipping data to external services.
Current scope covers high-value operational events:
- SSH connection lifecycle (
ssh-session) - Host fingerprint trust actions (
ssh-host-trust) - SSH server and keychain entity mutations (
ssh-server,ssh-keychain) - SSH port forwarding rule and runtime actions (
port-forward) - Settings mutation events (
settings)
Compatibility note:
- Existing
SshLoginAuditis still retained for SSH last-used sorting and legacy flows. - New
AuditEventis the canonical event log for cross-domain security auditing.
2. Data Model
Audit persistence is implemented in backend Prisma schema:
AuditEvent: immutable event records with time-ordered IDs and query indexesAuditSyncCursor: reserved local state for future remote-sync checkpoints
Core fields in AuditEvent:
- Event identity:
eventId,occurredAt,createdAt - Semantics:
category,action,outcome,severity - Scope:
scopeAccountId,scopeDeviceId - Correlation:
entityType,entityId,sessionId,requestId,correlationId,relatedRecordId - Metadata:
metadataJson - Retention:
retentionUntilAt
3. Write Path and Failure Model
AuditEventService.logEvent(...) is the single backend write entry point.
flowchart LR A[Route or Runtime Action] --> B[AuditEventService.logEvent] B --> C[sanitizeAuditMetadata] C --> D[Prisma auditEvent.create] D --> E[(SQLite AuditEvent)]
Write behavior constraints:
- Redaction first: metadata keys like
password,token,privateKey,passphrase,secretare replaced by a placeholder. - Size cap: metadata JSON is truncated to a fixed upper bound (default 8 KB) to avoid runaway payload growth.
- Best effort: write failures are logged server-side and never break the parent request/session action.
- Retention sweep: service performs interval cleanup for expired rows (default retention: 180 days, sweep every 6 hours).
4. Query Contract
Audit APIs are exposed by backend routes and API contract types:
GET /api/v1/audit/events- Filters: category, action, outcome, severity, entityType, entityId, sessionId, requestId, keyword, time range
- Pagination:
page,pageSize
GET /api/v1/audit/events/{eventId}- Returns full record including parsed metadata
Standard result codes:
AUDIT_EVENT_LIST_OKAUDIT_EVENT_DETAIL_OKAUDIT_VALIDATION_FAILEDAUDIT_EVENT_NOT_FOUND
5. Electron Bridge and Renderer Integration
Main/preload/renderer chain exposes two channels:
backend:audit-list-eventsbackend:audit-get-event-by-id
Renderer integration points:
AuditLogspage (packages/renderer/src/pages/AuditLogs.tsx)- Tab route id:
audit-logs - Header quick entry opens the audit tab directly
The page uses a dense split layout:
- Left: filter controls
- Center: paginated event list
- Right: selected event details with formatted metadata
6. Security and Privacy Constraints
- Raw secrets and credential payloads are never persisted in audit metadata.
- Correlation IDs (
requestId,sessionId,relatedRecordId) are kept to support forensic traceability without exposing secret values. - Audit storage remains local-first by default; sync-cursor model is pre-provisioned for future optional synchronization.