A modern web interface for managing SpiceDB authorization systems. Built with Next.js and Tailwind CSS.
- Dashboard - Real-time overview of your SpiceDB instance with stats and activity
- Schema Management - Visual and text-based schema editor with validation
- Relationship Management - CRUD operations with smart dropdowns and search
- Authorization Testing - Permission checks, expansions, and subject lookups
- Zed Terminal - Run
zedcommands against the connected SpiceDB instance
- Screenshots
- Features
- Prerequisites
- Quick Start
- Configuration
- Mock Data
- Usage
- API Endpoints
- Tech Stack
- Troubleshooting
- Development
- License
- Links
- Node.js 18+
- Docker & Docker Compose (for containerized setup)
The easiest way to get started with both SpiceDB and Saffron:
-
Clone and install
git clone https://github.com/dreaminhex/saffron.git cd saffron npm install -
Start everything with Docker Compose
docker-compose up -d
This starts:
- PostgreSQL (SpiceDB's datastore)
- SpiceDB (authorization service)
- Saffron UI
-
Run database migrations (first time only)
docker-compose exec spicedb spicedb datastore migrate head --datastore-engine postgres --datastore-conn-uri "postgres://spicedb:spicedb@postgres:5432/spicedb?sslmode=disable"
-
Initialize with mock data
Windows (PowerShell):
.\init-spicedb.ps1Linux/Mac/WSL:
chmod +x init-spicedb.sh ./init-spicedb.sh
-
Access the application
For development, run SpiceDB in Docker but Saffron locally:
-
Clone and install
git clone https://github.com/dreaminhex/saffron.git cd saffron npm install -
Start only SpiceDB services
docker-compose up -d postgres spicedb
-
Run database migrations (first time only)
docker-compose exec spicedb spicedb datastore migrate head --datastore-engine postgres --datastore-conn-uri "postgres://spicedb:spicedb@postgres:5432/spicedb?sslmode=disable"
-
Initialize SpiceDB with mock data
Windows (PowerShell):
.\init-spicedb.ps1Linux/Mac/WSL:
chmod +x init-spicedb.sh ./init-spicedb.sh
-
Run Saffron locally
npm run dev
The
.env.localfile is already configured to connect to the correct SpiceDB HTTP and gRPC endpoints. If you change ports or run SpiceDB elsewhere, update this file accordingly.
If you want to run SpiceDB without Docker Compose (requires manual PostgreSQL setup):
-
Start PostgreSQL
docker run -d --name spicedb-postgres \ -e POSTGRES_USER=spicedb \ -e POSTGRES_PASSWORD=spicedb \ -e POSTGRES_DB=spicedb \ -p 5432:5432 \ postgres:15-alpine
-
Start SpiceDB with HTTP API
docker run -d --name spicedb \ -p 50051:50051 -p 8443:8443 \ -e SPICEDB_GRPC_PRESHARED_KEY="saffron-dev-key" \ -e SPICEDB_DATASTORE_ENGINE=postgres \ -e SPICEDB_DATASTORE_CONN_URI="postgres://spicedb:spicedb@host.docker.internal:5432/spicedb?sslmode=disable" \ authzed/spicedb serve --http-enabled
-
Run database migrations
docker exec spicedb spicedb datastore migrate head \ --datastore-engine postgres \ --datastore-conn-uri "postgres://spicedb:spicedb@host.docker.internal:5432/spicedb?sslmode=disable"
-
Initialize with mock data
chmod +x init-spicedb.sh ./init-spicedb.sh
-
Configure environment
Create
.env.local:# HTTP API (used by UI for schema, relationships, permissions) SPICEDB_URL=http://localhost:8443 SPICEDB_TOKEN=saffron-dev-key # gRPC API (used only by the Terminal page for zed emulation) SPICEDB_ENDPOINT=localhost:50051 SPICEDB_PRESHARED_KEY=saffron-dev-key SPICEDB_INSECURE=true > **Note:** The UI itself uses only the HTTP API. The gRPC endpoint is used only for the Terminal page's zed command emulation. If you do not use the Terminal, you may ignore the gRPC settings.
-
Start the UI
npm run dev
Create a .env.local file in the root directory (see above for details). The default values are:
SPICEDB_URL=http://localhost:8443
SPICEDB_TOKEN=saffron-dev-key
SPICEDB_ENDPOINT=localhost:50051
SPICEDB_PRESHARED_KEY=saffron-dev-key
SPICEDB_INSECURE=trueNote: If you do not set these, the backend will fall back to legacy/test defaults (
http://localhost:8080andsomerandomkeyhere), which may not work with your setup. Always use.env.localfor local development.
The docker-compose.yml defines three services:
- postgres - PostgreSQL database (SpiceDB's datastore) on internal network
- spicedb - Authorization service
- gRPC API:
localhost:50051 - HTTP API:
localhost:8443
- gRPC API:
- saffron - Next.js UI application on
localhost:7777
All services share a saffron-network for internal communication.
The initialization scripts (init-spicedb.sh / init-spicedb.ps1) load a sample organizational structure:
Users:
ceo,cto,an_eng_director,an_eng_manager,an_engineerit_admin,an_external_user,a_villain
Groups (nested hierarchy):
csuite→engineering→applications→productname
Resources:
promserver- Managed by productname team, viewed by engineeringjira- Managed by engineering managers, viewed by all engineering
Organization:
org1- Contains all groups and resources
- Navigate to the Schema page
- A default schema has already been loaded - you can edit this from
./examples/spicedb/data/schema.yml - Edit your authorization model using SpiceDB schema language
- Use the visual view to see parsed namespaces, relations, and permissions
- Save changes directly to SpiceDB
- Go to the Relationships page
- Add relationships using smart dropdowns:
- Resource: Search existing or create new (e.g.,
business:acme-corp) - Relation: Auto-populated from your schema (e.g.,
owner,manager) - Subject: Manual entry (e.g.,
user:alice)
- Resource: Search existing or create new (e.g.,
- View, search, and filter existing relationships
Use the Check page for permission testing with the following features:
- Permission Check: Test if a subject has permission on a resource
- Expand Permission: Visualize permission trees
- Lookup Subjects: Find all subjects with a specific permission
✅ Should ALLOW:
-
CEO can admin org1
- Resource:
organization:org1 - Permission:
admin - Subject:
user:ceo
- Resource:
-
Engineer can view promserver
- Resource:
resource:promserver - Permission:
view - Subject:
user:an_engineer
- Resource:
-
CTO can manage jira
- Resource:
resource:jira - Permission:
manage - Subject:
user:cto
- Resource:
-
External user can view promserver
- Resource:
resource:promserver - Permission:
view - Subject:
user:an_external_user
- Resource:
❌ Should DENY:
-
External user cannot manage promserver
- Resource:
resource:promserver - Permission:
manage - Subject:
user:an_external_user
- Resource:
-
Villain cannot access jira
- Resource:
resource:jira - Permission:
view - Subject:
user:a_villain
- Resource:
-
Engineer cannot manage jira (only view)
- Resource:
resource:jira - Permission:
manage - Subject:
user:an_engineer
- Resource:
- Use the Terminal page for executing
zedqueries against the database
The UI creates several API routes:
GET /api/spicedb/stats- Dashboard statisticsGET /api/spicedb/health- Connection health checkGET /api/spicedb/activity- Recent activity feedGET /api/spicedb/resources- Available resources and relationsGET|POST /api/spicedb/schema- Schema managementGET|POST|DELETE /api/spicedb/relationships- Relationship CRUDPOST /api/spicedb/check- Permission checkingPOST /api/spicedb/expand- Permission expansionPOST /api/spicedb/lookup-subjects- Subject lookup
- Frontend: Next.js 13+, React, Tailwind CSS
- Backend: Next.js API routes
- Database: SpiceDB (via HTTP API)
- Styling: Tailwind CSS with custom components
- Icons: Tabler
If the initialization script hangs or SpiceDB isn't responding:
-
Check if SpiceDB migrations have been run:
docker-compose logs spicedb | grep -i migrate -
If you see "datastore is not migrated" errors, run migrations:
docker-compose exec spicedb spicedb datastore migrate head --datastore-engine postgres --datastore-conn-uri "postgres://spicedb:spicedb@postgres:5432/spicedb?sslmode=disable"
-
Restart SpiceDB after migration:
docker-compose restart spicedb
To completely reset everything:
docker-compose down -v
docker-compose up -d
# Run migrations again
docker-compose exec spicedb spicedb datastore migrate head --datastore-engine postgres --datastore-conn-uri "postgres://spicedb:spicedb@postgres:5432/spicedb?sslmode=disable"
# Initialize data
./init-spicedb.shnpm run dev # Start development server
npm run build # Build for production
npm start # Start production server# Start all services (Saffron, SpiceDB, PostgreSQL)
docker-compose up -d
# Start only SpiceDB services (for local Saffron development)
docker-compose up -d postgres spicedb
# View logs
docker-compose logs -f saffron
docker-compose logs -f spicedb
# Stop services
docker-compose down
# Stop and remove volumes (fresh start)
docker-compose down -v
# Rebuild Saffron container
docker-compose up -d --build saffronWhen Saffron runs locally:
- SpiceDB gRPC:
localhost:50051 - SpiceDB HTTP:
http://localhost:8443
When Saffron runs in Docker:
- SpiceDB gRPC:
spicedb:50051(internal network) - SpiceDB HTTP:
http://spicedb:8443(internal network)
GPL v3





