GitHub - grokify/systemforge: SystemForge is a batteries-included Go platform module providing reusable identity, session, authorization, and feature flags for multi-tenant SaaS applications. Think of it as Django/Laravel-style conveniences for Go.
SystemForge is a batteries-included Go platform module providing reusable identity, session, authorization, and feature flags for multi-tenant SaaS applications. Think of it as Django/Laravel-style conveniences for Go.
Features
Identity Module
- ๐ค Users - Email, password hash (Argon2id), platform admin flag
- ๐ข Organizations - Multi-tenant with name, slug, plan, settings
- ๐ Memberships - User-org relationships with flexible roles
- ๐ Ownership Transfer - Transaction-safe organization ownership transfers
- ๐ OAuth Accounts - External OAuth provider links (GitHub, Google)
- ๐ API Keys - Machine-to-machine authentication with scopes
OAuth 2.0 Server (Fosite)
- ๐ Authorization Code + PKCE - Secure browser-based auth
- ๐ค Client Credentials - Service-to-service auth
- ๐ Refresh Token - With rotation and theft detection
- ๐ JWT Bearer (RFC 7523) - Service account authentication
- โ๏ธ Service Accounts - Non-human identities with RSA/EC key pairs
- ๐ Token Introspection & Revocation - RFC 7662/7009
Session Module
- ๐ซ JWT Service - Access/refresh token generation with HS256/RS256/ES256
- ๐ DPoP (RFC 9449) - Proof-of-possession token binding
- ๐ฅ๏ธ BFF Pattern - Backend for Frontend with server-side sessions
- ๐พ OmniStorage Backend - Production session storage with Redis, size limits, and observability
- ๐ OAuth Handlers - GitHub and Google social login
- ๐ก๏ธ Middleware - JWT Bearer and API key authentication
Authorization Module
- ๐ฅ RBAC/ReBAC - Role and relationship-based access control
- ๐ SpiceDB Provider - Zanzibar-style fine-grained authorization
- โจ Simple Provider - Lightweight permission checking
- ๐ง HTTP Middleware - Route protection for Chi and stdlib
Observability
- ๐ Vendor-Agnostic - Integrates with omniobserve
- ๐ Multiple Backends - OTLP, Datadog, New Relic, Dynatrace
- ๐ Pre-Built Metrics - CoreAuth, rate limiting, JWT/API key validation
- ๐ Distributed Tracing - Automatic span creation for OAuth flows
- ๐ slog Integration - Trace-correlated structured logging
Marketplace
- ๐ Listings - Product catalog with tiers and pricing
- ๐ณ Stripe Integration - Subscription billing and webhooks
- ๐ Licensing - Per-seat, unlimited, and time-limited licenses
- ๐ฅ Seat Management - Assign and revoke user access
Feature Flags
- ๐ฉ Flag Engine - Boolean, percentage, and user list flags
- ๐ข Organization Scoping - Per-org flag evaluation
- ๐พ In-Memory Store - Development and testing
Row-Level Security (RLS)
- ๐๏ธ PostgreSQL RLS - Policy generation and session variables
- ๐ Tenant Isolation - Multi-tenant data separation
- ๐ Ent Integration - Transaction helpers with tenant context
Multi-App Platform
- ๐๏ธ Multi-App Server - Run multiple SaaS apps on shared infrastructure
- ๐ฆ Schema Isolation - Each app gets its own PostgreSQL schema
- ๐ X-App-ID Routing - Header-based request routing to app backends
- ๐ AppBackend Interface - Composable app registration with lifecycle hooks
- ๐พ Shared Caching - Redis or in-memory with app-scoped prefixes
Installation
go get github.com/grokify/systemforge
Quick Start
Using Identity Schemas
SystemForge provides Ent schemas with cf_ table prefix for side-by-side migration.
Direct Schema Usage
package main import ( "context" "github.com/grokify/systemforge/identity/ent" _ "github.com/lib/pq" ) func main() { client, err := ent.Open("postgres", "postgres://...") if err != nil { panic(err) } defer client.Close() // Run migrations if err := client.Schema.Create(context.Background()); err != nil { panic(err) } // Create a user user, err := client.User.Create(). SetEmail("user@example.com"). SetName("Example User"). Save(context.Background()) }
Mixin Composition (Recommended)
Compose SystemForge mixins into your own schemas:
// your-app/ent/schema/user.go package schema import ( "entgo.io/ent" "entgo.io/ent/schema/field" cfmixin "github.com/grokify/systemforge/identity/ent/mixin" ) type User struct { ent.Schema } func (User) Mixin() []ent.Mixin { return []ent.Mixin{ cfmixin.UUIDMixin{}, // UUID primary key cfmixin.TimestampMixin{}, // created_at, updated_at } } func (User) Fields() []ent.Field { return []ent.Field{ field.String("username").Unique(), // App-specific fields... } }
JWT Authentication
import ( "github.com/grokify/systemforge/session/jwt" "github.com/grokify/systemforge/session/middleware" ) // Create JWT service svc, err := jwt.NewService(&jwt.Config{ Secret: []byte("your-secret-key"), AccessTokenExpiry: 15 * time.Minute, RefreshTokenExpiry: 7 * 24 * time.Hour, Issuer: "your-app", }) // Generate tokens pair, err := svc.GenerateTokenPair(userID, email, name) // Middleware for protected routes r.Use(middleware.JWT(svc))
DPoP Token Binding
import "github.com/grokify/systemforge/session/dpop" // Generate DPoP key pair (BFF side) keyPair, err := dpop.GenerateKeyPair() // Create proof for API request proof, err := dpop.CreateProofWithOptions(keyPair, "POST", "https://api.example.com/data", dpop.ProofOptions{ AccessToken: accessToken, }) // Verify proof (API side) verifier := dpop.NewVerifier(dpop.VerificationConfig{ MaxAge: 5 * time.Minute, }) result, err := verifier.Verify(proofJWT, dpop.VerificationRequest{ Method: "POST", URI: "https://api.example.com/data", AccessToken: accessToken, })
BFF Pattern
import "github.com/grokify/systemforge/session/bff" // Create BFF proxy proxy := bff.NewProxy(bff.ProxyConfig{ Backend: "https://api.internal.example.com", AllowedOrigins: []string{"https://app.example.com"}, SessionStore: bff.NewMemoryStore(), }) // Mount proxy handler r.Handle("/api/*", proxy.Handler())
Authorization
import ( "github.com/grokify/systemforge/authz" "github.com/grokify/systemforge/authz/simple" ) // Create authorization provider provider := simple.NewProvider(simple.Config{ AllowOwnerFullAccess: true, AllowPlatformAdminAll: true, }) // Add role permissions provider.AddRolePermissions("admin", []string{ "users:read", "users:write", "settings:read", "settings:write", }) provider.AddRolePermissions("member", []string{ "users:read", }) // Use middleware mw := authz.NewMiddleware(provider) r.With(mw.RequireAction(authz.ResourceType("users"), authz.ActionRead)).Get("/users", listUsers)
Module Structure
github.com/grokify/systemforge/
โโโ identity/ # User, Organization, Membership, OAuth
โ โโโ ent/schema/ # Ent schemas with cf_ prefix
โ โโโ apikey/ # API key service
โ โโโ oauth/ # OAuth 2.0 server (Fosite)
โ โโโ password.go # Argon2id hashing
โ โโโ service.go # Identity service interfaces
โ
โโโ session/ # Session management
โ โโโ jwt/ # JWT service with DPoP claims
โ โโโ dpop/ # DPoP proof-of-possession
โ โโโ bff/ # Backend for Frontend pattern
โ โโโ oauth/ # Social login handlers
โ โโโ middleware/ # Auth middleware
โ โโโ ratelimit/ # Rate limiting with observability
โ
โโโ observability/ # Vendor-agnostic observability
โ โโโ observability.go # Core wrapper for omniobserve
โ โโโ middleware.go # HTTP request tracing
โ โโโ metrics.go # Pre-defined metric names
โ
โโโ authz/ # Authorization
โ โโโ simple/ # Simple RBAC provider
โ โโโ spicedb/ # SpiceDB ReBAC provider
โ โโโ noop/ # No-op syncer for testing
โ โโโ providertest/ # Provider test suite
โ โโโ middleware.go # HTTP middleware
โ
โโโ marketplace/ # SaaS marketplace
โ โโโ listing.go # Product listings and tiers
โ โโโ license.go # License management
โ โโโ subscription.go # Subscription handling
โ โโโ stripe/ # Stripe billing integration
โ
โโโ featureflags/ # Feature flag engine
โ โโโ stores/ # Flag stores
โ
โโโ multiapp/ # Multi-app platform
โ โโโ server.go # Multi-app server with routing
โ โโโ app.go # AppBackend interface
โ โโโ database.go # Schema-per-app isolation
โ โโโ cache.go # Redis and memory cache
โ โโโ context.go # App context helpers
โ
โโโ rls/ # PostgreSQL Row-Level Security
โโโ rls.go # Policy generation
โโโ middleware.go # HTTP middleware
Design Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Table prefix | cf_ |
Avoids conflicts, enables side-by-side migration |
| Role storage | String field | Apps define own vocabularies (owner/admin/member) |
| OAuth pattern | Fosite library | Production-ready, RFC-compliant OAuth 2.0 |
| Refresh tokens | Database-backed | Enables revocation, theft detection |
| Primary keys | UUID | Modern, distributed-friendly |
| Password hashing | Argon2id | OWASP recommended, memory-hard |
Database Tables
SystemForge creates the following tables (all prefixed with cf_):
| Table | Description |
|---|---|
cf_users |
User accounts |
cf_organizations |
Multi-tenant organizations |
cf_memberships |
User-organization relationships |
cf_oauth_accounts |
External OAuth provider links |
cf_refresh_tokens |
JWT refresh token tracking |
cf_api_keys |
Developer API keys |
cf_oauth_apps |
OAuth client applications |
cf_oauth_app_secrets |
Client secrets (hashed) |
cf_oauth_tokens |
Issued OAuth tokens |
cf_oauth_auth_codes |
Authorization codes |
cf_oauth_consents |
User consent records |
cf_service_accounts |
Non-human identities |
cf_service_account_key_pairs |
RSA/EC key pairs |
Migration Strategy
For existing apps, SystemForge supports side-by-side migration:
- Side-by-Side: Create
cf_*tables alongside existing tables - Dual-Write: Write to both old and new tables
- Cutover: Switch reads to SystemForge tables
- Cleanup: Remove old tables
Documentation
Full documentation is available via MkDocs:
# Install MkDocs pip install mkdocs mkdocs-material # Serve locally mkdocs serve # Build static site mkdocs build
Contributing
Contributions are welcome! Please read the contributing guidelines before submitting PRs.
License
MIT License - see LICENSE file for details.