Skip to content

Oxmus/platform-base-template

Repository files navigation

Multi-Tenant SaaS Platform Base

A production-ready Elixir/Phoenix platform with authentication, authorization, multi-tenancy, and white-labeling built-in.

Extracted from a real production SaaS application, this platform provides enterprise-grade features out of the box, allowing you to focus on your domain-specific business logic rather than rebuilding common infrastructure.

What's Included

Core Platform Features

  • Multi-Tenancy - Schema-per-tenant isolation using PostgreSQL
  • Authentication - Email/password with AshAuthentication
  • Authorization - Role-Based Access Control (RBAC) with policies
  • Multi-Organization Support - Users can belong to multiple organizations
  • White-Labeling - Per-organization branding and subdomain management
  • Three-Tier Hierarchy - Platform Owner → MSP → Client organizations
  • Test Suite - Comprehensive test coverage with factories

Applications

App Purpose Port
core Ash Resources & Domain logic (multi-tenant, RBAC) -
web Public website (Phoenix + LiveView + LiveSvelte) 5000
admin Admin portal (multi-org management) 5001
client Client portal (tenant-scoped UI) 5002
ui Shared UI components (daisyUI + Svelte) -
build_tools Custom Mix tasks and testing utilities -
dev_tools Development tools (E2E test runner UI) -

Technology Stack

  • Elixir 1.18+
  • Erlang OTP 27+
  • Phoenix 1.8.1
  • Ash Framework 3.0+ (declarative resource framework)
  • Phoenix LiveView 1.1.13 (server-rendered interactivity)
  • LiveSvelte 0.16.0 (Svelte 5 components in LiveView)
  • PostgreSQL 16+ (with schema-per-tenant multitenancy)
  • TailwindCSS 4.1+ & daisyUI 5.1+ (styling)

Quick Start

Prerequisites

# Install asdf (version manager)
# See: https://asdf-vm.com/guide/getting-started.html

# Install plugins
asdf plugin add elixir
asdf plugin add erlang
asdf plugin add nodejs
asdf plugin add postgres

Setup

# 1. Install versions from .tool-versions
asdf install

# 2. Install dependencies
mix deps.get

# 3. Create and migrate databases
mix ash_postgres.create
mix ash_postgres.migrate

# 4. Start the Phoenix apps
mix phx.server

# Access:
# - Web:    http://localhost:5000
# - Admin:  http://localhost:5001
# - Client: http://localhost:5002

Initial Super Admin

# Run in iex -S mix
Core.Resources.Identity
|> Ash.Changeset.for_create(:register_with_password, %{
  email: "[email protected]",
  password: "SecurePassword123!",
  password_confirmation: "SecurePassword123!",
  role: :super_admin
})
|> Ash.create!()

Architecture

Core Resources

Located in apps/core/lib/core/resources/:

Authentication & Authorization:

  • Identity - Authentication credentials (public schema)
  • User - User profiles (tenant-scoped)
  • Organization - Organizations (global, with tenant schemas)
  • OrganizationMembership - User-organization relationships
  • Token - API tokens and session management

RBAC:

  • Role - Role definitions (tenant-scoped)
  • Permission - Fine-grained permissions (tenant-scoped)
  • UserRole - User-role assignments (tenant-scoped)

White-Labeling:

  • BrandingAsset - Logo, favicon, colors (per-organization)

Optional Analytics:

  • AIVisit - Track AI crawler visits (ChatGPT, Claude, etc.)

Multi-Tenancy Pattern

Schema-per-tenant isolation:

# Organizations create separate PostgreSQL schemas
org = Organization.create!(%{name: "Acme Corp", slug: "acme"})
# Creates schema: "org_acme"

# Tenant resources require schema context
User
|> Ash.Changeset.for_create(:create, %{email: "[email protected]"})
|> Ash.Changeset.set_tenant("acme")  # ← Required for tenant data
|> Ash.create!()

Global vs Tenant Resources:

  • Global (multitenancy { global? true }): Organizations, Identities
  • Tenant (multitenancy { strategy :context }): Users, Roles, Permissions

See: docs/architecture/MULTI_ORGANIZATION_ARCHITECTURE.md

Authentication Flow

  1. User logs in with email/password
  2. Identity verified in public schema
  3. Session includes organization_id for tenant context
  4. User profile loaded from tenant schema
  5. Policies check role and permissions

See: docs/architecture/AUTHENTICATION_ARCHITECTURE.md

Development

Testing

# All tests
mix test

# Unit tests only (exclude E2E)
mix test.unit

# Re-run failed tests
mix test.quick

# Coverage report
mix coverage

# E2E tests with Playwright
mix test.playwright

See: docs/testing/TESTING_WORKFLOW.md

Code Quality

# Format code
mix format

# Static analysis
mix credo --strict

# Type checking
mix dialyzer

# All quality checks
mix quality

Database

# Create databases
mix ash_postgres.create

# Run migrations
mix ash_postgres.migrate

# Drop databases
mix ash_postgres.drop

# Reset (drop + create + migrate)
mix ash_postgres.reset

Customization

Adding Domain Resources

  1. Create resource in apps/core/lib/core/resources/:
defmodule Core.Resources.BlogPost do
  use Ash.Resource,
    domain: Core.Domain,
    data_layer: AshPostgres.DataLayer

  postgres do
    table "blog_posts"
    repo Core.Repo
  end

  multitenancy do
    strategy :context  # Tenant-scoped
  end

  attributes do
    uuid_primary_key :id
    attribute :title, :string, allow_nil?: false
    attribute :content, :string
    timestamps()
  end

  actions do
    defaults [:read, :destroy, create: [:title, :content], update: [:title, :content]]
  end
end
  1. Register in domain (apps/core/lib/core/domain.ex):
resources do
  # ... existing resources ...
  resource(Core.Resources.BlogPost)
end
  1. Generate migration:
mix ash_postgres.generate_migrations --name add_blog_posts
  1. Add tests in apps/core/test/core/resources/blog_post_test.exs

Customizing Branding

Update web app marketing content:

  • apps/web/lib/web_web/live/ - LiveView pages
  • apps/web/assets/static/images/ - Logo and images
  • apps/web/assets/css/app.css - Brand colors

For portal branding (admin/client):

  • Managed via BrandingAsset resource
  • Upload logo, set colors per organization
  • UI automatically applies white-label branding

See: docs/architecture/BRANDING_PATTERNS.md

Environment Configuration

Copy .env.example to .env and configure:

# Your primary domain (for white-label subdomains)
CLOUDFLARE_DEFAULT_DOMAIN=example.ai

# Cloudflare API (optional - for branded subdomain automation)
CLOUDFLARE_PRIMARY_ZONE_ID=your_zone_id
CLOUDFLARE_PRIMARY_API_TOKEN=your_api_token

# Production hosts
ADMIN_HOST=admin.example.com
CLIENT_HOST=client.example.com
WEB_HOST=example.com

Documentation

Architecture

Development Guides

App-Specific

Deployment

The platform is deployment-ready with:

  • Mix releases configured
  • Docker support (TODO: add Dockerfile)
  • Health checks on all endpoints
  • Asset digesting for production
  • Migration runner for zero-downtime deploys

See: docs/deployment/ for deployment guides.

Contributing

This is an extracted platform base. When using it for your project:

  1. Fork or copy this repository
  2. Customize the domain resources for your use case
  3. Remove unused features (e.g., AIVisit if not tracking crawlers)
  4. Add your business logic in new resources
  5. Update branding and marketing content

Extraction History

This platform was extracted from a production SaaS application in January 2026. The extraction process involved:

  • ✅ Removed domain-specific resources (vendor management, call logs)
  • ✅ Removed domain-specific UI (asset navigator, billing pages)
  • ✅ Genericized configuration (domains, branding)
  • ✅ Cleaned up documentation
  • ✅ Verified compilation and tests

See: docs/extraction/ for extraction details.

License

[TODO: Add your license here]

Support

  • Documentation: See docs/ directory
  • Issues: [TODO: Add issue tracker URL]
  • Questions: [TODO: Add discussion forum or contact]

Built with Ash Framework - Declarative resource-oriented application framework for Elixir.

About

Generic multi-tenant SaaS platform template built with Phoenix, Ash Framework, LiveView, and LiveSvelte. Features: schema-per-tenant isolation, RBAC, multi-organization support, white-label branding.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors