Custom Store
Implement a custom store backend for Shield persistence.
Every Shield subsystem defines a Go store interface. These are composed into the single store.Store composite. Implement this interface to use any database backend.
The composite store interface
type Store interface {
instinct.Store
awareness.Store
boundary.Store
values.Store
judgment.Store
reflex.Store
profile.Store
scan.Store
policy.Store
pii.Store
compliance.Store
Migrate(ctx context.Context) error
Ping(ctx context.Context) error
Close() error
}Implementing a custom store
Your struct must implement all methods from each subsystem store. Here's the pattern:
package mystore
import (
"context"
"github.com/xraph/shield/store"
)
type MyStore struct {
// your database connection fields
}
// Compile-time check
var _ store.Store = (*MyStore)(nil)
// Register with the engine:
eng, _ := engine.New(engine.WithStore(&MyStore{}))Subsystem store interfaces
Each safety primitive defines its own store interface. Here are the key ones:
instinct.Store
type Store interface {
CreateInstinct(ctx context.Context, inst *Instinct) error
GetInstinct(ctx context.Context, instID id.InstinctID) (*Instinct, error)
GetInstinctByName(ctx context.Context, appID, name string) (*Instinct, error)
UpdateInstinct(ctx context.Context, inst *Instinct) error
DeleteInstinct(ctx context.Context, instID id.InstinctID) error
ListInstincts(ctx context.Context, filter *ListFilter) ([]*Instinct, error)
}scan.Store
type Store interface {
CreateScan(ctx context.Context, result *Result) error
GetScan(ctx context.Context, scanID id.ScanID) (*Result, error)
ListScans(ctx context.Context, filter *ListFilter) ([]*Result, error)
ScanStats(ctx context.Context, filter *StatsFilter) (*Stats, error)
}pii.Store
type Store interface {
StorePIITokens(ctx context.Context, tokens []*Token) error
LoadPIITokens(ctx context.Context, tokenIDs []id.PIITokenID) ([]*Token, error)
LoadPIITokensByScan(ctx context.Context, scanID id.ScanID) ([]*Token, error)
DeletePIITokens(ctx context.Context, tokenIDs []id.PIITokenID) error
DeletePIITokensByTenant(ctx context.Context, tenantID string) error
PurgePIITokens(ctx context.Context, olderThan time.Time) (int64, error)
PIIStats(ctx context.Context, tenantID string) (*Stats, error)
}policy.Store
type Store interface {
CreatePolicy(ctx context.Context, pol *Policy) error
GetPolicy(ctx context.Context, polID id.PolicyID) (*Policy, error)
GetPolicyByName(ctx context.Context, scopeKey, name string) (*Policy, error)
UpdatePolicy(ctx context.Context, pol *Policy) error
DeletePolicy(ctx context.Context, polID id.PolicyID) error
ListPolicies(ctx context.Context, filter *ListFilter) ([]*Policy, error)
GetPoliciesForScope(ctx context.Context, scopeKey string, level ScopeLevel) ([]*Policy, error)
AssignToTenant(ctx context.Context, tenantID string, polID id.PolicyID) error
UnassignFromTenant(ctx context.Context, tenantID string, polID id.PolicyID) error
}All other subsystem stores (awareness, boundary, values, judgment, reflex, profile, compliance) follow the same CRUD pattern as instinct.Store.
Tips
- Use compile-time interface checks:
var _ store.Store = (*MyStore)(nil) - Enforce tenant scoping in all queries — filter by
app_idandtenant_id - Wrap sentinel errors with context:
fmt.Errorf("mystore: %w", shield.ErrScanNotFound) - Use JSONB columns for slice fields like
Strategies,Detectors,Rules,Triggers,Actions