PRD: CoreForge Authorization (SpiceDB)¶
Status: Draft
This PRD defines the requirements for CoreForge's relationship-based authorization system using SpiceDB (Zanzibar-style ReBAC).
Overview¶
CoreForge Authorization provides a unified, scalable authorization framework for multi-tenant SaaS applications. It replaces simple RBAC with relationship-based access control (ReBAC) using SpiceDB, enabling fine-grained permissions based on object relationships.
Goals¶
- Relationship-Based Access: Model permissions as relationships between subjects and objects
- Scalable Performance: Sub-10ms permission checks at scale
- Unified Schema: Shared authorization patterns across CoreForge apps
- Marketplace Integration: Native support for licensing and entitlements
- Migration Path: Smooth transition from existing RBAC/Casbin
Why SpiceDB?¶
| Feature | Simple RBAC | Casbin | SpiceDB |
|---|---|---|---|
| Relationship modeling | No | Limited | Native |
| Nested permissions | No | Complex | Native (-> syntax) |
| Performance at scale | Good | Good | Excellent |
| Caching | Manual | Manual | Built-in |
| Consistency guarantees | App-managed | App-managed | ZedTokens |
| Schema validation | None | Limited | Comprehensive |
User Stories¶
Developer Stories¶
US-1: As a developer, I can define authorization schemas using SpiceDB's Zed language.
US-2: As a developer, I can check permissions with a simple Can(subject, action, resource) API.
US-3: As a developer, I can sync database changes to SpiceDB automatically.
US-4: As a developer, I can test authorization rules without a running SpiceDB instance.
Application Stories¶
US-5: As an app, I can model organization hierarchies (owner > admin > member > viewer).
US-6: As an app, I can model resource ownership and sharing.
US-7: As an app, I can model marketplace licensing (creator org -> listing -> licensed org).
US-8: As an app, I can check if a user has access through any valid path (direct, org membership, license).
Operations Stories¶
US-9: As an operator, I can deploy SpiceDB with PostgreSQL or CockroachDB backend.
US-10: As an operator, I can monitor authorization performance and errors.
US-11: As an operator, I can migrate schemas without downtime.
Architecture¶
System Overview¶
┌─────────────────────────────────────────────────────────────────────────────┐
│ APPLICATION LAYER │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ AcademyOS │ │ DashForge │ │ Future App │ │ CoreForge Identity │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
│ │ │ │ │ │
│ └────────────────┴────────────────┴─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ CoreForge Authz Package ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ ││
│ │ │ Provider │ │ Syncer │ │ Middleware │ │ Testing │ ││
│ │ │ Interface │ │ │ │ │ │ Utilities │ ││
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └─────────────────┘ ││
│ └─────────┼────────────────┼────────────────┼─────────────────────────────┘│
│ │ │ │ │
└────────────┼────────────────┼────────────────┼───────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ SpiceDB │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ Schema (Zed) ││
│ │ ├── principal ││
│ │ ├── organization (consumer_org) ││
│ │ ├── creator_org (publisher/tenant) ││
│ │ ├── listing ││
│ │ ├── license ││
│ │ └── [app-specific definitions] ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ Relationships (Tuples) ││
│ │ principal:user-123 | member | organization:acme ││
│ │ organization:acme | licensed_org | listing:course-456 ││
│ └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘
Module Structure¶
coreforge/
├── authz/
│ ├── types.go # Principal, Resource, Action types
│ ├── roles.go # Role hierarchy utilities
│ ├── provider.go # Provider interface
│ ├── middleware.go # HTTP middleware
│ │
│ ├── spicedb/ # SpiceDB implementation
│ │ ├── provider.go # SpiceDB provider
│ │ ├── client.go # gRPC client wrapper
│ │ ├── schema.go # Schema management
│ │ └── syncer.go # Relationship syncer
│ │
│ ├── simple/ # Simple in-memory provider
│ │ └── provider.go
│ │
│ └── providertest/ # Conformance testing
│ ├── providertest.go
│ └── mock.go
│
├── marketplace/ # Marketplace integration (NEW)
│ ├── types.go
│ ├── service.go
│ └── authz.go # SpiceDB sync for licenses
│
└── docs/design/
├── FEAT_AUTHZ_PRD.md # This document
└── FEAT_AUTHZ_TRD.md # Technical reference
Authorization Model¶
Core Concepts¶
| Concept | Description | Example |
|---|---|---|
| Principal | Entity requesting access | User, Service, API Key |
| Resource | Object being accessed | Organization, Course, Dashboard |
| Relation | Relationship between principal and resource | owner, admin, member, viewer |
| Permission | Action that can be performed | manage, edit, view, delete |
Relationship Hierarchy¶
Permission Flow (computed via relations):
owner ────────────────────────────────────────┐
│ │
▼ ▼
admin ─────────────────────────────────┐ manage
│ │ │
▼ ▼ ▼
editor/creator ────────────────────┐ edit ◀──┤
│ │ │ │
▼ ▼ ▼ │
viewer/member ─────────────────┐ view ◀──────┘
│ │
▼ │
use ◀──┘
SpiceDB Schema (Shared Base)¶
// =============================================================================
// PRINCIPALS
// =============================================================================
definition principal {}
// =============================================================================
// ORGANIZATIONS
// =============================================================================
// Consumer organization - uses products, subscribes to platform
definition organization {
relation owner: principal
relation admin: principal
relation member: principal
relation viewer: principal
// Membership hierarchy
permission manage = owner + admin
permission edit = manage + member
permission view = edit + viewer
permission use = view
// Operations
permission delete = owner
permission settings = manage
permission billing = owner + admin
permission invite_member = manage
permission purchase = manage
}
// Creator organization - creates and sells products
definition creator_org {
relation owner: principal
relation admin: principal
relation creator: principal
relation reviewer: principal
// Membership hierarchy
permission manage = owner + admin
permission create = manage + creator
permission review = manage + reviewer
// Operations
permission delete = owner
permission settings = manage
permission billing = owner + admin
permission publish = manage
permission view_analytics = manage
}
// =============================================================================
// MARKETPLACE
// =============================================================================
definition listing {
relation creator_org: creator_org
relation owner: principal
relation licensed_org: organization
permission manage = owner + creator_org->manage
permission edit = manage + creator_org->create
permission review = creator_org->review
permission publish = creator_org->publish
permission use = licensed_org->use
permission view = use + edit
}
definition license {
relation listing: listing
relation organization: organization
relation purchased_by: principal
relation seat_holder: principal
permission view = organization->manage + purchased_by
permission use = seat_holder + organization->use
permission manage = organization->manage
permission transfer = organization->manage
}
App-Specific Extensions¶
Applications extend the base schema with domain-specific definitions:
AcademyOS:
definition course {
relation tenant: creator_org
relation owner: principal
relation listing: listing
relation enrolled: principal
permission manage = owner + tenant->manage
permission edit = manage + tenant->create
permission view = enrolled + listing->use + edit
permission enroll = listing->use
}
DashForge:
definition dashboard_template {
relation publisher: creator_org
relation owner: principal
relation listing: listing
permission manage = owner + publisher->manage
permission edit = manage + publisher->create
permission use = listing->use
permission view = use + edit
}
Provider Interface¶
// Provider defines the authorization interface.
type Provider interface {
// Can checks if the principal can perform the action on the resource.
Can(ctx context.Context, principal Principal, action Action, resource Resource) (bool, error)
// CanAll checks if the principal can perform all actions on the resource.
CanAll(ctx context.Context, principal Principal, actions []Action, resource Resource) (bool, error)
// CanAny checks if the principal can perform any of the actions on the resource.
CanAny(ctx context.Context, principal Principal, actions []Action, resource Resource) (bool, error)
// ListPermissions returns all permissions the principal has on the resource.
ListPermissions(ctx context.Context, principal Principal, resource Resource) ([]Action, error)
// ListResources returns all resources of the given type the principal can access.
ListResources(ctx context.Context, principal Principal, resourceType ResourceType, action Action) ([]Resource, error)
}
// Syncer syncs relationships to the authorization backend.
type Syncer interface {
// WriteRelationship creates or updates a relationship.
WriteRelationship(ctx context.Context, resource Resource, relation string, subject Principal) error
// DeleteRelationship removes a relationship.
DeleteRelationship(ctx context.Context, resource Resource, relation string, subject Principal) error
// BulkWrite performs multiple relationship operations atomically.
BulkWrite(ctx context.Context, ops []RelationshipOp) error
}
Sync Strategy¶
Event-Driven Sync¶
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Database │ │ Syncer │ │ SpiceDB │
│ (Ent) │ │ │ │ │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ Hook: OnCreate │ │
│──────────────────▶│ │
│ │ WriteRelationship│
│ │──────────────────▶│
│ │ │
│ Hook: OnUpdate │ │
│──────────────────▶│ │
│ │ Update Relations │
│ │──────────────────▶│
│ │ │
│ Hook: OnDelete │ │
│──────────────────▶│ │
│ │ DeleteRelationship
│ │──────────────────▶│
Sync Events¶
| Entity | Event | SpiceDB Action |
|---|---|---|
| Organization | Created | Create org with owner relation |
| Organization | Member added | Add member relation |
| Organization | Member role changed | Update relation |
| Organization | Member removed | Delete relation |
| License | Created | Add licensed_org relation to listing |
| License | Seat assigned | Add seat_holder relation |
| License | Revoked | Delete licensed_org relation |
| CreatorOrg | Created | Create creator_org with owner relation |
| Listing | Published | Add to marketplace |
Performance Requirements¶
| Operation | Target | Notes |
|---|---|---|
| Permission check | < 10ms p99 | With caching |
| Relationship write | < 50ms | Single operation |
| Bulk write (100 ops) | < 200ms | Atomic |
| List resources (100) | < 100ms | Paginated |
Caching Strategy¶
- ZedTokens: Use SpiceDB's consistency tokens for cache invalidation
- Local Cache: Cache positive permission results (5 min TTL)
- Negative Cache: Short TTL (30s) for negative results
- Cache Keys:
{principal_type}:{principal_id}:{action}:{resource_type}:{resource_id}
Migration Path¶
From Casbin¶
- Phase 1: Deploy SpiceDB alongside Casbin
- Phase 2: Dual-write to both systems
- Phase 3: Read from SpiceDB, write to both
- Phase 4: Read and write from SpiceDB only
- Phase 5: Remove Casbin
From Simple RBAC¶
- Phase 1: Map existing roles to SpiceDB relations
- Phase 2: Sync existing memberships to SpiceDB
- Phase 3: Switch permission checks to SpiceDB
- Phase 4: Remove old RBAC code
Testing Strategy¶
Unit Tests¶
- Mock provider for fast tests
- Conformance tests for all providers
- Schema validation tests
Integration Tests¶
- SpiceDB container for CI
- Full flow tests (sync + check)
- Performance benchmarks
Conformance Test Suite¶
// providertest/providertest.go
func RunConformanceTests(t *testing.T, provider Provider, syncer Syncer) {
t.Run("OwnerCanManage", func(t *testing.T) { ... })
t.Run("AdminCanManage", func(t *testing.T) { ... })
t.Run("MemberCanView", func(t *testing.T) { ... })
t.Run("NonMemberCannotAccess", func(t *testing.T) { ... })
t.Run("LicenseGrantsAccess", func(t *testing.T) { ... })
// ...
}
Success Metrics¶
- Latency: p99 permission check < 10ms
- Availability: 99.9% uptime for authorization service
- Accuracy: Zero false positives/negatives in permission checks
- Developer Experience: < 1 hour to integrate new app
Out of Scope (v1)¶
- Cedar policy language support (future)
- OPA integration (future)
- Custom policy expressions (future)
- Real-time permission streaming (future)
Dependencies¶
- SpiceDB v1.x
- PostgreSQL or CockroachDB (for SpiceDB backend)
- CoreForge identity module