---
title: Custom Store
description: 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

```go
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:

```go
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

```go
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

```go
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

```go
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

```go
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`
