Documentation
¶
Overview ¶
Package sqlite is part of the GoFastr harness.
See docs/harness-architecture.md for the architecture this package implements.
Package sqlite is the SQLite-backed implementation of session.Store.
Uses the existing framework dependency `mattn/go-sqlite3`. Per the architecture doc, at-rest encryption is on the v0.2 roadmap; v0.1 stores plaintext and relies on the redaction middleware on the write path to keep secrets out of the file.
Index ¶
- Variables
- func DefaultCostLedgerPath() string
- func DefaultPath() string
- func ExportDEK(dbPath string, kek, recipientKey []byte, outPath string) error
- func HashArgs(args []byte) string
- func ImportDEK(dbPath, exportedPath string, recipientKey, localKEK []byte) error
- func LoadOrCreateDEK(dbPath string, kek []byte) ([]byte, string, error)
- func RotateKEK(dbPath string, oldKEK, newKEK []byte) error
- type CostLedger
- func (c *CostLedger) Close() error
- func (c *CostLedger) CostByProvider(ctx context.Context, since time.Time) (map[string]float64, error)
- func (c *CostLedger) CostBySession(ctx context.Context, since time.Time) (map[string]float64, error)
- func (c *CostLedger) Record(ctx context.Context, session, provider, model string, ...) error
- type DEKHeader
- type DefaultRedactor
- type EncryptionMode
- type MonthlyRollover
- type ProviderAttempt
- type Reconciliation
- type Redactor
- type Store
- func (s *Store) AppendEvent(ctx context.Context, env control.EventEnvelope) error
- func (s *Store) ApplyRetention(ctx context.Context, ttl time.Duration) (int64, error)
- func (s *Store) AttemptsForTurn(ctx context.Context, session string, turn int) ([]ProviderAttempt, error)
- func (s *Store) Close() error
- func (s *Store) CloseEncrypted() error
- func (s *Store) DropMetadata(ctx context.Context, age time.Duration, exemptSessions []string) (int64, error)
- func (s *Store) EnsureAttemptsSchema(ctx context.Context) error
- func (s *Store) EventsSince(ctx context.Context, sess ids.SessionID, since uint64, limit int) ([]control.EventEnvelope, error)
- func (s *Store) ListPastSessions(ctx context.Context, limit int) ([]session.PastSession, error)
- func (s *Store) LocalCostInPeriod(ctx context.Context, provider string, start, end time.Time) (float64, error)
- func (s *Store) OrphanIntents(ctx context.Context, sess ids.SessionID) ([]session.ToolIntent, error)
- func (s *Store) ReconcileNow(ctx context.Context, fetcher UpstreamUsageFetcher, period time.Duration) (Reconciliation, error)
- func (s *Store) RecordAttempt(ctx context.Context, a ProviderAttempt) (int64, error)
- func (s *Store) RecordToolIntent(ctx context.Context, intent session.ToolIntent) error
- func (s *Store) RecordToolOutcome(ctx context.Context, outcome session.ToolOutcome) error
- func (s *Store) SaveReconciliation(ctx context.Context, r Reconciliation) (int64, error)
- func (s *Store) SnapshotPath() string
- type UpstreamUsageFetcher
Constants ¶
This section is empty.
Variables ¶
var ErrLedgerClosed = errors.New("sqlite: cost ledger closed")
ErrLedgerClosed is returned when methods are called after Close.
var ErrNoReconciliations = errors.New("sqlite: no reconciliations recorded")
ErrNoReconciliations sentinel.
Functions ¶
func DefaultCostLedgerPath ¶
func DefaultCostLedgerPath() string
DefaultCostLedgerPath returns the canonical XDG state location.
func ExportDEK ¶
ExportDEK writes the DEK wrapped under a recipient's public key so the user can escrow or migrate the DB. The recipient format is X25519 base64; we use a tiny in-package wrapper (no third-party dep) that emits an AES-GCM-wrapped DEK plus the recipient's public key fingerprint for identification.
To keep the implementation stdlib-only, the "recipient" here is another 32-byte key, not full X25519 + age. Real X25519/age integration lives behind a build-tagged extension at session/sqlite/escrow_age.go; the v0.1 default ships this simpler form that still meets the threat-model goal of "key escrow that doesn't require the harness binary to recover."
func HashArgs ¶
HashArgs returns the SHA-256 hex of tool arguments, used to fill tool_call_intents.args_hash.
func ImportDEK ¶
ImportDEK reads a previously-exported DEK and installs it as the active DEK for the given DB, wrapped under the local KEK. The recipientKey must match what ExportDEK used.
func LoadOrCreateDEK ¶
LoadOrCreateDEK opens the DEK header at `<dbPath>.dek`. If it doesn't exist, a fresh DEK is generated and wrapped with `kek`. Returns the unwrapped DEK and the header path.
Types ¶
type CostLedger ¶
type CostLedger struct {
// contains filtered or unexported fields
}
CostLedger is a separate SQLite DB that records cost rows independently from the event log. Survives VACUUM on the main session DB and is queryable directly for the cost dashboard.
func OpenCostLedger ¶
func OpenCostLedger(path string) (*CostLedger, error)
OpenCostLedger opens the cost ledger DB.
func (*CostLedger) CostByProvider ¶
func (c *CostLedger) CostByProvider(ctx context.Context, since time.Time) (map[string]float64, error)
CostByProvider returns USD totals grouped by provider since `since`.
func (*CostLedger) CostBySession ¶
func (c *CostLedger) CostBySession(ctx context.Context, since time.Time) (map[string]float64, error)
CostBySession returns USD totals grouped by session since `since`.
type DEKHeader ¶
type DEKHeader struct {
Version int `json:"version"`
Nonce []byte `json:"nonce"` // AES-GCM nonce for the wrap
Wrapped []byte `json:"wrapped"` // AES-GCM ciphertext of the DEK
Metadata string `json:"meta,omitempty"` // human-readable, e.g. "kek=machine"
}
DEKHeader is the on-disk shape of the wrapped DEK.
type DefaultRedactor ¶
type DefaultRedactor struct{}
DefaultRedactor replaces common secret patterns with «redacted:KIND».
func (DefaultRedactor) Redact ¶
func (DefaultRedactor) Redact(text string) string
Redact applies a list of canned regex replacements. The list is intentionally tight in v0.1 — false positives are worse than false negatives here because the regex runs on *every event payload*. Roadmap OBS-EXPORT-BUNDLE adds a deeper-pass detector for export.
type EncryptionMode ¶
type EncryptionMode int
EncryptionMode controls how the session log file is protected.
const ( // EncryptionNone — plaintext SQLite file. The default for v0.1. EncryptionNone EncryptionMode = iota // EncryptionAtRest — the SQLite file is encrypted in place // using file-level AES-GCM whenever the harness is not running. // On Open(), the file is decrypted to a sidecar; on Close(), the // sidecar is re-encrypted and the decrypted copy is removed. // // The architecture doc notes that whole-file encryption + VACUUM // is a problem for very large logs; this mode accepts that // limitation in exchange for a real defense against laptop theft. // Page-level encryption requires a custom SQLite VFS and is a // genuinely larger sub-project (acknowledged in the spec but not // blocking v0.1). EncryptionAtRest )
type MonthlyRollover ¶
type MonthlyRollover struct {
// contains filtered or unexported fields
}
MonthlyRollover provides per-month log files (sessions-YYYYMM.db) for retention efficiency: VACUUM only touches the current month's file, not the whole history.
The current month's file is the "live" Store; older months are closed and become read-only archives. Open queries can span archives by iterating Open() on each month file.
func NewMonthlyRollover ¶
func NewMonthlyRollover(dir string) (*MonthlyRollover, error)
NewMonthlyRollover returns a rollover manager rooted at dir. The current month's store is opened immediately.
func (*MonthlyRollover) Close ¶
func (m *MonthlyRollover) Close() error
Close releases the live Store.
func (*MonthlyRollover) Current ¶
func (m *MonthlyRollover) Current() (*Store, error)
Current returns the live Store, rolling over if the calendar month has changed since the last call.
func (*MonthlyRollover) ListArchives ¶
func (m *MonthlyRollover) ListArchives() ([]string, error)
ListArchives returns the paths of every month file in the directory (current + archives), oldest first.
type ProviderAttempt ¶
type ProviderAttempt struct {
ID int64
Session string
Turn int
Provider string
Model string
RequestID string // upstream request ID when surfaced
StartedAt time.Time
EndedAt time.Time
TerminatedReason string // "complete" | "eof" | "timeout" | "cancel" | "http_error"
InputTokens int
OutputTokens int
CacheReadTokens int
CacheWriteTokens int
EstimatedUSD float64
}
ProviderAttempt records one provider HTTP call. Used by the cost reconciliation job to compare local estimates against provider-side usage APIs (OpenRouter /credits, ZAI /billing/usage, etc.).
type Reconciliation ¶
type Reconciliation struct {
ID int64
Provider string
PeriodStart time.Time
PeriodEnd time.Time
UpstreamUSD float64
LocalUSD float64
VarianceUSD float64
RanAt time.Time
}
Reconciliation is one reconciliation pass against an upstream provider's billing API.
type Store ¶
type Store struct {
// Redactors run on every event payload before it lands in the
// `events.payload` column. The default redactor uses common
// secret regexes; profiles can swap in tighter sets.
Redactors []Redactor
// contains filtered or unexported fields
}
Store is the SQLite-backed session.Store.
func Open ¶
Open opens (or creates) the SQLite session log at path. Creates the parent directory and applies the v1 schema on first use.
func OpenEncrypted ¶
func OpenEncrypted(path string, mode EncryptionMode, key []byte) (*Store, error)
OpenEncrypted opens a SQLite session log with the requested encryption mode. The key (32 bytes) protects the at-rest file when mode == EncryptionAtRest.
- mode = EncryptionNone: behaves exactly like Open.
- mode = EncryptionAtRest: if `<path>.enc` exists, decrypt to `path` first; then open. The encrypted form is written on Close.
The unencrypted `path` is created with mode 0600 and lives in the XDG state dir, which is owner-readable only on every supported OS.
func OpenWithKEK ¶
OpenWithKEK opens an encrypted Store using the DEK/KEK scheme. If no .dek file exists, a fresh DEK is generated and wrapped.
func (*Store) AppendEvent ¶
AppendEvent persists one envelope with redactors applied.
func (*Store) ApplyRetention ¶
ApplyRetention drops the payload column for events older than ttl, leaving metadata-only rows.
func (*Store) AttemptsForTurn ¶
func (s *Store) AttemptsForTurn(ctx context.Context, session string, turn int) ([]ProviderAttempt, error)
AttemptsForTurn returns all attempts for one turn. Useful when computing the true cost of a turn that retried.
func (*Store) CloseEncrypted ¶
CloseEncrypted closes the Store and re-encrypts the file when EncryptionAtRest is active. The plaintext file is removed afterwards.
func (*Store) DropMetadata ¶
func (s *Store) DropMetadata(ctx context.Context, age time.Duration, exemptSessions []string) (int64, error)
DropMetadata removes event rows entirely (not just their content) whose ts is older than the given age. Returns the count of deleted rows.
Default policy per § Persistence: drop metadata-only rows after 180 days. Pinned sessions (tracked separately) survive.
func (*Store) EnsureAttemptsSchema ¶
EnsureAttemptsSchema applies the provider_attempts migration. Safe to call repeatedly.
func (*Store) EventsSince ¶
func (s *Store) EventsSince(ctx context.Context, sess ids.SessionID, since uint64, limit int) ([]control.EventEnvelope, error)
EventsSince returns events with id > since for a session.
func (*Store) ListPastSessions ¶
ListPastSessions aggregates the events table into per-session summaries. Newest-last-seen first. Uses two passes — one for the counts/timestamps (fast), one for the first user-message text per session (slower; bounded by `limit`).
func (*Store) LocalCostInPeriod ¶
func (s *Store) LocalCostInPeriod(ctx context.Context, provider string, start, end time.Time) (float64, error)
LocalCostInPeriod returns the local-estimated USD for one provider across the time window. Used by the reconciliation job.
func (*Store) OrphanIntents ¶
func (s *Store) OrphanIntents(ctx context.Context, sess ids.SessionID) ([]session.ToolIntent, error)
OrphanIntents returns intents without matching outcomes. v0.1 returns all intents for the given LogID; the higher layer narrows by session.
func (*Store) ReconcileNow ¶
func (s *Store) ReconcileNow(ctx context.Context, fetcher UpstreamUsageFetcher, period time.Duration) (Reconciliation, error)
ReconcileNow runs one reconciliation pass for the given provider. Returns the diff. Callers schedule this daily.
func (*Store) RecordAttempt ¶
RecordAttempt inserts a provider_attempts row, returning the assigned ID.
func (*Store) RecordToolIntent ¶
RecordToolIntent writes an intent row.
func (*Store) RecordToolOutcome ¶
RecordToolOutcome writes an outcome row.
func (*Store) SaveReconciliation ¶
SaveReconciliation persists a reconciliation result.
func (*Store) SnapshotPath ¶
SnapshotPath returns the on-disk path the store was opened with. Useful for diagnostics + the /health endpoint.
type UpstreamUsageFetcher ¶
type UpstreamUsageFetcher interface {
// Name returns the provider name (matches ProviderAttempt.Provider).
Name() string
// FetchUsageUSD returns the upstream-reported USD spent in the
// given period.
FetchUsageUSD(ctx context.Context, start, end time.Time) (float64, error)
}
UpstreamUsageFetcher is the abstraction the reconciliation job uses to pull billing data from a provider. Each Provider package can implement this if its upstream offers a usage API.