COMPLETE ARCHITECTURE - nself-org/cli GitHub Wiki

Complete Architecture Documentation

Version 0.9.9 | System Design, Patterns, and Technical Decisions


Table of Contents

  1. Architecture Overview
  2. C4 Model Diagrams
  3. Component Architecture
  4. Data Flow
  5. Security Architecture
  6. Scalability Architecture
  7. Design Decisions
  8. Technology Stack
  9. Integration Patterns

Architecture Overview

nself is a full-stack backend platform built on modern cloud-native principles with a focus on:

  • Self-hosting - Complete infrastructure control
  • Multi-tenancy - Isolated data per tenant with shared infrastructure
  • Scalability - Horizontal and vertical scaling capabilities
  • Security - Defense-in-depth with RLS, JWT, SSL, and audit logging
  • Developer Experience - GraphQL-first API with CLI automation

Architectural Principles

  1. Separation of Concerns - Each service has a single responsibility
  2. API-First - GraphQL as primary interface, REST for legacy compatibility
  3. Database-Centric - PostgreSQL as source of truth, RLS for isolation
  4. Stateless Services - Services can be restarted/scaled without data loss
  5. Configuration as Code - Everything defined in .env and generated files
  6. Zero-Trust Security - Every request authenticated and authorized
  7. Observability Built-In - Metrics, logs, and traces from day one

C4 Model Diagrams

Level 1: System Context

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      External Actors                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  End Users   β”‚      β”‚  Developers  β”‚      β”‚  Admins      β”‚
    β”‚              β”‚      β”‚              β”‚      β”‚              β”‚
    β”‚ Web/Mobile   β”‚      β”‚ CLI/API      β”‚      β”‚ Dashboard    β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚                     β”‚                     β”‚
           β”‚                     β–Ό                     β”‚
           β”‚            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚
           β”‚            β”‚  GraphQL API    β”‚            β”‚
           └───────────►│  (Hasura)       β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚                        β”‚                        β”‚
        β–Ό                        β–Ό                        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  nself        β”‚      β”‚   PostgreSQL    β”‚      β”‚  Object Storageβ”‚
β”‚  Platform     │◄────►│   Database      β”‚      β”‚   (MinIO)      β”‚
β”‚               β”‚      β”‚                 β”‚      β”‚                β”‚
β”‚ β€’ Auth        β”‚      β”‚ β€’ Data          β”‚      β”‚ β€’ Files        β”‚
β”‚ β€’ Functions   β”‚      β”‚ β€’ RLS           β”‚      β”‚ β€’ Media        β”‚
β”‚ β€’ Monitoring  β”‚      β”‚ β€’ Multi-tenant  β”‚      β”‚ β€’ Backups      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚
        β”‚
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  External     β”‚
β”‚  Services     β”‚
β”‚               β”‚
β”‚ β€’ Email       β”‚
β”‚ β€’ Payment     β”‚
β”‚ β€’ Analytics   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

External Systems:

  • End Users - Access via web/mobile applications
  • Developers - Interact via CLI, API, and Hasura Console
  • Administrators - Manage via Admin Dashboard and monitoring tools
  • External Services - Email providers, payment gateways, analytics

Level 2: Container Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        nself Platform                                β”‚
β”‚                      Docker Compose Deployment                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

    Internet
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚     Nginx       β”‚  Port 80/443
β”‚  Reverse Proxy  β”‚  β€’ SSL Termination
β”‚                 β”‚  β€’ Load Balancing
β”‚                 β”‚  β€’ Rate Limiting
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β”‚  Routes to Services
         β”‚
    β”Œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚    β”‚                β”‚               β”‚              β”‚
    β–Ό    β–Ό                β–Ό               β–Ό              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Hasura  β”‚  β”‚   Auth     β”‚  β”‚ Functionsβ”‚  β”‚  Admin  β”‚  β”‚  Custom  β”‚
β”‚ GraphQL β”‚  β”‚  Service   β”‚  β”‚ Runtime  β”‚  β”‚   UI    β”‚  β”‚ Services β”‚
β”‚         β”‚  β”‚            β”‚  β”‚          β”‚  β”‚         β”‚  β”‚ (CS_1-10)β”‚
β”‚ :8080   β”‚  β”‚   :4000    β”‚  β”‚  :3001   β”‚  β”‚  :3000  β”‚  β”‚  :800x   β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
     β”‚             β”‚              β”‚             β”‚            β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚
                   β”‚              β”‚                          β”‚
                   β–Ό              β–Ό                          β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚     PostgreSQL           β”‚            β”‚   Redis    β”‚
            β”‚   Port 5432              β”‚            β”‚  :6379     β”‚
            β”‚                          β”‚            β”‚            β”‚
            β”‚ β€’ Application Data       β”‚            β”‚ β€’ Sessions β”‚
            β”‚ β€’ Auth Tables            β”‚            β”‚ β€’ Cache    β”‚
            β”‚ β€’ Multi-tenant Schemas   β”‚            β”‚ β€’ Queues   β”‚
            β”‚ β€’ Row Level Security     β”‚            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚    MinIO     β”‚            β”‚  Monitoring Stack    β”‚
     β”‚  S3 Storage  β”‚            β”‚                      β”‚
     β”‚   :9000      β”‚            β”‚ β€’ Prometheus :9090   β”‚
     β”‚              β”‚            β”‚ β€’ Grafana :3000      β”‚
     β”‚ β€’ User Files β”‚            β”‚ β€’ Loki :3100         β”‚
     β”‚ β€’ Uploads    β”‚            β”‚ β€’ Tempo :3200        β”‚
     β”‚ β€’ Backups    β”‚            β”‚ β€’ Alertmanager :9093 β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Container Responsibilities:

  1. Nginx - Entry point for all traffic

    • SSL/TLS termination
    • Routing to backend services
    • Static file serving
    • Rate limiting and DDoS protection
  2. Hasura GraphQL Engine - API layer

    • GraphQL API generation from database schema
    • Real-time subscriptions
    • Remote schemas and actions
    • Authorization via permissions and RLS
  3. Auth Service (nHost Auth) - Authentication

    • User registration and login
    • JWT token generation
    • OAuth provider integration
    • MFA support
    • Session management
  4. Functions Runtime - Serverless functions

    • Node.js/Deno runtime
    • Event-driven execution
    • Database triggers
    • Scheduled jobs
  5. Admin UI - Management interface

    • Visual service management
    • Database browser
    • User management
    • Monitoring dashboards
  6. Custom Services (CS_1 - CS_10) - User-defined

    • Generated from templates
    • Any language/framework
    • Custom business logic
    • Microservices architecture
  7. PostgreSQL - Primary database

    • Application data storage
    • Auth system tables
    • Multi-tenant data isolation (RLS)
    • Full-text search
    • JSON/JSONB support
  8. Redis - In-memory data store

    • Session storage
    • Caching layer
    • Rate limiting counters
    • Job queues (with BullMQ)
  9. MinIO - S3-compatible object storage

    • File uploads
    • Media storage
    • Backup storage
    • CDN source
  10. Monitoring Stack - Observability

    • Prometheus (metrics)
    • Grafana (visualization)
    • Loki (logs)
    • Tempo (traces)
    • Alertmanager (alerts)

Level 3: Component Diagram (Hasura)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Hasura GraphQL Engine Container               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  GraphQL       β”‚  Port 8080
    β”‚  HTTP Server   β”‚  /v1/graphql
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  /v1/metadata
             β”‚          /healthz
             β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚        β”‚                β”‚               β”‚
    β–Ό        β–Ό                β–Ό               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Query      β”‚  β”‚ Subscription β”‚  β”‚ Metadata  β”‚  β”‚  Actions   β”‚
β”‚  Engine     β”‚  β”‚  Engine      β”‚  β”‚  Manager  β”‚  β”‚  Handler   β”‚
β”‚             β”‚  β”‚              β”‚  β”‚           β”‚  β”‚            β”‚
β”‚ β€’ Parse     β”‚  β”‚ β€’ WebSocket  β”‚  β”‚ β€’ Schema  β”‚  β”‚ β€’ REST     β”‚
β”‚ β€’ Validate  β”‚  β”‚ β€’ Live       β”‚  β”‚ β€’ Perms   β”‚  β”‚ β€’ Custom   β”‚
β”‚ β€’ Execute   β”‚  β”‚ β€’ Streaming  β”‚  β”‚ β€’ Remote  β”‚  β”‚ β€’ Webhook  β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚                β”‚                β”‚              β”‚
       β”‚                β”‚                β”‚              β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
                        β”‚                               β”‚
                        β–Ό                               β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚  Authorization    β”‚             β”‚  Remote Schema  β”‚
            β”‚  Layer            β”‚             β”‚  Proxy          β”‚
            β”‚                   β”‚             β”‚                 β”‚
            β”‚ β€’ Session Vars    β”‚             β”‚ β€’ Stitching     β”‚
            β”‚ β€’ RLS Context     β”‚             β”‚ β€’ Federation    β”‚
            β”‚ β€’ Permissions     β”‚             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚  SQL Compiler     β”‚
            β”‚                   β”‚
            β”‚ β€’ Query Builder   β”‚
            β”‚ β€’ Join Optimizer  β”‚
            β”‚ β€’ RLS Injection   β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚  PostgreSQL       β”‚
            β”‚  Connection Pool  β”‚
            β”‚                   β”‚
            β”‚ β€’ Pool Manager    β”‚
            β”‚ β€’ Health Check    β”‚
            β”‚ β€’ Reconnect       β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β–Ό
                   PostgreSQL
                   Database

Component Interactions:

  1. Query Engine

    • Receives GraphQL queries
    • Parses and validates against schema
    • Compiles to SQL
    • Returns JSON response
  2. Subscription Engine

    • Maintains WebSocket connections
    • Polls database for changes (live queries)
    • Pushes updates to clients
    • Multiplexing for efficiency
  3. Authorization Layer

    • Extracts session variables from JWT
    • Sets PostgreSQL session context
    • Applies role-based permissions
    • Injects RLS policies
  4. Metadata Manager

    • Stores schema configuration
    • Manages permissions
    • Handles remote schemas
    • Triggers and event handlers
  5. Actions Handler

    • Proxies to custom REST endpoints
    • Transforms requests/responses
    • Error handling and retries

Level 4: Code-Level (PostgreSQL RLS)

-- Multi-Tenant Row Level Security Implementation

-- 1. Enable RLS on table
CREATE TABLE posts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,
    user_id UUID NOT NULL,
    title TEXT NOT NULL,
    content TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- 2. Create policies

-- Policy: Users can only see posts in their tenant
CREATE POLICY tenant_isolation_select ON posts
    FOR SELECT
    USING (tenant_id = current_setting('app.tenant_id', true)::uuid);

-- Policy: Users can only insert posts in their tenant
CREATE POLICY tenant_isolation_insert ON posts
    FOR INSERT
    WITH CHECK (
        tenant_id = current_setting('app.tenant_id', true)::uuid
        AND user_id = current_setting('hasura.user.id', true)::uuid
    );

-- Policy: Users can only update their own posts
CREATE POLICY user_update_own ON posts
    FOR UPDATE
    USING (
        tenant_id = current_setting('app.tenant_id', true)::uuid
        AND user_id = current_setting('hasura.user.id', true)::uuid
    );

-- Policy: Admin can see all posts in tenant
CREATE POLICY admin_select_all ON posts
    FOR SELECT
    USING (
        tenant_id = current_setting('app.tenant_id', true)::uuid
        AND current_setting('hasura.user.role', true) = 'admin'
    );

-- 3. Session variables set by Hasura from JWT

-- JWT payload:
{
  "sub": "user-uuid",
  "https://hasura.io/jwt/claims": {
    "x-hasura-user-id": "user-uuid",
    "x-hasura-allowed-roles": ["user", "admin"],
    "x-hasura-default-role": "user",
    "x-hasura-tenant-id": "tenant-uuid"
  }
}

-- Hasura sets PostgreSQL session:
SET LOCAL app.tenant_id = 'tenant-uuid';
SET LOCAL hasura.user.id = 'user-uuid';
SET LOCAL hasura.user.role = 'user';

-- 4. Query execution with RLS

-- User queries:
SELECT * FROM posts;

-- PostgreSQL rewrites to:
SELECT * FROM posts
WHERE tenant_id = 'tenant-uuid'  -- RLS policy applied
  AND user_id = 'user-uuid';      -- If user, not admin

Component Architecture

Request Flow (GraphQL Query)

1. Client Request
   ↓
2. Nginx (SSL termination, routing)
   ↓
3. Hasura GraphQL Engine
   β€’ Parse GraphQL query
   β€’ Validate against schema
   β€’ Extract JWT from Authorization header
   ↓
4. JWT Validation
   β€’ Verify signature (HMAC-SHA256)
   β€’ Check expiration
   β€’ Extract claims (user ID, role, tenant ID)
   ↓
5. Set Session Variables
   β€’ app.tenant_id
   β€’ hasura.user.id
   β€’ hasura.user.role
   ↓
6. Authorization Check
   β€’ Check table permissions for role
   β€’ Apply column-level permissions
   ↓
7. SQL Compilation
   β€’ GraphQL β†’ SQL
   β€’ Inject RLS policy filters
   β€’ Optimize joins
   ↓
8. Database Query
   β€’ PostgreSQL executes query
   β€’ RLS policies filter rows
   β€’ Return result set
   ↓
9. Response Transformation
   β€’ SQL result β†’ JSON
   β€’ Apply field transformations
   β€’ Nested object resolution
   ↓
10. Client Response
   β€’ JSON over HTTPS

Performance Optimizations:

  • Query Caching - Hasura caches compiled SQL
  • Connection Pooling - Reuse database connections
  • Prepared Statements - Faster query execution
  • Multiplexing - Batch subscriptions
  • Compression - gzip response payloads

Authentication Flow (JWT)

1. User Login Request
   POST /v1/auth/login
   { "email": "[email protected]", "password": "..." }
   ↓
2. Auth Service
   β€’ Hash password (bcrypt)
   β€’ Query: SELECT * FROM auth.users WHERE email = ?
   β€’ Compare password hash
   ↓
3. Password Match
   ↓
4. Generate JWT
   β€’ Header: { "alg": "HS256", "typ": "JWT" }
   β€’ Payload: {
       "sub": "user-uuid",
       "iat": 1706745600,
       "exp": 1706749200,
       "https://hasura.io/jwt/claims": {
         "x-hasura-user-id": "user-uuid",
         "x-hasura-allowed-roles": ["user"],
         "x-hasura-default-role": "user",
         "x-hasura-tenant-id": "tenant-uuid"
       }
     }
   β€’ Sign with secret key
   ↓
5. Generate Refresh Token
   β€’ Random 64-byte token
   β€’ Store in database with expiry (30 days)
   ↓
6. Response
   {
     "accessToken": "eyJhbGc...",  // 15 min expiry
     "refreshToken": "abc123...",  // 30 day expiry
     "user": { "id": "...", "email": "..." }
   }
   ↓
7. Client Stores Tokens
   β€’ Access token in memory
   β€’ Refresh token in httpOnly cookie
   ↓
8. Subsequent Requests
   Authorization: Bearer eyJhbGc...
   ↓
9. Token Refresh (when expired)
   POST /v1/auth/refresh
   { "refreshToken": "abc123..." }
   ↓
10. New Access Token Issued

Security Features:

  • Short-lived access tokens (15 minutes)
  • Long-lived refresh tokens (30 days)
  • Token rotation on refresh
  • Secure storage (httpOnly cookies for refresh token)
  • Token revocation support
  • Rate limiting on auth endpoints

Data Flow

File Upload Flow

1. Client Request
   POST https://api.yourdomain.com/v1/storage/upload
   Content-Type: multipart/form-data
   Authorization: Bearer <jwt>
   ↓
2. Nginx Route
   β†’ Forward to Hasura Actions endpoint
   ↓
3. Hasura Actions Handler
   β€’ Validate JWT
   β€’ Extract user/tenant from claims
   β€’ Proxy to custom upload function
   ↓
4. Upload Function (Node.js)
   β€’ Validate file type and size
   β€’ Generate unique filename
   β€’ Extract tenant_id from session
   ↓
5. MinIO Upload
   const s3 = new AWS.S3({
     endpoint: 'http://minio:9000',
     accessKeyId: process.env.MINIO_ACCESS_KEY,
     secretAccessKey: process.env.MINIO_SECRET_KEY,
   });

   await s3.putObject({
     Bucket: `tenant-${tenant_id}`,
     Key: filename,
     Body: fileBuffer,
     ACL: 'private',
   });
   ↓
6. Database Record
   INSERT INTO files (id, tenant_id, user_id, filename, url, size)
   VALUES (uuid, tenant_id, user_id, filename, url, file_size);
   ↓
7. Response
   {
     "fileId": "file-uuid",
     "url": "https://cdn.yourdomain.com/tenant-uuid/filename.jpg",
     "size": 1024000
   }

Security:

  • Tenant isolation - Separate S3 buckets per tenant
  • Access control - Pre-signed URLs for private files
  • Virus scanning - ClamAV integration (optional)
  • File type validation - Whitelist allowed MIME types
  • Size limits - Per-user and per-tenant quotas

Real-Time Subscription Flow

1. Client Subscription
   subscription {
     posts(where: { user_id: { _eq: $userId } }) {
       id
       title
       content
     }
   }
   ↓
2. WebSocket Handshake
   ws://api.yourdomain.com/v1/graphql
   Connection: Upgrade
   ↓
3. Hasura Subscription Manager
   β€’ Parse subscription
   β€’ Validate permissions
   β€’ Set session variables
   ↓
4. Initial Data Fetch
   β€’ Execute query once
   β€’ Return current data to client
   ↓
5. Polling Setup (Live Query)
   β€’ Hasura polls database every 1 second (configurable)
   β€’ Compares result hash with previous
   ↓
6. Data Change Detected
   β€’ New post inserted
   β€’ Hash changed
   ↓
7. Push Update to Client
   {
     "type": "data",
     "id": "subscription-id",
     "payload": {
       "data": {
         "posts": [/* updated data */]
       }
     }
   }
   ↓
8. Client Updates UI
   β€’ React/Vue/Angular component re-renders
   β€’ New post appears instantly

Optimization:

  • Multiplexing - Batch identical subscriptions
  • Refetch interval - Configurable (default: 1s)
  • Cursor-based - Only fetch changes since last poll
  • Connection management - Automatic reconnection

Security Architecture

Defense in Depth

Layer 1: Network Security
β”œβ”€β”€ Firewall (ufw/iptables)
β”œβ”€β”€ DDoS Protection (Cloudflare/AWS Shield)
└── Rate Limiting (Nginx)

Layer 2: Transport Security
β”œβ”€β”€ TLS 1.3 (SSL certificates)
β”œβ”€β”€ HSTS (Strict-Transport-Security)
└── Certificate Pinning (mobile apps)

Layer 3: Application Security
β”œβ”€β”€ JWT Authentication
β”œβ”€β”€ CORS Configuration
β”œβ”€β”€ Security Headers (CSP, X-Frame-Options, etc.)
└── Input Validation

Layer 4: API Security
β”œβ”€β”€ GraphQL Query Depth Limiting
β”œβ”€β”€ Query Cost Analysis
β”œβ”€β”€ Rate Limiting per User
└── API Key Management

Layer 5: Database Security
β”œβ”€β”€ Row Level Security (RLS)
β”œβ”€β”€ Role-Based Access Control
β”œβ”€β”€ SQL Injection Prevention (Parameterized Queries)
└── Encrypted Connections (SSL)

Layer 6: Data Security
β”œβ”€β”€ Encryption at Rest
β”œβ”€β”€ Encryption in Transit
β”œβ”€β”€ PII Anonymization
└── Secure Backups

Layer 7: Audit & Monitoring
β”œβ”€β”€ Audit Logging
β”œβ”€β”€ Anomaly Detection
β”œβ”€β”€ Intrusion Detection
└── Security Alerts

RLS (Row Level Security) Enforcement

Multi-Tenant Isolation:

-- Every query is rewritten by PostgreSQL

-- User query:
SELECT * FROM posts WHERE title LIKE '%search%';

-- PostgreSQL rewrites to:
SELECT * FROM posts
WHERE title LIKE '%search%'
  AND tenant_id = current_setting('app.tenant_id')::uuid  -- RLS policy
  AND (
    user_id = current_setting('hasura.user.id')::uuid     -- User posts
    OR current_setting('hasura.user.role') = 'admin'      -- Or admin
  );

Benefits:

  • Zero-trust - Database enforces isolation, not application
  • SQL injection proof - Policies can't be bypassed
  • Centralized - Security rules in one place
  • Performance - Indexes work with RLS

Scalability Architecture

Scaling Strategy by Load

0-10K Users: Single Server

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Single Server (16GB RAM)  β”‚
β”‚                             β”‚
β”‚  β€’ All services in Docker   β”‚
β”‚  β€’ PostgreSQL               β”‚
β”‚  β€’ Redis                    β”‚
β”‚  β€’ Hasura                   β”‚
β”‚  β€’ Auth                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

10K-100K Users: Separated Database

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  App Server     β”‚        β”‚  Database Server β”‚
β”‚                 β”‚        β”‚                  β”‚
β”‚  β€’ Hasura       │───────►│  β€’ PostgreSQL    β”‚
β”‚  β€’ Auth         β”‚        β”‚  β€’ Redis         β”‚
β”‚  β€’ Functions    β”‚        β”‚                  β”‚
β”‚  β€’ Nginx        β”‚        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

100K+ Users: Horizontal Scaling

       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚ Load Balancerβ”‚
       β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β–Ό         β–Ό         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ App 1  β”‚ β”‚ App 2  β”‚ β”‚ App 3  β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”˜
     β”‚          β”‚          β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚  PostgreSQL    β”‚
       β”‚  Primary       β”‚
       β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
       β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
       β–Ό         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚Replica 1β”‚ β”‚Replica 2β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Design Decisions

Why GraphQL (Hasura) over REST?

Decision: Use Hasura GraphQL as primary API layer

Rationale:

  1. Automatic API generation - No manual endpoint coding
  2. Real-time built-in - WebSocket subscriptions
  3. Type safety - Schema-driven development
  4. Performance - Fetch exactly what you need
  5. RLS integration - Direct PostgreSQL security

Trade-offs:

  • βœ… Faster development
  • βœ… Better developer experience
  • βœ… Built-in subscriptions
  • ❌ Learning curve for GraphQL
  • ❌ Caching more complex than REST

Why PostgreSQL over NoSQL?

Decision: PostgreSQL as primary database

Rationale:

  1. ACID compliance - Strong consistency guarantees
  2. Row Level Security - Built-in multi-tenancy
  3. Rich data types - JSON, arrays, full-text search
  4. Mature ecosystem - 30+ years of development
  5. Excellent performance - Scales to millions of rows

Trade-offs:

  • βœ… Data integrity
  • βœ… Complex queries
  • βœ… ACID guarantees
  • ❌ Harder horizontal scaling than NoSQL
  • ❌ Schema migrations required

Why Docker Compose over Kubernetes (for small/medium)?

Decision: Docker Compose for <100K users, Kubernetes for larger

Rationale:

  1. Simplicity - Single YAML file vs many manifests
  2. Local development - Same as production
  3. Resource efficiency - No k8s overhead
  4. Easier debugging - Logs and exec simpler
  5. Cost - No k8s control plane costs

Trade-offs:

  • βœ… Simpler operations
  • βœ… Lower resource usage
  • βœ… Faster iteration
  • ❌ Less auto-scaling
  • ❌ Manual failover

When to switch to Kubernetes:

  • 100K concurrent users

  • Multi-region deployment
  • Advanced auto-scaling needed
  • Service mesh requirements

Technology Stack

Infrastructure

  • Container Orchestration: Docker Compose (development/small), Kubernetes (large scale)
  • Reverse Proxy: Nginx
  • SSL: Let's Encrypt / Commercial certs
  • Load Balancer: HAProxy / Cloud LB

Backend Services

  • GraphQL API: Hasura GraphQL Engine v2.35+
  • Database: PostgreSQL 15+
  • Auth: nHost Auth (fork of Hasura Auth)
  • Cache/Queue: Redis 7+
  • Object Storage: MinIO (S3-compatible)
  • Functions: Node.js 20 / Deno 1.40

Monitoring

  • Metrics: Prometheus
  • Visualization: Grafana
  • Logs: Loki + Promtail
  • Traces: Tempo
  • Alerts: Alertmanager

Security

  • Authentication: JWT (HS256/RS256)
  • Authorization: RLS + Hasura permissions
  • Encryption: TLS 1.3, AES-256
  • Secrets: Encrypted environment variables

Development

  • CLI: Bash 3.2+ (POSIX-compliant)
  • CI/CD: GitHub Actions
  • Testing: Bats (Bash), Jest (JS), pytest (Python)
  • Documentation: Markdown, Mermaid diagrams

Integration Patterns

Service-to-Service Communication

Pattern 1: Database-Mediated

Service A β†’ PostgreSQL ← Service B
(via triggers, LISTEN/NOTIFY)

Pattern 2: Event-Driven

Service A β†’ Redis Pub/Sub β†’ Service B

Pattern 3: Direct HTTP

Service A β†’ HTTP β†’ Service B
(via Hasura Actions or custom endpoints)

Pattern 4: Message Queue

Service A β†’ BullMQ (Redis) β†’ Service B
(for async jobs)

Related Documentation


Maintainers:

  • Architecture Review: Monthly
  • Diagram Updates: On major changes
  • Performance Benchmarks: Quarterly
⚠️ **GitHub.com Fallback** ⚠️