---
title: Custom Store
description: Implement a custom storage backend by satisfying the composite store.Store interface.
---

Every Sentinel storage backend is a Go interface. This guide shows how to implement a custom store.

## The composite interface

Your store must implement the composite `store.Store` interface, which embeds 5 subsystem stores:

```go
import "github.com/xraph/sentinel/store"

type Store interface {
    suite.Store
    testcase.Store
    evalrun.Store
    baseline.Store
    promptversion.Store

    Migrate(ctx context.Context) error
    Ping(ctx context.Context) error
    Close() error
}
```

## Subsystem interfaces

### suite.Store

```go
type Store interface {
    CreateSuite(ctx context.Context, s *Suite) error
    GetSuite(ctx context.Context, suiteID id.SuiteID) (*Suite, error)
    GetSuiteByName(ctx context.Context, appID, name string) (*Suite, error)
    UpdateSuite(ctx context.Context, s *Suite) error
    DeleteSuite(ctx context.Context, suiteID id.SuiteID) error
    ListSuites(ctx context.Context, filter *ListFilter) ([]*Suite, error)
}
```

### testcase.Store

```go
type Store interface {
    CreateCase(ctx context.Context, tc *Case) error
    CreateCaseBatch(ctx context.Context, cases []*Case) error
    GetCase(ctx context.Context, caseID id.CaseID) (*Case, error)
    UpdateCase(ctx context.Context, tc *Case) error
    DeleteCase(ctx context.Context, caseID id.CaseID) error
    ListCases(ctx context.Context, suiteID id.SuiteID) ([]*Case, error)
    CountCases(ctx context.Context, suiteID id.SuiteID) (int64, error)
    ImportCases(ctx context.Context, suiteID id.SuiteID, format string, data []byte) (int64, error)
}
```

### evalrun.Store

```go
type Store interface {
    CreateRun(ctx context.Context, r *Run) error
    GetRun(ctx context.Context, runID id.EvalRunID) (*Run, error)
    UpdateRun(ctx context.Context, r *Run) error
    ListRuns(ctx context.Context, filter *ListFilter) ([]*Run, error)
    ListRunsBySuite(ctx context.Context, suiteID id.SuiteID) ([]*Run, error)
    CreateResult(ctx context.Context, result *Result) error
    CreateResultBatch(ctx context.Context, results []*Result) error
    ListResults(ctx context.Context, runID id.EvalRunID) ([]*Result, error)
    GetResultStats(ctx context.Context, runID id.EvalRunID) (*ResultStats, error)
}
```

### baseline.Store

```go
type Store interface {
    SaveBaseline(ctx context.Context, b *Baseline) error
    GetBaseline(ctx context.Context, baselineID id.BaselineID) (*Baseline, error)
    GetLatestBaseline(ctx context.Context, suiteID id.SuiteID) (*Baseline, error)
    ListBaselines(ctx context.Context, suiteID id.SuiteID) ([]*Baseline, error)
    DeleteBaseline(ctx context.Context, baselineID id.BaselineID) error
}
```

### promptversion.Store

```go
type Store interface {
    CreatePromptVersion(ctx context.Context, pv *PromptVersion) error
    GetPromptVersion(ctx context.Context, pvID id.PromptVersionID) (*PromptVersion, error)
    ListPromptVersions(ctx context.Context, suiteID id.SuiteID) ([]*PromptVersion, error)
    GetCurrentPromptVersion(ctx context.Context, suiteID id.SuiteID) (*PromptVersion, error)
    SetCurrentPromptVersion(ctx context.Context, suiteID id.SuiteID, pvID id.PromptVersionID) error
}
```

## Compile-time check

Use a compile-time assertion to verify your implementation satisfies all interfaces:

```go
var _ store.Store = (*MyStore)(nil)
```

## Registering with the engine

```go
eng, _ := engine.New(engine.WithStore(&MyStore{}))
```

## Reference implementations

Study the existing implementations:

| Backend | Package | Lines | Notes |
|---------|---------|-------|-------|
| Memory | `store/memory` | ~557 | Hash maps with RWMutex, simplest reference |
| SQLite | `store/sqlite` | ~511 | Bun ORM, creates tables on Migrate() |
| PostgreSQL | `store/postgres` | ~567 | Bun ORM, embedded SQL migrations |
