db
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.
- Constants
- type AccessItemRow
- type AuditMode
- type AuditSummaryRow
- type AuditWriter
- type Entry
- type Level
- type OffsetStoreAdapter
- func (a *OffsetStoreAdapter) DeleteOffset(projectID, agentType, filePath string) error
- func (a *OffsetStoreAdapter) DeleteOffsets(projectID, agentType string) error
- func (a *OffsetStoreAdapter) LoadOffset(projectID, agentType, filePath string) (int64, error)
- func (a *OffsetStoreAdapter) SaveOffset(projectID, agentType, filePath string, offset int64) error
- type ProjectAgentKey
- type ProjectCostRow
- type ProjectRow
- type QueryFilters
- type SessionCostRow
- type SlogHandler
- func NewSlogHandler(delegate slog.Handler, writer *AuditWriter) *SlogHandler
- func (h *SlogHandler) Enabled(ctx context.Context, level slog.Level) bool
- func (h *SlogHandler) Handle(ctx context.Context, record slog.Record) error
- func (h *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler
- func (h *SlogHandler) WithGroup(name string) slog.Handler
- type Source
- type Store
- func New(dir string) (*Store, error)
- func (l *Store) Clear() error
- func (l *Store) Close() error
- func (l *Store) Delete(filters QueryFilters) (int64, error)
- func (l *Store) DeleteAccessItem(id string) error
- func (l *Store) DeleteProject(projectID, agentType string) error
- func (l *Store) DeleteProjectCosts(projectID, agentType string) error
- func (l *Store) DeleteSessionCosts(projectID string, since, until time.Time) error
- func (l *Store) DeleteTailerOffset(projectID, agentType, filePath string) error
- func (l *Store) DeleteTailerOffsets(projectID, agentType string) error
- func (l *Store) DistinctProjectIDs() ([]string, error)
- func (l *Store) GetAccessItem(id string) (*AccessItemRow, error)
- func (l *Store) GetAccessItemsByIDs(ids []string) ([]AccessItemRow, error)
- func (l *Store) GetAllProjectTotalCosts() (map[ProjectAgentKey]ProjectCostRow, error)
- func (l *Store) GetCostInTimeRange(projectID string, since, until time.Time) (ProjectCostRow, error)
- func (l *Store) GetProject(projectID, agentType string) (*ProjectRow, error)
- func (l *Store) GetProjectByContainerName(containerName string) (*ProjectRow, error)
- func (l *Store) GetProjectTotalCost(projectID, agentType string) (ProjectCostRow, error)
- func (l *Store) GetProjectsByPath(hostPath string) ([]*ProjectRow, error)
- func (l *Store) GetSetting(key, defaultValue string) string
- func (l *Store) HasProject(projectID, agentType string) (bool, error)
- func (l *Store) InsertAccessItem(item AccessItemRow) error
- func (l *Store) InsertProject(p ProjectRow) error
- func (l *Store) ListAccessItems() ([]AccessItemRow, error)
- func (l *Store) ListAllProjects() ([]*ProjectRow, error)
- func (l *Store) ListProjectKeys() ([]ProjectAgentKey, error)
- func (l *Store) ListSessionCosts(projectID, agentType string) ([]SessionCostRow, error)
- func (l *Store) LoadTailerOffset(projectID, agentType, filePath string) (int64, error)
- func (l *Store) Query(filters QueryFilters) ([]Entry, error)
- func (l *Store) QueryAuditSummary(filters QueryFilters) (*AuditSummaryRow, error)
- func (l *Store) QueryTopTools(filters QueryFilters, limit int) ([]ToolCountRow, error)
- func (l *Store) Read() ([]Entry, error)
- func (l *Store) SaveTailerOffset(projectID, agentType, filePath string, offset int64) error
- func (l *Store) SetSetting(key, value string) error
- func (l *Store) UpdateAccessItem(item AccessItemRow) error
- func (l *Store) UpdateProjectContainer(projectID, agentType, containerID, containerName string) error
- func (l *Store) UpdateProjectSettings(projectID, agentType, name, containerName string, skipPermissions bool, costBudget float64, allowedDomains, forwardedPorts string) error
- func (l *Store) UpsertSessionCost(projectID, agentType, sessionID string, cost float64, isEstimated bool) error
- func (l *Store) Write(entry Entry) error
- type ToolCountRow
Constants
Section titled “Constants”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_000type AccessItemRow
Section titled “type AccessItemRow”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}type AuditMode
Section titled “type AuditMode”AuditMode controls which events are persisted to the audit log.
type AuditMode stringconst ( // 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")type AuditSummaryRow
Section titled “type AuditSummaryRow”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}type AuditWriter
Section titled “type AuditWriter”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
Section titled “func NewAuditWriter”func NewAuditWriter(store *Store, mode AuditMode, standardEvents map[string]bool) *AuditWriterNewAuditWriter 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 (*AuditWriter) Mode
Section titled “func (*AuditWriter) Mode”func (w *AuditWriter) Mode() AuditModeMode returns the current audit mode.
func (*AuditWriter) SetMode
Section titled “func (*AuditWriter) SetMode”func (w *AuditWriter) SetMode(mode AuditMode)SetMode updates the audit mode at runtime. Takes effect immediately for subsequent Write calls.
func (*AuditWriter) Write
Section titled “func (*AuditWriter) Write”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 Entry
Section titled “type Entry”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.AuditEntrytype Level
Section titled “type Level”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.AuditLeveltype OffsetStoreAdapter
Section titled “type OffsetStoreAdapter”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 (*OffsetStoreAdapter) DeleteOffset
Section titled “func (*OffsetStoreAdapter) DeleteOffset”func (a *OffsetStoreAdapter) DeleteOffset(projectID, agentType, filePath string) errorDeleteOffset implements watcher.OffsetStore.
func (*OffsetStoreAdapter) DeleteOffsets
Section titled “func (*OffsetStoreAdapter) DeleteOffsets”func (a *OffsetStoreAdapter) DeleteOffsets(projectID, agentType string) errorDeleteOffsets implements watcher.OffsetStore.
func (*OffsetStoreAdapter) LoadOffset
Section titled “func (*OffsetStoreAdapter) LoadOffset”func (a *OffsetStoreAdapter) LoadOffset(projectID, agentType, filePath string) (int64, error)LoadOffset implements watcher.OffsetStore.
func (*OffsetStoreAdapter) SaveOffset
Section titled “func (*OffsetStoreAdapter) SaveOffset”func (a *OffsetStoreAdapter) SaveOffset(projectID, agentType, filePath string, offset int64) errorSaveOffset implements watcher.OffsetStore.
type ProjectAgentKey
Section titled “type ProjectAgentKey”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}type ProjectCostRow
Section titled “type ProjectCostRow”ProjectCostRow holds aggregated cost data for a project from the DB.
type ProjectCostRow struct { TotalCost float64 IsEstimated bool}type ProjectRow
Section titled “type ProjectRow”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}type QueryFilters
Section titled “type QueryFilters”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}type SessionCostRow
Section titled “type SessionCostRow”SessionCostRow holds cost data for a single session.
type SessionCostRow struct { SessionID string Cost float64 IsEstimated bool CreatedAt string UpdatedAt string}type SlogHandler
Section titled “type SlogHandler”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
Section titled “func NewSlogHandler”func NewSlogHandler(delegate slog.Handler, writer *AuditWriter) *SlogHandlerNewSlogHandler creates a handler that writes to both the delegate and the audit log. If writer is nil, only the delegate receives records.
func (*SlogHandler) Enabled
Section titled “func (*SlogHandler) Enabled”func (h *SlogHandler) Enabled(ctx context.Context, level slog.Level) boolEnabled reports whether the handler handles records at the given level.
func (*SlogHandler) Handle
Section titled “func (*SlogHandler) Handle”func (h *SlogHandler) Handle(ctx context.Context, record slog.Record) errorHandle 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 (*SlogHandler) WithAttrs
Section titled “func (*SlogHandler) WithAttrs”func (h *SlogHandler) WithAttrs(attrs []slog.Attr) slog.HandlerWithAttrs returns a new handler with the given attributes pre-set.
func (*SlogHandler) WithGroup
Section titled “func (*SlogHandler) WithGroup”func (h *SlogHandler) WithGroup(name string) slog.HandlerWithGroup returns a new handler with the given group name.
type Source
Section titled “type Source”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.AuditSourcetype Store
Section titled “type Store”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
Section titled “func New”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 (*Store) Clear
Section titled “func (*Store) Clear”func (l *Store) Clear() errorClear removes all audit log entries.
func (*Store) Close
Section titled “func (*Store) Close”func (l *Store) Close() errorClose closes the underlying database connection.
func (*Store) Delete
Section titled “func (*Store) Delete”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 (*Store) DeleteAccessItem
Section titled “func (*Store) DeleteAccessItem”func (l *Store) DeleteAccessItem(id string) errorDeleteAccessItem removes a user-created access item by ID.
func (*Store) DeleteProject
Section titled “func (*Store) DeleteProject”func (l *Store) DeleteProject(projectID, agentType string) errorDeleteProject removes a project from the projects table. Audit events and cost data are intentionally retained so the audit page can show historical data.
func (*Store) DeleteProjectCosts
Section titled “func (*Store) DeleteProjectCosts”func (l *Store) DeleteProjectCosts(projectID, agentType string) errorDeleteProjectCosts removes all cost entries for a project+agent pair.
func (*Store) DeleteSessionCosts
Section titled “func (*Store) DeleteSessionCosts”func (l *Store) DeleteSessionCosts(projectID string, since, until time.Time) errorDeleteSessionCosts removes session cost entries matching the given filters. Supports scoping by project ID and time range. With no filters, clears all session costs.
func (*Store) DeleteTailerOffset
Section titled “func (*Store) DeleteTailerOffset”func (l *Store) DeleteTailerOffset(projectID, agentType, filePath string) errorDeleteTailerOffset removes the stored offset for a single file.
func (*Store) DeleteTailerOffsets
Section titled “func (*Store) DeleteTailerOffsets”func (l *Store) DeleteTailerOffsets(projectID, agentType string) errorDeleteTailerOffsets removes all stored offsets for a project+agent pair.
func (*Store) DistinctProjectIDs
Section titled “func (*Store) DistinctProjectIDs”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 (*Store) GetAccessItem
Section titled “func (*Store) GetAccessItem”func (l *Store) GetAccessItem(id string) (*AccessItemRow, error)GetAccessItem returns a user-created access item by ID, or nil if not found.
func (*Store) GetAccessItemsByIDs
Section titled “func (*Store) GetAccessItemsByIDs”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 (*Store) GetAllProjectTotalCosts
Section titled “func (*Store) GetAllProjectTotalCosts”func (l *Store) GetAllProjectTotalCosts() (map[ProjectAgentKey]ProjectCostRow, error)GetAllProjectTotalCosts returns cumulative costs for all project+agent pairs by summing across all sessions.
func (*Store) GetCostInTimeRange
Section titled “func (*Store) GetCostInTimeRange”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 (*Store) GetProject
Section titled “func (*Store) GetProject”func (l *Store) GetProject(projectID, agentType string) (*ProjectRow, error)GetProject returns a project by its compound key, or nil if not found.
func (*Store) GetProjectByContainerName
Section titled “func (*Store) GetProjectByContainerName”func (l *Store) GetProjectByContainerName(containerName string) (*ProjectRow, error)GetProjectByContainerName returns a project by its Docker container name, or nil if not found.
func (*Store) GetProjectTotalCost
Section titled “func (*Store) GetProjectTotalCost”func (l *Store) GetProjectTotalCost(projectID, agentType string) (ProjectCostRow, error)GetProjectTotalCost returns the cumulative cost for a single project+agent pair.
func (*Store) GetProjectsByPath
Section titled “func (*Store) GetProjectsByPath”func (l *Store) GetProjectsByPath(hostPath string) ([]*ProjectRow, error)GetProjectsByPath returns all projects at a host path (one per agent type).
func (*Store) GetSetting
Section titled “func (*Store) GetSetting”func (l *Store) GetSetting(key, defaultValue string) stringGetSetting returns the value for a settings key, or the provided default if the key does not exist.
func (*Store) HasProject
Section titled “func (*Store) HasProject”func (l *Store) HasProject(projectID, agentType string) (bool, error)HasProject reports whether a project with the given compound key exists.
func (*Store) InsertAccessItem
Section titled “func (*Store) InsertAccessItem”func (l *Store) InsertAccessItem(item AccessItemRow) errorInsertAccessItem adds a user-created access item to the database.
func (*Store) InsertProject
Section titled “func (*Store) InsertProject”func (l *Store) InsertProject(p ProjectRow) errorInsertProject adds a project to the database. If a project with the same project ID already exists, it is replaced (upsert).
func (*Store) ListAccessItems
Section titled “func (*Store) ListAccessItems”func (l *Store) ListAccessItems() ([]AccessItemRow, error)ListAccessItems returns all user-created access items.
func (*Store) ListAllProjects
Section titled “func (*Store) ListAllProjects”func (l *Store) ListAllProjects() ([]*ProjectRow, error)ListAllProjects returns all projects as a flat slice ordered by insertion time.
func (*Store) ListProjectKeys
Section titled “func (*Store) ListProjectKeys”func (l *Store) ListProjectKeys() ([]ProjectAgentKey, error)ListProjectKeys returns all project+agent pairs in insertion order.
func (*Store) ListSessionCosts
Section titled “func (*Store) ListSessionCosts”func (l *Store) ListSessionCosts(projectID, agentType string) ([]SessionCostRow, error)ListSessionCosts returns per-session cost data for a project+agent pair.
func (*Store) LoadTailerOffset
Section titled “func (*Store) LoadTailerOffset”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 (*Store) Query
Section titled “func (*Store) Query”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 (*Store) QueryAuditSummary
Section titled “func (*Store) QueryAuditSummary”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 (*Store) QueryTopTools
Section titled “func (*Store) QueryTopTools”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 (*Store) Read
Section titled “func (*Store) Read”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 (*Store) SaveTailerOffset
Section titled “func (*Store) SaveTailerOffset”func (l *Store) SaveTailerOffset(projectID, agentType, filePath string, offset int64) errorSaveTailerOffset persists the byte offset for a tailed file (upsert).
func (*Store) SetSetting
Section titled “func (*Store) SetSetting”func (l *Store) SetSetting(key, value string) errorSetSetting writes a key-value pair to the settings table (upsert).
func (*Store) UpdateAccessItem
Section titled “func (*Store) UpdateAccessItem”func (l *Store) UpdateAccessItem(item AccessItemRow) errorUpdateAccessItem updates a user-created access item.
func (*Store) UpdateProjectContainer
Section titled “func (*Store) UpdateProjectContainer”func (l *Store) UpdateProjectContainer(projectID, agentType, containerID, containerName string) errorUpdateProjectContainer updates the container ID and name for a project. Used after container creation, rebuild, or deletion.
func (*Store) UpdateProjectSettings
Section titled “func (*Store) UpdateProjectSettings”func (l *Store) UpdateProjectSettings(projectID, agentType, name, containerName string, skipPermissions bool, costBudget float64, allowedDomains, forwardedPorts string) errorUpdateProjectSettings 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 (*Store) UpsertSessionCost
Section titled “func (*Store) UpsertSessionCost”func (l *Store) UpsertSessionCost(projectID, agentType, sessionID string, cost float64, isEstimated bool) errorUpsertSessionCost 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 (*Store) Write
Section titled “func (*Store) Write”func (l *Store) Write(entry Entry) errorWrite inserts an entry into the database.
type ToolCountRow
Section titled “type ToolCountRow”ToolCountRow pairs a tool name with its invocation count.
type ToolCountRow struct { Name string Count int}Generated by gomarkdoc