store

package
v0.0.0-...-39adbd8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 24, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// RoleAdmin grants unrestricted access including key management and audit log.
	RoleAdmin = "admin"
	// RoleOperator may trigger scans and mutate findings within scope.
	RoleOperator = "operator"
	// RoleViewer may only read.
	RoleViewer = "viewer"
)

Role enumerates the authorisation levels Fleetsweeper recognises.

View Source
const ScopeWildcard = "*"

ScopeWildcard is the cluster scope entry that grants access to every cluster.

Variables

View Source
var (
	// ErrStore indicates a general storage failure.
	ErrStore = errors.New("store")
	// ErrNotFound indicates the requested record does not exist.
	ErrNotFound = errors.New("not found")
	// ErrQuery indicates a failure executing a database query.
	ErrQuery = errors.New("query")
	// ErrMigrate indicates a failure applying schema migrations.
	ErrMigrate = errors.New("migrate")
)

Functions

func AckFingerprint

func AckFingerprint(cluster, scanner, title string) string

AckFingerprint returns the stable SHA-256 used to key acknowledgements for a finding identified by its cluster, scanner, and title.

func GenerateToken

func GenerateToken() (string, error)

GenerateToken returns a freshly minted token suitable for an API key. The "fsk_" prefix lets operators identify a Fleetsweeper key at a glance in logs and secret stores. The remaining 32 bytes come from crypto/rand.

func HashToken

func HashToken(raw string) string

HashToken returns the lowercase-hex SHA-256 of a raw token. Used both at creation time (when persisting) and on each request (when matching).

func TokensEqual

func TokensEqual(a, b string) bool

TokensEqual compares two raw tokens in constant time to defeat timing oracles.

func ValidRole

func ValidRole(r string) bool

ValidRole reports whether the provided string is one of the recognised roles.

Types

type APIKeyRecord

type APIKeyRecord struct {
	// ID is the stable identifier used in audit log entries and revocation calls.
	ID string `json:"id"`
	// TokenHash is the lowercase-hex SHA-256 of the raw token.
	TokenHash string `json:"-"`
	// Name is a human-readable label for the key (for example "ci-runner").
	Name string `json:"name"`
	// Role is the authorisation role: admin, operator, or viewer.
	Role string `json:"role"`
	// ClusterScope lists cluster names (or "group:<name>" entries) this key may
	// act on. The single element "*" grants unrestricted access.
	ClusterScope []string `json:"cluster_scope"`
	// CreatedAt is when the key was minted.
	CreatedAt time.Time `json:"created_at"`
	// ExpiresAt, when non-zero, is when the key automatically becomes invalid.
	ExpiresAt time.Time `json:"expires_at,omitempty"`
	// LastUsedAt is the most recent successful authentication.
	LastUsedAt time.Time `json:"last_used_at,omitempty"`
	// RevokedAt, when non-zero, marks the key as administratively disabled.
	RevokedAt time.Time `json:"revoked_at,omitempty"`
	// CreatedBy identifies the actor that created this key (admin key id, or
	// "bootstrap" for the legacy --auth-token, or "cli" for offline creation).
	CreatedBy string `json:"created_by,omitempty"`
}

APIKeyRecord is a persisted API key authorised to call the server. Tokens are never stored in plaintext: only TokenHash is persisted, and the raw token is returned to the operator exactly once at creation time.

type AckRecord

type AckRecord struct {
	// Fingerprint is the SHA-256 of cluster|scanner|title.
	Fingerprint string `json:"fingerprint"`
	// Cluster echoes the finding's cluster scope (or "fleet").
	Cluster string `json:"cluster"`
	// Scanner echoes the finding's source scanner.
	Scanner string `json:"scanner"`
	// Title echoes the finding's title at ack time, for human context.
	Title string `json:"title"`
	// AckBy identifies the operator who acked, free-form.
	AckBy string `json:"ack_by,omitempty"`
	// Reason is the operator's stated reason for acking.
	Reason string `json:"reason,omitempty"`
	// SnoozeUntil, when non-zero, is when the ack expires and the finding
	// resumes alerting. Zero value means "permanent until removed".
	SnoozeUntil time.Time `json:"snooze_until,omitempty"`
	// CreatedAt is when the ack was recorded.
	CreatedAt time.Time `json:"created_at"`
}

AckRecord is a single finding acknowledgement persisted in SQLite. The fingerprint is stable across scans for the same finding shape (cluster + scanner + title) so a recurring critical can be acknowledged once instead of every scan cycle.

type AlertListOptions

type AlertListOptions struct {
	// Limit caps the number of rows returned. Defaults to 200 when zero.
	Limit int
	// Cluster, when non-empty, restricts results to a single cluster.
	Cluster string
	// Status, when non-empty, filters by alert status ("firing"/"resolved").
	Status string
	// Severity, when non-empty, filters by severity label value.
	Severity string
	// Since, when non-zero, only returns alerts received strictly after this time.
	Since time.Time
}

AlertListOptions filters AlertRecord queries.

type AlertRecord

type AlertRecord struct {
	// Fingerprint is the AlertManager-assigned stable identity.
	Fingerprint string `json:"fingerprint"`
	// Cluster is the cluster the alert applies to. Derived from the
	// `cluster` label when present and falling back to the empty string
	// when AlertManager doesn't carry a cluster label.
	Cluster string `json:"cluster"`
	// Status is "firing" or "resolved".
	Status string `json:"status"`
	// AlertName is the value of the alertname label.
	AlertName string `json:"alertname"`
	// Severity is the value of the severity label when present.
	Severity string `json:"severity,omitempty"`
	// Summary is the value of the summary annotation when present.
	Summary string `json:"summary,omitempty"`
	// StartsAt is when the alert began firing.
	StartsAt time.Time `json:"starts_at,omitempty"`
	// EndsAt is when the alert resolved (zero while firing).
	EndsAt time.Time `json:"ends_at,omitempty"`
	// ReceivedAt is when Fleetsweeper recorded the alert.
	ReceivedAt time.Time `json:"received_at"`
	// Labels carries every label AlertManager attached to the alert.
	Labels map[string]string `json:"labels"`
	// Annotations carries every annotation AlertManager attached to the alert.
	Annotations map[string]string `json:"annotations"`
	// GeneratorURL is the link back to the Prometheus rule that fired.
	GeneratorURL string `json:"generator_url,omitempty"`
}

AlertRecord is one inbound AlertManager alert persisted to the alerts table. Fingerprint is AlertManager's stable per-alert identifier and is reused as the primary key so a transition from firing to resolved updates the existing row in place.

type AuditEntry

type AuditEntry struct {
	// ID is a time-sortable identifier.
	ID string `json:"id"`
	// Timestamp is when the request was received.
	Timestamp time.Time `json:"timestamp"`
	// ActorID identifies the api key that performed the action.
	// Empty when --insecure mode allowed an anonymous call.
	ActorID string `json:"actor_id,omitempty"`
	// ActorName is the key's human-readable label at request time.
	ActorName string `json:"actor_name,omitempty"`
	// ActorRole is the role the key carried at request time.
	ActorRole string `json:"actor_role,omitempty"`
	// Method is the HTTP verb (POST, PUT, DELETE).
	Method string `json:"method"`
	// Path is the request path.
	Path string `json:"path"`
	// Status is the HTTP response status.
	Status int `json:"status"`
	// RemoteAddr is the client address as reported by the transport.
	RemoteAddr string `json:"remote_addr,omitempty"`
	// UserAgent is the client's User-Agent header.
	UserAgent string `json:"user_agent,omitempty"`
	// DurationMS is the request handler duration in milliseconds.
	DurationMS int64 `json:"duration_ms"`
	// Error, when non-empty, is a short message describing why the request
	// failed authorisation or processing.
	Error string `json:"error,omitempty"`
}

AuditEntry is a single audit log row recording one mutating request. Read-only requests are not audited to keep the table volume manageable; the request log already captures those at info level.

type AuditListOptions

type AuditListOptions struct {
	// Limit caps the number of rows returned. Defaults to 100 when zero.
	Limit int
	// Since, when non-zero, only returns entries strictly newer than this time.
	Since time.Time
	// ActorID, when non-empty, restricts results to a single API key.
	ActorID string
	// MinStatus, when non-zero, only returns entries with status >= MinStatus.
	// Use 400 to surface only failures.
	MinStatus int
}

AuditListOptions filters AuditEntry queries.

type ClusterRecord

type ClusterRecord struct {
	// Name is the kubeconfig context name.
	Name string `json:"name"`
	// FirstSeen is when this cluster was first scanned.
	FirstSeen time.Time `json:"first_seen"`
	// LastSeen is when this cluster was last scanned.
	LastSeen time.Time `json:"last_seen"`
	// Groups lists the group names this cluster belongs to.
	Groups []string `json:"groups"`
	// Tags is the cluster's free-form tag map, omitted from the wire
	// when no tags are set.
	Tags map[string]string `json:"tags,omitempty"`
}

ClusterRecord represents a known cluster.

type Driver

type Driver string

Driver identifies a supported backend.

const (
	// DriverSQLite stores data in a single SQLite file. Default for single-instance
	// deployments. Concurrent readers are fine; writers are serialised by the
	// SQLite engine.
	DriverSQLite Driver = "sqlite"
	// DriverPostgres stores data in a PostgreSQL database. Use this when running
	// multiple fleetsweeper replicas behind a load balancer.
	DriverPostgres Driver = "postgres"
)

func DetectDriver

func DetectDriver(dsn string) Driver

DetectDriver infers the driver from a DSN's prefix. URL-shaped DSNs like "postgres://..." or "postgresql://..." choose Postgres; everything else falls back to SQLite. Callers can override with --db-driver when the DSN is ambiguous.

type GroupRecord

type GroupRecord struct {
	// Name is the group name.
	Name string `json:"name"`
	// Clusters lists the cluster names in this group.
	Clusters []string `json:"clusters"`
}

GroupRecord represents a cluster group.

type LocationRecord

type LocationRecord struct {
	// Cluster is the cluster context name.
	Cluster string `json:"cluster"`
	// Lat is latitude in degrees north (positive) or south (negative).
	Lat float64 `json:"lat"`
	// Lng is longitude in degrees east (positive) or west (negative).
	Lng float64 `json:"lng"`
	// Site is a human-readable label (for example "Store #42, Manhattan").
	Site string `json:"site,omitempty"`
	// Notes is free-form text the operator can use.
	Notes string `json:"notes,omitempty"`
	// UpdatedAt is when the record was last written.
	UpdatedAt time.Time `json:"updated_at"`
}

LocationRecord is a user-supplied geographic override for a cluster. This is how operators map clusters to physical sites (retail stores, factories, edge devices) that have no cloud-region label to auto-detect.

type Postgres

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

Postgres implements Store against a PostgreSQL database. It is functionally equivalent to SQLite but tolerates concurrent writers, so multi-replica fleetsweeper deployments share a single backend. Timestamps are stored as RFC3339 TEXT and JSON columns as TEXT so the same row marshalling code as SQLite continues to work; the performance cost is negligible at the row volumes Fleetsweeper produces.

func NewPostgres

func NewPostgres(dsn string) (*Postgres, error)

NewPostgres opens a connection pool against the given DSN and applies migrations. The DSN is a standard libpq/pgx URL such as "postgres://user:pass@host:5432/db?sslmode=require".

func (*Postgres) AddClusterToGroup

func (p *Postgres) AddClusterToGroup(ctx context.Context, group, cluster string) error

AddClusterToGroup adds a cluster to an existing group.

func (*Postgres) Close

func (p *Postgres) Close() error

Close releases connection pool resources.

func (*Postgres) DeleteAck

func (p *Postgres) DeleteAck(ctx context.Context, fingerprint string) error

DeleteAck removes an acknowledgement by fingerprint. Idempotent.

func (*Postgres) DeleteClusterTag

func (p *Postgres) DeleteClusterTag(ctx context.Context, cluster, key string) error

DeleteClusterTag removes one key from a cluster's tag set.

func (*Postgres) DeleteGroup

func (p *Postgres) DeleteGroup(ctx context.Context, name string) error

DeleteGroup removes a group. Cascades to group_clusters.

func (*Postgres) DeleteLocation

func (p *Postgres) DeleteLocation(ctx context.Context, cluster string) error

DeleteLocation removes a manual override for a cluster.

func (*Postgres) GetAPIKey

func (p *Postgres) GetAPIKey(ctx context.Context, id string) (*APIKeyRecord, error)

GetAPIKey returns the key with the given ID.

func (*Postgres) GetAPIKeyByHash

func (p *Postgres) GetAPIKeyByHash(ctx context.Context, hash string) (*APIKeyRecord, error)

GetAPIKeyByHash looks up a key by token hash.

func (*Postgres) GetAlert

func (p *Postgres) GetAlert(ctx context.Context, fingerprint string) (*AlertRecord, error)

GetAlert returns the alert row with the given fingerprint. Returns the wrapped ErrNotFound when no row matches.

func (*Postgres) GetClusterHistory

func (p *Postgres) GetClusterHistory(ctx context.Context, cluster string, limit int) ([]ScanResultRecord, error)

GetClusterHistory returns scan results for a cluster across scans.

func (*Postgres) GetClusterTags

func (p *Postgres) GetClusterTags(ctx context.Context, cluster string) (map[string]string, error)

GetClusterTags returns every tag pair on a cluster.

func (*Postgres) GetGroup

func (p *Postgres) GetGroup(ctx context.Context, name string) (*GroupRecord, error)

GetGroup retrieves a group by name.

func (*Postgres) GetLocation

func (p *Postgres) GetLocation(ctx context.Context, cluster string) (*LocationRecord, error)

GetLocation returns the manual override for a cluster, or nil/no error when none has been set.

func (*Postgres) GetScan

func (p *Postgres) GetScan(ctx context.Context, id string) (*ScanRecord, error)

GetScan retrieves a scan record by ID.

func (*Postgres) GetScanResults

func (p *Postgres) GetScanResults(ctx context.Context, scanID string) (map[string]map[string]scanner.Result, error)

GetScanResults retrieves all per-cluster scanner results for a scan.

func (*Postgres) GetScansByTimeRange

func (p *Postgres) GetScansByTimeRange(ctx context.Context, start, end time.Time) ([]ScanRecord, error)

GetScansByTimeRange returns scans within a time window.

func (*Postgres) IsAcked

func (p *Postgres) IsAcked(ctx context.Context, fingerprint string) (bool, error)

IsAcked reports whether a finding has an active ack.

func (*Postgres) ListAPIKeys

func (p *Postgres) ListAPIKeys(ctx context.Context) ([]APIKeyRecord, error)

ListAPIKeys returns every API key.

func (*Postgres) ListAcks

func (p *Postgres) ListAcks(ctx context.Context) ([]AckRecord, error)

ListAcks returns every active ack with expired snoozes filtered out.

func (*Postgres) ListAlerts

func (p *Postgres) ListAlerts(ctx context.Context, opts AlertListOptions) ([]AlertRecord, error)

ListAlerts returns alerts matching opts, newest received_at first.

func (*Postgres) ListAuditEntries

func (p *Postgres) ListAuditEntries(ctx context.Context, opts AuditListOptions) ([]AuditEntry, error)

ListAuditEntries returns audit entries matching opts.

func (*Postgres) ListClusterTags

func (p *Postgres) ListClusterTags(ctx context.Context) (map[string]map[string]string, error)

ListClusterTags returns every tag across the fleet, grouped by cluster.

func (*Postgres) ListClusters

func (p *Postgres) ListClusters(ctx context.Context) ([]ClusterRecord, error)

ListClusters returns all known clusters with their group memberships.

func (*Postgres) ListGroups

func (p *Postgres) ListGroups(ctx context.Context) ([]GroupRecord, error)

ListGroups returns all groups.

func (*Postgres) ListLocations

func (p *Postgres) ListLocations(ctx context.Context) ([]LocationRecord, error)

ListLocations returns every manual override sorted by cluster name.

func (*Postgres) ListScans

func (p *Postgres) ListScans(ctx context.Context, limit int) ([]ScanRecord, error)

ListScans returns scan records ordered by timestamp descending.

func (*Postgres) Ping

func (p *Postgres) Ping(ctx context.Context) error

Ping verifies database connectivity. Used by the server's /readyz endpoint.

func (*Postgres) Prune

func (p *Postgres) Prune(ctx context.Context, cutoff time.Time) (int, error)

Prune deletes scans older than cutoff. The scan_results rows cascade.

func (*Postgres) PruneAlerts

func (p *Postgres) PruneAlerts(ctx context.Context, cutoff time.Time) (int, error)

PruneAlerts deletes alert rows older than cutoff. Returns rows removed.

func (*Postgres) PruneAuditEntries

func (p *Postgres) PruneAuditEntries(ctx context.Context, cutoff time.Time) (int, error)

PruneAuditEntries deletes audit_log rows older than cutoff. Returns the number of rows removed. Idempotent; safe to call from a periodic ticker.

func (*Postgres) RemoveClusterFromGroup

func (p *Postgres) RemoveClusterFromGroup(ctx context.Context, group, cluster string) error

RemoveClusterFromGroup removes a cluster from a group.

func (*Postgres) RevokeAPIKey

func (p *Postgres) RevokeAPIKey(ctx context.Context, id string) error

RevokeAPIKey marks a key as revoked.

func (*Postgres) SaveAPIKey

func (p *Postgres) SaveAPIKey(ctx context.Context, rec APIKeyRecord) error

SaveAPIKey inserts a new API key.

func (*Postgres) SaveAck

func (p *Postgres) SaveAck(ctx context.Context, rec AckRecord) error

SaveAck upserts an acknowledgement.

func (*Postgres) SaveAuditEntry

func (p *Postgres) SaveAuditEntry(ctx context.Context, rec AuditEntry) error

SaveAuditEntry inserts one audit entry.

func (*Postgres) SaveGroup

func (p *Postgres) SaveGroup(ctx context.Context, name string, clusters []string) error

SaveGroup creates or updates a group with the given cluster members.

func (*Postgres) SaveScan

func (p *Postgres) SaveScan(ctx context.Context, clusters []string, results map[string]map[string]scanner.Result) (string, error)

SaveScan persists a complete scan with all per-cluster results.

func (*Postgres) SetClusterTag

func (p *Postgres) SetClusterTag(ctx context.Context, cluster, key, value string) error

SetClusterTag upserts one key/value tag on a cluster.

func (*Postgres) SetLocation

func (p *Postgres) SetLocation(ctx context.Context, loc LocationRecord) error

SetLocation upserts a manual geographic override for a cluster.

func (*Postgres) TouchAPIKey

func (p *Postgres) TouchAPIKey(ctx context.Context, id string, at time.Time) error

TouchAPIKey records the last-used-at timestamp.

func (*Postgres) UpsertAlert

func (p *Postgres) UpsertAlert(ctx context.Context, rec AlertRecord) error

UpsertAlert inserts or updates an alert keyed by fingerprint.

func (*Postgres) Vacuum

func (p *Postgres) Vacuum(_ context.Context) error

Vacuum is a no-op on Postgres because autovacuum runs in the background. The method exists so the Store interface stays uniform.

type SQLite

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

SQLite implements Store using a SQLite database.

func NewSQLite

func NewSQLite(path string) (*SQLite, error)

NewSQLite opens or creates a SQLite database at path and runs migrations. WAL is enabled for read/write concurrency; busy_timeout retries instead of failing under contention; foreign_keys are required for ON DELETE CASCADE to work on scan_results.

func (*SQLite) AddClusterToGroup

func (s *SQLite) AddClusterToGroup(ctx context.Context, group, cluster string) error

AddClusterToGroup adds a cluster to an existing group.

func (*SQLite) Close

func (s *SQLite) Close() error

Close releases database resources.

func (*SQLite) DeleteAck

func (s *SQLite) DeleteAck(ctx context.Context, fingerprint string) error

DeleteAck removes an acknowledgement by fingerprint. Idempotent: a missing row is not an error so callers can call from a "clear" button without worrying about state.

func (*SQLite) DeleteClusterTag

func (s *SQLite) DeleteClusterTag(ctx context.Context, cluster, key string) error

DeleteClusterTag removes one key from a cluster's tag set.

func (*SQLite) DeleteGroup

func (s *SQLite) DeleteGroup(ctx context.Context, name string) error

DeleteGroup removes a group. Cascades to group_clusters.

func (*SQLite) DeleteLocation

func (s *SQLite) DeleteLocation(ctx context.Context, cluster string) error

DeleteLocation removes a manual override for a cluster.

func (*SQLite) GetAPIKey

func (s *SQLite) GetAPIKey(ctx context.Context, id string) (*APIKeyRecord, error)

GetAPIKey returns the key with the given ID.

func (*SQLite) GetAPIKeyByHash

func (s *SQLite) GetAPIKeyByHash(ctx context.Context, hash string) (*APIKeyRecord, error)

GetAPIKeyByHash looks up a key by its token hash. Revoked or expired keys are returned with their RevokedAt/ExpiresAt set so callers can produce the right error message.

func (*SQLite) GetAlert

func (s *SQLite) GetAlert(ctx context.Context, fingerprint string) (*AlertRecord, error)

GetAlert returns the alert row with the given fingerprint. Returns the wrapped ErrNotFound when no row matches.

func (*SQLite) GetClusterHistory

func (s *SQLite) GetClusterHistory(ctx context.Context, cluster string, limit int) ([]ScanResultRecord, error)

GetClusterHistory returns scan results for a cluster across scans.

func (*SQLite) GetClusterTags

func (s *SQLite) GetClusterTags(ctx context.Context, cluster string) (map[string]string, error)

GetClusterTags returns every tag pair on a cluster.

func (*SQLite) GetGroup

func (s *SQLite) GetGroup(ctx context.Context, name string) (*GroupRecord, error)

GetGroup retrieves a group by name.

func (*SQLite) GetLocation

func (s *SQLite) GetLocation(ctx context.Context, cluster string) (*LocationRecord, error)

GetLocation returns the manual override for a cluster, or nil/no error when none has been set.

func (*SQLite) GetScan

func (s *SQLite) GetScan(ctx context.Context, id string) (*ScanRecord, error)

GetScan retrieves a scan record by ID.

func (*SQLite) GetScanResults

func (s *SQLite) GetScanResults(ctx context.Context, scanID string) (map[string]map[string]scanner.Result, error)

GetScanResults retrieves all per-cluster scanner results for a scan.

func (*SQLite) GetScansByTimeRange

func (s *SQLite) GetScansByTimeRange(ctx context.Context, start, end time.Time) ([]ScanRecord, error)

GetScansByTimeRange returns scans within a time window.

func (*SQLite) IsAcked

func (s *SQLite) IsAcked(ctx context.Context, fingerprint string) (bool, error)

IsAcked reports whether a finding identified by fingerprint has an active (non-expired) ack.

func (*SQLite) ListAPIKeys

func (s *SQLite) ListAPIKeys(ctx context.Context) ([]APIKeyRecord, error)

ListAPIKeys returns every API key, ordered by creation time descending. Revoked keys are included so administrators can audit them.

func (*SQLite) ListAcks

func (s *SQLite) ListAcks(ctx context.Context) ([]AckRecord, error)

ListAcks returns every active ack. Expired snoozes are filtered out and removed from the database as a side effect so the table stays small.

func (*SQLite) ListAlerts

func (s *SQLite) ListAlerts(ctx context.Context, opts AlertListOptions) ([]AlertRecord, error)

ListAlerts returns alerts matching opts, newest received_at first.

func (*SQLite) ListAuditEntries

func (s *SQLite) ListAuditEntries(ctx context.Context, opts AuditListOptions) ([]AuditEntry, error)

ListAuditEntries returns audit entries matching opts, newest first.

func (*SQLite) ListClusterTags

func (s *SQLite) ListClusterTags(ctx context.Context) (map[string]map[string]string, error)

ListClusterTags returns every tag across the fleet, grouped by cluster.

func (*SQLite) ListClusters

func (s *SQLite) ListClusters(ctx context.Context) ([]ClusterRecord, error)

ListClusters returns all known clusters with their group memberships.

func (*SQLite) ListGroups

func (s *SQLite) ListGroups(ctx context.Context) ([]GroupRecord, error)

ListGroups returns all groups.

func (*SQLite) ListLocations

func (s *SQLite) ListLocations(ctx context.Context) ([]LocationRecord, error)

ListLocations returns every manual override sorted by cluster name.

func (*SQLite) ListScans

func (s *SQLite) ListScans(ctx context.Context, limit int) ([]ScanRecord, error)

ListScans returns scan records ordered by timestamp descending.

func (*SQLite) Ping

func (s *SQLite) Ping(ctx context.Context) error

Ping verifies database connectivity. Used by the server's /readyz endpoint.

func (*SQLite) Prune

func (s *SQLite) Prune(ctx context.Context, cutoff time.Time) (int, error)

Prune deletes scans older than cutoff. The scan_results rows cascade. Returns the number of scans deleted.

func (*SQLite) PruneAlerts

func (s *SQLite) PruneAlerts(ctx context.Context, cutoff time.Time) (int, error)

PruneAlerts removes alert rows older than cutoff. Returns rows removed.

func (*SQLite) PruneAuditEntries

func (s *SQLite) PruneAuditEntries(ctx context.Context, cutoff time.Time) (int, error)

PruneAuditEntries deletes audit_log rows older than cutoff. Returns the number of rows removed. Idempotent; safe to call from a periodic ticker.

func (*SQLite) RemoveClusterFromGroup

func (s *SQLite) RemoveClusterFromGroup(ctx context.Context, group, cluster string) error

RemoveClusterFromGroup removes a cluster from a group.

func (*SQLite) RevokeAPIKey

func (s *SQLite) RevokeAPIKey(ctx context.Context, id string) error

RevokeAPIKey marks a key as revoked. The row is retained so audit log entries referencing it remain interpretable.

func (*SQLite) SaveAPIKey

func (s *SQLite) SaveAPIKey(ctx context.Context, rec APIKeyRecord) error

SaveAPIKey inserts a new API key. The ID and TokenHash must already be set; CreatedAt defaults to now when zero. Re-inserting the same ID returns ErrStore.

func (*SQLite) SaveAck

func (s *SQLite) SaveAck(ctx context.Context, rec AckRecord) error

SaveAck upserts an acknowledgement. The fingerprint is the primary key, so re-acking an already-acked finding refreshes its metadata in place.

func (*SQLite) SaveAuditEntry

func (s *SQLite) SaveAuditEntry(ctx context.Context, rec AuditEntry) error

SaveAuditEntry inserts one audit entry. ID and Timestamp default to fresh values when unset, so callers can supply just the request-level fields.

func (*SQLite) SaveGroup

func (s *SQLite) SaveGroup(ctx context.Context, name string, clusters []string) error

SaveGroup creates or updates a group with the given cluster members.

func (*SQLite) SaveScan

func (s *SQLite) SaveScan(ctx context.Context, clusters []string, results map[string]map[string]scanner.Result) (string, error)

SaveScan persists a complete scan with all per-cluster results.

func (*SQLite) SetClusterTag

func (s *SQLite) SetClusterTag(ctx context.Context, cluster, key, value string) error

SetClusterTag upserts one key/value tag on a cluster. Keys are case-sensitive and free-form; common conventions are `env=prod`, `tier=critical`, `owner=team-a`.

func (*SQLite) SetLocation

func (s *SQLite) SetLocation(ctx context.Context, loc LocationRecord) error

SetLocation upserts a manual geographic override for a cluster.

func (*SQLite) TouchAPIKey

func (s *SQLite) TouchAPIKey(ctx context.Context, id string, at time.Time) error

TouchAPIKey records the most recent successful authentication time. This is best-effort: a failure here does not invalidate the request.

func (*SQLite) UpsertAlert

func (s *SQLite) UpsertAlert(ctx context.Context, rec AlertRecord) error

UpsertAlert inserts or updates an alert keyed by fingerprint.

func (*SQLite) Vacuum

func (s *SQLite) Vacuum(ctx context.Context) error

Vacuum reclaims free pages by running SQLite VACUUM.

func (*SQLite) VacuumInto

func (s *SQLite) VacuumInto(ctx context.Context, path string) error

VacuumInto writes a consistent snapshot of the database to the given file path. Wraps the SQLite `VACUUM INTO` statement which captures the entire database state atomically without blocking concurrent reads. The target path must not already exist.

type ScanRecord

type ScanRecord struct {
	// ID is the unique scan identifier (ULID).
	ID string `json:"id"`
	// Timestamp is when the scan was executed.
	Timestamp time.Time `json:"timestamp"`
	// Clusters lists the cluster context names that were scanned.
	Clusters []string `json:"clusters"`
	// Scanners lists the scanner names that were executed.
	Scanners []string `json:"scanners"`
}

ScanRecord represents a persisted scan execution.

type ScanResultRecord

type ScanResultRecord struct {
	// ScanID is the parent scan identifier.
	ScanID string `json:"scan_id"`
	// Cluster is the cluster context name.
	Cluster string `json:"cluster"`
	// Scanner is the scanner name.
	Scanner string `json:"scanner"`
	// DataJSON is the raw JSON-encoded scanner output.
	DataJSON []byte `json:"data_json"`
}

ScanResultRecord represents one scanner's output for one cluster in one scan.

type Store

type Store interface {
	// SaveScan persists a complete scan with all per-cluster results. Returns
	// the generated scan ID.
	SaveScan(ctx context.Context, clusters []string, results map[string]map[string]scanner.Result) (string, error)

	// GetScan retrieves a scan record by ID.
	GetScan(ctx context.Context, id string) (*ScanRecord, error)

	// ListScans returns scan records ordered by timestamp descending.
	ListScans(ctx context.Context, limit int) ([]ScanRecord, error)

	// GetScanResults retrieves all per-cluster scanner results for a scan,
	// reconstructed into the same map shape the scan engine produces.
	GetScanResults(ctx context.Context, scanID string) (map[string]map[string]scanner.Result, error)

	// ListClusters returns all known clusters.
	ListClusters(ctx context.Context) ([]ClusterRecord, error)

	// SaveGroup creates or updates a group with the given cluster members.
	SaveGroup(ctx context.Context, name string, clusters []string) error

	// GetGroup retrieves a group by name.
	GetGroup(ctx context.Context, name string) (*GroupRecord, error)

	// ListGroups returns all groups.
	ListGroups(ctx context.Context) ([]GroupRecord, error)

	// DeleteGroup removes a group.
	DeleteGroup(ctx context.Context, name string) error

	// AddClusterToGroup adds a cluster to an existing group.
	AddClusterToGroup(ctx context.Context, group, cluster string) error

	// RemoveClusterFromGroup removes a cluster from a group.
	RemoveClusterFromGroup(ctx context.Context, group, cluster string) error

	// GetClusterHistory returns scan results for a specific cluster across
	// scans, ordered by time descending.
	GetClusterHistory(ctx context.Context, cluster string, limit int) ([]ScanResultRecord, error)

	// GetScansByTimeRange returns scans within a time window.
	GetScansByTimeRange(ctx context.Context, start, end time.Time) ([]ScanRecord, error)

	// Prune deletes scans older than cutoff. Returns the number of scans deleted.
	Prune(ctx context.Context, cutoff time.Time) (int, error)

	// Vacuum reclaims unused database pages.
	Vacuum(ctx context.Context) error

	// SetLocation upserts a manual location override for a cluster.
	SetLocation(ctx context.Context, loc LocationRecord) error

	// GetLocation returns a single cluster's manual override, or nil if none.
	GetLocation(ctx context.Context, cluster string) (*LocationRecord, error)

	// ListLocations returns every manual override.
	ListLocations(ctx context.Context) ([]LocationRecord, error)

	// DeleteLocation removes a manual override.
	DeleteLocation(ctx context.Context, cluster string) error

	// SaveAck upserts a finding acknowledgement. The fingerprint primary key
	// allows the same finding to be acked once across many scan cycles.
	SaveAck(ctx context.Context, rec AckRecord) error

	// DeleteAck removes an acknowledgement. Idempotent.
	DeleteAck(ctx context.Context, fingerprint string) error

	// ListAcks returns every active acknowledgement (expired snoozes pruned).
	ListAcks(ctx context.Context) ([]AckRecord, error)

	// IsAcked reports whether a finding is currently acknowledged.
	IsAcked(ctx context.Context, fingerprint string) (bool, error)

	// SaveAPIKey inserts a new API key. The raw token is never persisted: the
	// caller must hash it and place the result in rec.TokenHash before calling.
	SaveAPIKey(ctx context.Context, rec APIKeyRecord) error

	// GetAPIKeyByHash looks up a key by token hash. Returns the wrapped
	// ErrNotFound when no row matches.
	GetAPIKeyByHash(ctx context.Context, hash string) (*APIKeyRecord, error)

	// GetAPIKey returns the key with the given ID.
	GetAPIKey(ctx context.Context, id string) (*APIKeyRecord, error)

	// ListAPIKeys returns every API key including revoked ones, newest first.
	ListAPIKeys(ctx context.Context) ([]APIKeyRecord, error)

	// RevokeAPIKey marks the key with the given ID as administratively disabled.
	RevokeAPIKey(ctx context.Context, id string) error

	// TouchAPIKey updates the last-used-at timestamp. Best-effort; callers
	// should not fail authentication on error.
	TouchAPIKey(ctx context.Context, id string, at time.Time) error

	// SaveAuditEntry inserts one audit log row.
	SaveAuditEntry(ctx context.Context, rec AuditEntry) error

	// ListAuditEntries returns audit entries matching opts, newest first.
	ListAuditEntries(ctx context.Context, opts AuditListOptions) ([]AuditEntry, error)

	// PruneAuditEntries deletes audit log rows older than cutoff and returns
	// the number of rows removed. Used by the audit-retention ticker.
	PruneAuditEntries(ctx context.Context, cutoff time.Time) (int, error)

	// UpsertAlert inserts or updates an inbound AlertManager alert keyed by
	// its fingerprint. Reusing the fingerprint allows the same alert to
	// transition between firing/resolved without producing duplicate rows.
	UpsertAlert(ctx context.Context, rec AlertRecord) error

	// GetAlert returns the alert row with the given fingerprint. Returns
	// ErrNotFound when no row matches.
	GetAlert(ctx context.Context, fingerprint string) (*AlertRecord, error)

	// ListAlerts returns alerts matching opts, newest received_at first.
	ListAlerts(ctx context.Context, opts AlertListOptions) ([]AlertRecord, error)

	// PruneAlerts deletes alert rows older than cutoff (received_at). Returns
	// the number of rows removed.
	PruneAlerts(ctx context.Context, cutoff time.Time) (int, error)

	// SetClusterTag upserts a single key/value tag on a cluster.
	SetClusterTag(ctx context.Context, cluster, key, value string) error

	// DeleteClusterTag removes one key from a cluster's tag set. Idempotent.
	DeleteClusterTag(ctx context.Context, cluster, key string) error

	// GetClusterTags returns every tag pair on a cluster as a key→value
	// map. Empty map when no tags are set.
	GetClusterTags(ctx context.Context, cluster string) (map[string]string, error)

	// ListClusterTags returns every tag across the fleet, grouped by
	// cluster. Convenient for the dashboard's per-cluster render.
	ListClusterTags(ctx context.Context) (map[string]map[string]string, error)

	// Close releases database resources.
	Close() error
}

Store persists and retrieves scan data.

func Open

func Open(driver, dsn string) (Store, error)

Open returns a Store backed by the named driver. Recognised drivers are "sqlite" and "postgres"; the DSN format depends on the driver:

  • sqlite: a filesystem path (":memory:" for ephemeral storage).
  • postgres: a libpq/pgx URL, e.g. "postgres://user:pass@host:5432/db?sslmode=require".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL