SpiceDB Setup Guide¶
This guide covers setting up SpiceDB for authorization in CoreForge applications.
Overview¶
CoreForge uses SpiceDB for fine-grained, relationship-based authorization (ReBAC). SpiceDB implements Google's Zanzibar authorization model, providing:
- Relationship-based access control: Define who can do what based on relationships
- Computed permissions: Permissions derived from relationship chains
- Consistent authorization: Strongly consistent permission checks
- Scalable: Handles millions of relationships efficiently
Deployment Modes¶
Embedded Mode (Development)¶
For development and testing, CoreForge can run an embedded SpiceDB instance:
import (
"context"
"github.com/grokify/coreforge/authz/spicedb"
)
func main() {
ctx := context.Background()
// Create embedded client with in-memory storage
client, err := spicedb.NewClient(ctx, spicedb.Config{
Mode: "embedded",
DatastoreEngine: "memory",
}, nil)
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Write schema
if err := client.WriteSchema(ctx, spicedb.BaseSchema); err != nil {
log.Fatal(err)
}
// Create provider for authorization checks
provider := spicedb.NewProvider(client)
// Create syncer for identity integration
syncer := spicedb.NewSyncer(client)
}
In-Memory Datastore¶
Best for unit tests and quick prototyping:
PostgreSQL Datastore (Embedded)¶
For development with persistent data:
cfg := spicedb.Config{
Mode: "embedded",
DatastoreEngine: "postgres",
DatastoreURI: "postgres://user:pass@localhost:5432/spicedb?sslmode=disable",
}
Remote Mode (Production)¶
For production, connect to a standalone SpiceDB instance:
cfg := spicedb.Config{
Mode: "remote",
Endpoint: "spicedb.example.com:50051",
Token: "your-preshared-key",
Insecure: false, // Use TLS in production
}
Docker Compose Setup¶
For local development with a standalone SpiceDB:
version: '3.8'
services:
spicedb:
image: authzed/spicedb:latest
command: serve
ports:
- "50051:50051" # gRPC
- "8443:8443" # HTTP/REST
- "9090:9090" # Metrics
environment:
SPICEDB_GRPC_PRESHARED_KEY: "your-secret-key"
SPICEDB_DATASTORE_ENGINE: postgres
SPICEDB_DATASTORE_CONN_URI: "postgres://spicedb:spicedb@postgres:5432/spicedb?sslmode=disable"
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:15
environment:
POSTGRES_USER: spicedb
POSTGRES_PASSWORD: spicedb
POSTGRES_DB: spicedb
volumes:
- spicedb-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U spicedb"]
interval: 5s
timeout: 5s
retries: 5
volumes:
spicedb-data:
Start with:
Configuration Reference¶
| Field | Type | Default | Description |
|---|---|---|---|
Mode |
string | "embedded" |
"embedded" or "remote" |
DatastoreEngine |
string | "memory" |
For embedded: "memory" or "postgres" |
DatastoreURI |
string | - | PostgreSQL connection string (embedded mode) |
Endpoint |
string | - | SpiceDB gRPC endpoint (remote mode) |
Token |
string | - | Preshared key for authentication (remote mode) |
Insecure |
bool | false |
Skip TLS verification (remote mode) |
Environment Variables¶
# Storage Configuration
SPICEDB_MODE=remote # embedded or remote
SPICEDB_DATASTORE_ENGINE=memory # For embedded: memory or postgres
SPICEDB_DATASTORE_URI=postgres://... # For embedded with postgres
# Remote Connection
SPICEDB_ENDPOINT=spicedb.example.com:50051
SPICEDB_TOKEN=your-preshared-key
SPICEDB_INSECURE=false
Wiring to Identity Services¶
To sync identity operations to SpiceDB:
import (
"github.com/grokify/coreforge/authz/spicedb"
"github.com/grokify/coreforge/identity/organization"
"github.com/grokify/coreforge/identity/principal"
)
func setupServices(client *spicedb.Client, entClient *ent.Client) {
// Create syncer
syncer := spicedb.NewSyncer(client)
// Wire to organization service
orgService := organization.NewService(
entClient,
organization.WithAuthzSyncer(syncer),
organization.WithSyncMode(authz.SyncModeStrict),
)
// Wire to principal service
principalService := principal.NewService(
entClient,
principal.WithAuthzSyncer(syncer),
principal.WithSyncMode(authz.SyncModeStrict),
)
}
Sync Modes¶
SyncModeStrict: Operations fail if SpiceDB sync fails. Use when authorization must be consistent.SyncModeEventual: Operations succeed, sync failures are logged. Use with a retry mechanism.
Verification¶
Test your setup:
func testSetup(ctx context.Context, provider *spicedb.Provider, syncer *spicedb.Syncer) error {
orgID := uuid.New()
ownerID := uuid.New()
// Register organization
if err := syncer.RegisterOrganization(ctx, orgID, ownerID); err != nil {
return fmt.Errorf("register org failed: %w", err)
}
// Check owner can manage
owner := authz.Principal{ID: ownerID}
canManage, err := provider.Can(ctx, owner, "manage", authz.Resource{
Type: "organization",
ID: &orgID,
})
if err != nil {
return fmt.Errorf("permission check failed: %w", err)
}
if !canManage {
return fmt.Errorf("owner should have manage permission")
}
return nil
}
Next Steps¶
- SpiceDB Schema Guide - Understanding the authorization schema
- Integration Guide - Detailed integration patterns