Skip to content

db

View on pkg.go.dev

import "github.com/thesimonho/warden/db"

Package db provides Warden’s central persistence layer backed by SQLite.

It stores three kinds of data:

  • **Projects**: container configuration (name, image, mounts, env vars, etc.)
  • **Settings**: key-value pairs (runtime, auditLogMode, disconnectKey)
  • **Events**: centralized audit log from agent hooks, backend, frontend, containers

The database lives at ~/.config/warden/warden.db (platform-dependent). All methods on Store are safe for concurrent use.

Re-export constants so existing db.SourceAgent, db.LevelInfo, etc. still work.

const (
SourceAgent = api.AuditSourceAgent
SourceBackend = api.AuditSourceBackend
SourceFrontend = api.AuditSourceFrontend
SourceContainer = api.AuditSourceContainer
SourceExternal = api.AuditSourceExternal
LevelInfo = api.AuditLevelInfo
LevelWarn = api.AuditLevelWarn
LevelError = api.AuditLevelError
)

DefaultQueryLimit is applied when QueryFilters.Limit is zero.

const DefaultQueryLimit = 10_000

AccessItemRow represents a user-created access item stored in the database. Built-in items (Git, SSH) are not stored — they come from the access package.

type AccessItemRow struct {
// ID is the unique identifier (UUID for user items).
ID string
// Label is the human-readable display name.
Label string
// Description explains what this access item provides.
Description string
// Method is the delivery strategy (only "transport" for now).
Method string
// Credentials is JSON-encoded []access.Credential.
Credentials json.RawMessage
}

AuditMode controls which events are persisted to the audit log.

type AuditMode string

const (
// AuditOff disables all audit logging. Nothing is written.
AuditOff AuditMode = "off"
// AuditStandard logs core operational events: session lifecycle,
// terminal lifecycle, worktree lifecycle, budget, and system events.
AuditStandard AuditMode = "standard"
// AuditDetailed logs everything in standard plus agent tool use,
// permissions, subagents, config changes, user prompts, and debug
// events (auto-captured backend warnings/errors via slog tee).
AuditDetailed AuditMode = "detailed"
)

AuditSummaryRow holds aggregate audit data from a summary query.

type AuditSummaryRow struct {
TotalSessions int
TotalToolUses int
TotalPrompts int
UniqueProjects int
UniqueWorktrees int
Earliest string
Latest string
}

AuditWriter gates audit log writes based on the current audit mode. All audit writes across the codebase go through this single entry point. Thread-safe for concurrent reads (mode) and writes.

type AuditWriter struct {
// contains filtered or unexported fields
}

func NewAuditWriter(store *Store, mode AuditMode, standardEvents map[string]bool) *AuditWriter

NewAuditWriter creates a writer that persists entries to the given store, filtered by the initial audit mode. The standardEvents set defines which events are logged in standard mode — events not in this set are only logged in detailed mode. Pass AuditOff as mode to start silent.

func (w *AuditWriter) Mode() AuditMode

Mode returns the current audit mode.

func (w *AuditWriter) SetMode(mode AuditMode)

SetMode updates the audit mode at runtime. Takes effect immediately for subsequent Write calls.

func (w *AuditWriter) Write(entry Entry)

Write persists an audit entry if the current mode allows it. Drops silently when mode is off or the event is not in the current mode’s allowlist. Logs a warning on DB write failure so callers don’t need error handling.

Type aliases for backward compatibility. Canonical definitions live in the api package; these aliases let existing db.Entry / db.Source / db.Level references compile without changes throughout the codebase.

type Entry = api.AuditEntry

Type aliases for backward compatibility. Canonical definitions live in the api package; these aliases let existing db.Entry / db.Source / db.Level references compile without changes throughout the codebase.

type Level = api.AuditLevel

OffsetStoreAdapter wraps a Store to implement watcher.OffsetStore. Lives in the db package to avoid the watcher package importing db.

type OffsetStoreAdapter struct {
Store *Store
}

func (a *OffsetStoreAdapter) DeleteOffset(projectID, agentType, filePath string) error

DeleteOffset implements watcher.OffsetStore.

func (a *OffsetStoreAdapter) DeleteOffsets(projectID, agentType string) error

DeleteOffsets implements watcher.OffsetStore.

func (a *OffsetStoreAdapter) LoadOffset(projectID, agentType, filePath string) (int64, error)

LoadOffset implements watcher.OffsetStore.

func (a *OffsetStoreAdapter) SaveOffset(projectID, agentType, filePath string, offset int64) error

SaveOffset implements watcher.OffsetStore.

ProjectAgentKey uniquely identifies a project+agent pair. Used as a map key where the compound (project_id, agent_type) identity is needed.

type ProjectAgentKey struct {
ProjectID string
AgentType string
}

ProjectCostRow holds aggregated cost data for a project from the DB.

type ProjectCostRow struct {
TotalCost float64
IsEstimated bool
}

ProjectRow represents a project stored in the database.

Complex fields (EnvVars, Mounts, OriginalMounts) are stored as opaque JSON. The service layer handles marshaling to/from engine types.

type ProjectRow struct {
// ProjectID is the deterministic identifier (sha256 of host path, 12 hex chars).
ProjectID string
// Name is the user-chosen display label / Docker container name.
Name string
// HostPath is the absolute host directory mounted into the container (local projects only).
HostPath string
// CloneURL is the git repository URL to clone (remote projects only).
CloneURL string
// Temporary is true when a remote project's workspace is ephemeral.
Temporary bool
// AddedAt is when the project was added to Warden.
AddedAt time.Time
// Image is the container image name.
Image string
// EnvVars is JSON-encoded map[string]string of user-provided env vars.
EnvVars json.RawMessage
// Mounts is JSON-encoded []Mount of additional bind mounts.
Mounts json.RawMessage
// OriginalMounts is JSON-encoded []Mount of pre-symlink-resolution mounts.
OriginalMounts json.RawMessage
// SkipPermissions controls whether terminals skip permission prompts.
SkipPermissions bool
// NetworkMode is the container's network isolation level (full/restricted/none).
NetworkMode string
// AllowedDomains is comma-separated domains for restricted mode.
AllowedDomains string
// CostBudget is the per-project cost limit in USD (0 = use global default).
CostBudget float64
// EnabledAccessItems is a comma-separated list of enabled access item IDs (e.g. "git,ssh").
EnabledAccessItems string
// EnabledRuntimes is a comma-separated list of enabled runtime IDs (e.g. "node,python,go").
EnabledRuntimes string
// ForwardedPorts is a comma-separated list of forwarded port numbers (e.g. "5173,3000").
ForwardedPorts string
// AgentType identifies the CLI agent running in this project (e.g. "claude-code", "codex").
AgentType string
// ContainerID is the Docker-assigned container ID (empty when no container exists).
ContainerID string
// ContainerName is the Docker container name (may differ from Name if renamed).
ContainerName string
}

QueryFilters controls which entries are returned by Store.Query.

Zero-value fields are ignored (no filtering). Limit defaults to DefaultQueryLimit when zero.

type QueryFilters struct {
// Source restricts results to a single source layer.
Source Source
// Level restricts results to a single severity level.
Level Level
// ProjectID restricts results to a single project by its deterministic ID.
ProjectID string
// Worktree restricts results to a single worktree ID.
Worktree string
// Event restricts results to a single event type identifier.
Event string
// Events restricts results to any of the listed event types (OR).
Events []string
// ExcludeEvents excludes entries whose event type is in this list (NOT IN).
// Mutually exclusive with Events; if both are set, Events takes precedence.
ExcludeEvents []string
// Since returns only entries with a timestamp at or after this time.
Since time.Time
// Until returns only entries with a timestamp strictly before this time.
Until time.Time
// Limit caps the number of returned entries. Defaults to DefaultQueryLimit if zero.
Limit int
// Offset skips this many entries before returning results.
Offset int
}

SessionCostRow holds cost data for a single session.

type SessionCostRow struct {
SessionID string
Cost float64
IsEstimated bool
CreatedAt string
UpdatedAt string
}

SlogHandler is a slog.Handler that writes backend log records to the audit log. It wraps a delegate handler so logs still go to their original destination (stderr).

type SlogHandler struct {
// contains filtered or unexported fields
}

func NewSlogHandler(delegate slog.Handler, writer *AuditWriter) *SlogHandler

NewSlogHandler creates a handler that writes to both the delegate and the audit log. If writer is nil, only the delegate receives records.

func (h *SlogHandler) Enabled(ctx context.Context, level slog.Level) bool

Enabled reports whether the handler handles records at the given level.

func (h *SlogHandler) Handle(ctx context.Context, record slog.Record) error

Handle writes the record to the delegate and, if the writer is non-nil, also writes a backend entry to the audit log. Only warnings and errors are written — INFO-level traffic is too noisy. These events have auto-generated snake_case names and are classified as debug category (detailed mode only) by the AuditWriter’s standardEvents allowlist.

func (h *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs returns a new handler with the given attributes pre-set.

func (h *SlogHandler) WithGroup(name string) slog.Handler

WithGroup returns a new handler with the given group name.

Type aliases for backward compatibility. Canonical definitions live in the api package; these aliases let existing db.Entry / db.Source / db.Level references compile without changes throughout the codebase.

type Source = api.AuditSource

Store writes structured audit log entries to a SQLite database.

All methods are safe for concurrent use. A nil Store is a valid no-op: all methods return immediately without error.

type Store struct {
// contains filtered or unexported fields
}

func New(dir string) (*Store, error)

New creates a Store backed by a SQLite database at dir/warden.db. The directory is created if it does not exist. Pass the config directory root (e.g. ~/.config/warden/) — the database file is created inside it.

func (l *Store) Clear() error

Clear removes all audit log entries.

func (l *Store) Close() error

Close closes the underlying database connection.

func (l *Store) Delete(filters QueryFilters) (int64, error)

Delete removes events matching the given filters. With no filters set, this is equivalent to [Clear]. Supports scoping by project, worktree, and time range (since/until).

func (l *Store) DeleteAccessItem(id string) error

DeleteAccessItem removes a user-created access item by ID.

func (l *Store) DeleteProject(projectID, agentType string) error

DeleteProject removes a project from the projects table. Audit events and cost data are intentionally retained so the audit page can show historical data.

func (l *Store) DeleteProjectCosts(projectID, agentType string) error

DeleteProjectCosts removes all cost entries for a project+agent pair.

func (l *Store) DeleteSessionCosts(projectID string, since, until time.Time) error

DeleteSessionCosts removes session cost entries matching the given filters. Supports scoping by project ID and time range. With no filters, clears all session costs.

func (l *Store) DeleteTailerOffset(projectID, agentType, filePath string) error

DeleteTailerOffset removes the stored offset for a single file.

func (l *Store) DeleteTailerOffsets(projectID, agentType string) error

DeleteTailerOffsets removes all stored offsets for a project+agent pair.

func (l *Store) DistinctProjectIDs() ([]string, error)

DistinctProjectIDs returns sorted, unique project IDs that have at least one event logged. Empty project IDs are excluded.

func (l *Store) GetAccessItem(id string) (*AccessItemRow, error)

GetAccessItem returns a user-created access item by ID, or nil if not found.

func (l *Store) GetAccessItemsByIDs(ids []string) ([]AccessItemRow, error)

GetAccessItemsByIDs returns user-created access items matching the given IDs. IDs not found are silently skipped.

func (l *Store) GetAllProjectTotalCosts() (map[ProjectAgentKey]ProjectCostRow, error)

GetAllProjectTotalCosts returns cumulative costs for all project+agent pairs by summing across all sessions.

func (l *Store) GetCostInTimeRange(projectID string, since, until time.Time) (ProjectCostRow, error)

GetCostInTimeRange returns the total cost for sessions that overlap with the given time range. A session overlaps if it was created before the range ends and last updated after the range starts. Pass zero-value times to leave either bound open.

func (l *Store) GetProject(projectID, agentType string) (*ProjectRow, error)

GetProject returns a project by its compound key, or nil if not found.

func (l *Store) GetProjectByContainerName(containerName string) (*ProjectRow, error)

GetProjectByContainerName returns a project by its Docker container name, or nil if not found.

func (l *Store) GetProjectTotalCost(projectID, agentType string) (ProjectCostRow, error)

GetProjectTotalCost returns the cumulative cost for a single project+agent pair.

func (l *Store) GetProjectsByPath(hostPath string) ([]*ProjectRow, error)

GetProjectsByPath returns all projects at a host path (one per agent type).

func (l *Store) GetSetting(key, defaultValue string) string

GetSetting returns the value for a settings key, or the provided default if the key does not exist.

func (l *Store) HasProject(projectID, agentType string) (bool, error)

HasProject reports whether a project with the given compound key exists.

func (l *Store) InsertAccessItem(item AccessItemRow) error

InsertAccessItem adds a user-created access item to the database.

func (l *Store) InsertProject(p ProjectRow) error

InsertProject adds a project to the database. If a project with the same project ID already exists, it is replaced (upsert).

func (l *Store) ListAccessItems() ([]AccessItemRow, error)

ListAccessItems returns all user-created access items.

func (l *Store) ListAllProjects() ([]*ProjectRow, error)

ListAllProjects returns all projects as a flat slice ordered by insertion time.

func (l *Store) ListProjectKeys() ([]ProjectAgentKey, error)

ListProjectKeys returns all project+agent pairs in insertion order.

func (l *Store) ListSessionCosts(projectID, agentType string) ([]SessionCostRow, error)

ListSessionCosts returns per-session cost data for a project+agent pair.

func (l *Store) LoadTailerOffset(projectID, agentType, filePath string) (int64, error)

LoadTailerOffset returns the stored byte offset for a tailed file. Returns 0 if no offset is stored.

func (l *Store) Query(filters QueryFilters) ([]Entry, error)

Query returns entries matching the given filters, sorted by timestamp (newest first). Zero-value filter fields are ignored.

func (l *Store) QueryAuditSummary(filters QueryFilters) (*AuditSummaryRow, error)

QueryAuditSummary returns aggregate audit statistics matching the given filters. Only ProjectID, Worktree, Since, and Until fields are used for filtering.

func (l *Store) QueryTopTools(filters QueryFilters, limit int) ([]ToolCountRow, error)

QueryTopTools returns the most frequently used tools from tool_use events. Only ProjectID, Worktree, Since, and Until fields are used for filtering.

func (l *Store) Read() ([]Entry, error)

Read returns all entries sorted by timestamp (newest first). This is a convenience wrapper around [Query] with empty filters.

func (l *Store) SaveTailerOffset(projectID, agentType, filePath string, offset int64) error

SaveTailerOffset persists the byte offset for a tailed file (upsert).

func (l *Store) SetSetting(key, value string) error

SetSetting writes a key-value pair to the settings table (upsert).

func (l *Store) UpdateAccessItem(item AccessItemRow) error

UpdateAccessItem updates a user-created access item.

func (l *Store) UpdateProjectContainer(projectID, agentType, containerID, containerName string) error

UpdateProjectContainer updates the container ID and name for a project. Used after container creation, rebuild, or deletion.

func (l *Store) UpdateProjectSettings(projectID, agentType, name, containerName string, skipPermissions bool, costBudget float64, allowedDomains, forwardedPorts string) error

UpdateProjectSettings updates lightweight project settings that do not require container recreation (name, skip_permissions, cost_budget, container_name, allowed_domains, forwarded_ports). All other fields remain unchanged.

func (l *Store) UpsertSessionCost(projectID, agentType, sessionID string, cost float64, isEstimated bool) error

UpsertSessionCost persists cost for a specific agent session. Cost for a given session ID is monotonically non-decreasing, so upserting is always safe — we simply take the max of old and new values. Project total cost is computed as SUM(cost) across all sessions for a project.

func (l *Store) Write(entry Entry) error

Write inserts an entry into the database.

ToolCountRow pairs a tool name with its invocation count.

type ToolCountRow struct {
Name string
Count int
}

Generated by gomarkdoc