Shield

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_id and tenant_id
  • Wrap sentinel errors with context: fmt.Errorf("mystore: %w", shield.ErrScanNotFound)
  • Use JSONB columns for slice fields like Strategies, Detectors, Rules, Triggers, Actions

On this page