Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

215 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SystemForge

Go CI Go Lint Go SAST Go Report Card Docs Visualization License

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:

  1. Side-by-Side: Create cf_* tables alongside existing tables
  2. Dual-Write: Write to both old and new tables
  3. Cutover: Switch reads to SystemForge tables
  4. 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.

About

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.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Contributors

Languages