Skip to main content

Architecture Overview

This document provides a high-level overview of the Heimdall platform architecture.

System Architecture​

┌──────────────────────────────────────────────────────────────────────────────────┐
│ Internet │
├──────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Client │ │ Backend │ │ ID │ │ Policies │ │ Docs │ │
│ │ Apps │ │ (Next.js)│ │ (Next.js)│ │ (Next.js)│ │(Docusaur)│ │
│ │ (REST/WS)│ │ :3001 │ │ :3002 │ │ :3004 │ │ :3003 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ │ HTTPS │ HTTPS │ HTTPS │
│ │ WebSocket │ GraphQL │ OAuth │
│ │ │ REST │ │
│ │ │ │ │
│ ┌────┴─────────────┴─────────────┴──────────────────────────────────────┐ │
│ │ Load Balancer │ │
│ │ (nginx / cloud LB) │ │
│ └───────────────────────────────┬────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────┐ ┌─────────────┐│ ┌─────────────┐ │
│ │ Discord Bot │ │ Twitch Bot ││ │ YouTube Bot │ │
│ │ (Rust) │ │ (Rust) ││ │ (Rust) │ ◄── Bots (Rust) │
│ │ :3006 │ │ :3007 ││ │ :3008 │ GraphQL, REST, WS │
│ └──────┬──────┘ └──────┬──────┘│ └──────┬──────┘ │
│ │ │ │ │ │
│ └────────────────┼───────┼─────────┘ │
│ │ │ │
│ ┌───────────────────────┴───────┴───────────────────────────────────────┐ │
│ │ Rust API Binary (platform/api) │ │
│ │ (Actix-web Server) │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────────┐ │ │
│ │ │ 19 Modular Crates (crates/) │ │ │
│ │ │ │ │ │
│ │ │ heimdall-rest heimdall-graphql heimdall-websocket │ │ │
│ │ │ heimdall-auth heimdall-db heimdall-cache │ │ │
│ │ │ heimdall-audit heimdall-audit-logger heimdall-email │ │ │
│ │ │ heimdall-config heimdall-common heimdall-api │ │ │
│ │ │ heimdall-geoip heimdall-integrations heimdall-proto │ │ │
│ │ │ heimdall-storage heimdall-scheduler heimdall-telemetry │ │ │
│ │ heimdall-pegelonline │ │ │
│ │ └────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └───────────────────────┬────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────┴─────────────────────────────────────────┐ │
│ │ Data Layer │ │
│ │ │ │
│ │ ┌─────────────────────┐ ┌─────────────────────┐ ┌──────────┐│ │
│ │ │ PostgreSQL │ │ TimescaleDB │ │ Redis ││ │
│ │ │ (Primary Store) │ │ (Time-Series DB) │ │ (Cache) ││ │
│ │ │ │ │ │ │ ││ │
│ │ │ - Users │ │ - AuditEvent │ │ - Session││ │
│ │ │ - Sessions │ │ (hypertable) │ │ - Perms ││ │
│ │ │ - API Keys │ │ - GpsData │ │ - Rate ││ │
│ │ │ - Roles/Permissions │ │ (hypertable) │ │ - Pub/Sub││ │
│ │ │ - OAuth Clients │ │ - Automatic data │ │ ││ │
│ │ │ - 2FA Secrets │ │ retention │ │ ││ │
│ │ │ - Data Exports │ │ - GeoIP enrichment │ │ ││ │
│ │ └─────────────────────┘ └─────────────────────┘ └──────────┘│ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────────────┘

Modular Crate Architecture​

The backend was split from a monolithic platform/api/ into 19 independent crates under crates/. The platform/api/ binary is now pure glue code (just main.rs, lib.rs, and bin/) that wires the crates together via Actix-web.

Crate Dependency Layers​

┌──────────────────┐
│ platform/api │ (binary — pure glue)
│ main.rs, lib.rs │
└────────┬─────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌─────────┴────────┐ ┌─────┴─────┐ ┌─────────┴────────┐
│ heimdall-rest │ │ heimdall- │ │ heimdall- │
│ (REST handlers, │ │ graphql │ │ websocket │
│ routes, OAuth, │ │ (types, │ │ (WS server, │
│ middleware) │ │ resolvers,│ │ sessions, │
│ │ │ schema) │ │ message types) │
└────────┬─────────┘ └─────┬─────┘ └────────┬─────────┘
│ │ │
└──────────────────┼───────────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
┌─────────┴─────┐ ┌───────┴──────┐ ┌───────┴────────┐
│ heimdall-auth │ │ heimdall- │ │ heimdall- │
│ (JWT, RBAC, │ │ audit-logger │ │ integrations │
│ TOTP, crypto)│ │ (AuditLogger │ │ (Twitch/YT/ │
│ │ │ + GeoIP) │ │ Kick/Trovo) │
└───────┬───────┘ └──────┬───────┘ └───────┬────────┘
│ │ │
└─────────────────┼───────────────────┘
│
┌────────────────────────┼───────────────────────────┐
│ │ │ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌───┴──────┐ ┌───────┴───────┐
│heimdall-│ │heimdall-│ │heimdall-│ │heimdall- │ │ heimdall- │
│ db │ │ cache │ │ email │ │ geoip │ │ storage │
│(models, │ │(Redis │ │(SMTP + │ │(MaxMind │ │(S3 client, │
│ queries)│ │ client) │ │ sailfish│ │ GeoLite2)│ │ file upload) │
└────┬────┘ └────┬────┘ │ tmpl) │ └──────────┘ └───────────────┘
│ │ └────────┘
│ │
┌────┴───────────┴──────────────────────────────────────┐
│ Foundation layer │
│ ┌────────────┐ ┌────────────┐ ┌────────────────────┐ │
│ │ heimdall- │ │ heimdall- │ │ heimdall- │ │
│ │ config │ │ common │ │ telemetry │ │
│ │ (4-tier │ │ (errors, │ │ (tracing, │ │
│ │ config) │ │ pagination,│ │ Sentry) │ │
│ │ │ │ responses) │ │ │ │
│ └────────────┘ └────────────┘ └────────────────────┘ │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────────────┐ │
│ │ heimdall- │ │ heimdall- │ │ heimdall- │ │
│ │ api │ │ audit │ │ scheduler │ │
│ │ (CORS, │ │ (event │ │ (background │ │
│ │ health, │ │ types & │ │ jobs, cron) │ │
│ │ helpers) │ │ constants) │ │ │ │
│ └────────────┘ └────────────┘ └────────────────────┘ │
│ │
│ ┌────────────┐ │
│ │ heimdall- │ │
│ │ proto │ │
│ │ (protobuf │ │
│ │ WS types) │ │
│ └────────────┘ │
└────────────────────────────────────────────────────────┘

Crate Summary​

CrateLocationPurpose
heimdall-configcrates/heimdall-config/4-tier config loading: default.toml -> {env}.toml -> local.toml -> HEIMDALL__* env vars
heimdall-commoncrates/heimdall-common/Shared error types, pagination, response wrappers
heimdall-dbcrates/heimdall-db/Database pool management, domain models, SQL queries
heimdall-cachecrates/heimdall-cache/Redis client wrapper (sessions, permissions, rate limiting)
heimdall-telemetrycrates/heimdall-telemetry/Tracing subscriber and Sentry initialization
heimdall-apicrates/heimdall-api/Actix-web infrastructure: CORS, health, response helpers
heimdall-auditcrates/heimdall-audit/Shared audit event types and constants
heimdall-audit-loggercrates/heimdall-audit-logger/AuditLogger service (writes to TimescaleDB + GeoIP enrichment)
heimdall-authcrates/heimdall-auth/OAuth 2.0 / OpenID Connect, RBAC, TOTP/2FA, crypto utilities
heimdall-emailcrates/heimdall-email/SMTP email service with Sailfish templates + rust-i18n
heimdall-geoipcrates/heimdall-geoip/MaxMind GeoLite2 IP-to-location lookup
heimdall-integrationscrates/heimdall-integrations/Platform OAuth: Twitch, YouTube, Kick, Trovo token management
heimdall-restcrates/heimdall-rest/REST handlers, routes, OAuth endpoints, middleware
heimdall-graphqlcrates/heimdall-graphql/GraphQL types, resolvers, and schema (async-graphql)
heimdall-websocketcrates/heimdall-websocket/WebSocket server, session management, channel pub/sub
heimdall-schedulercrates/heimdall-scheduler/Background jobs and scheduled tasks
heimdall-protocrates/heimdall-proto/Protobuf definitions for WebSocket communication
heimdall-storagecrates/heimdall-storage/S3-compatible storage client and file upload
heimdall-pegelonlinecrates/heimdall-pegelonline/Client for the German PegelOnline API (water level gauging stations)

How the API Binary Wires Everything​

The platform/api/ binary contains only main.rs, lib.rs, and bin/ scripts. It:

  1. Loads configuration via heimdall-config
  2. Initializes telemetry via heimdall-telemetry
  3. Creates database pools via heimdall-db
  4. Creates Redis client via heimdall-cache
  5. Injects services as web::Data<T> into Actix-web (no global AppState struct)
  6. Registers routes from heimdall-rest::routes and heimdall-graphql
  7. Starts the WebSocket server from heimdall-websocket
  8. Starts background jobs from heimdall-scheduler

Services are injected individually via web::Data<T> (e.g., web::Data<PgPool>, web::Data<RedisClient>, web::Data<GeoIpReader>), or via ServiceContext for handlers that need multiple services.

Build System​

All crates are built via Bazel with BuildBuddy remote cache for fast CI builds. Standard cargo build also works for local development.

Component Overview​

Rust API (platform/api)​

The core backend service, handling all API requests. This is a thin binary that wires together the 19 modular crates.

Technology Stack:

  • Framework: Actix-web 4
  • Database: SQLx with PostgreSQL (primary) + TimescaleDB (time-series)
  • GraphQL: async-graphql
  • OpenAPI: utoipa with Swagger UI
  • Caching: Redis (via heimdall-cache)
  • GeoIP: MaxMind GeoLite2 (via heimdall-geoip)

Responsibilities:

  • REST API endpoints (via heimdall-rest)
  • GraphQL queries and mutations (via heimdall-graphql)
  • WebSocket connections for real-time updates (via heimdall-websocket)
  • Authentication and authorization (via heimdall-auth)
  • Rate limiting (via heimdall-rest::middleware)
  • Data persistence (via heimdall-db)

Backend (platform/backend)​

Administrative interface for platform management.

Technology Stack:

  • Framework: Next.js 16 (App Router)
  • Language: TypeScript
  • Styling: TailwindCSS 4
  • Auth: NextAuth.js 5
  • GraphQL: Apollo Client

Responsibilities:

  • Admin dashboard
  • GPS data visualization
  • User management
  • Role management
  • API key management
  • Developer tools (OAuth apps)

ID (platform/id)​

Identity and authentication service with GDPR compliance.

Technology Stack:

  • Framework: Next.js 16 (App Router)
  • Language: TypeScript
  • Styling: TailwindCSS 4
  • Auth: NextAuth.js 5
  • i18n: next-intl

Responsibilities:

  • User registration and login
  • Two-factor authentication (TOTP)
  • OAuth 2.0 provider functionality
  • Account management
  • Account linking (multiple providers)
  • Connected apps management
  • GDPR data export
  • Account deletion with grace period
  • Session management

Policies (platform/policies)​

Legal, privacy, and compliance content.

Technology Stack:

  • Framework: Next.js 16 (App Router)
  • Language: TypeScript
  • Styling: TailwindCSS 4
  • i18n: next-intl

Responsibilities:

  • Privacy policy
  • Terms of service
  • Cookie policy
  • Security information
  • FAQ

Documentation (platform/docs)​

This documentation site.

Technology Stack:

  • Framework: Docusaurus 3
  • Language: TypeScript/MDX

Discord Bot (platform/discord_bot)​

Discord integration bot for the Heimdall platform.

Technology Stack:

  • Framework: Poise (on Serenity-rs)
  • Language: Rust 2024 edition
  • API Communication: GraphQL, REST, WebSocket (Protobuf)
  • i18n: rust-i18n

Responsibilities:

  • Slash commands and prefix commands
  • RBAC permission checking via Heimdall API
  • Audit event logging to Heimdall
  • Real-time event notifications via WebSocket
  • Moderation commands (warn, kick, ban, mute)
  • Admin settings management

Twitch Bot (platform/twitch_bot)​

Twitch integration bot for the Heimdall platform.

Technology Stack:

  • Framework: twitch-irc (Rust)
  • Language: Rust 2024 edition
  • API Communication: GraphQL, REST, WebSocket (Protobuf)

Responsibilities:

  • Chat commands
  • Stream event handling
  • Audit event logging to Heimdall
  • Real-time notifications

Data Flow​

REST API Request Flow​

Client Request
│
▼
┌─────────────┐
│ CORS │ (heimdall-api)
│ Middleware │
└──────┬──────┘
│
▼
┌─────────────┐
│ Logger │ (heimdall-telemetry)
│ Middleware │
└──────┬──────┘
│
▼
┌─────────────┐
│ Auth │ (heimdall-rest::middleware)
│ Middleware │
└──────┬──────┘
│
▼
┌─────────────┐
│ Rate Limit │ (heimdall-rest::middleware)
│ Middleware │
└──────┬──────┘
│
▼
┌─────────────┐
│ Handler │ (heimdall-rest::handlers)
│ Function │
└──────┬──────┘
│
▼
┌─────────────┐
│ Database │ (heimdall-db)
│ Query │
└──────┬──────┘
│
▼
Response

WebSocket Connection Flow​

Client Connect
│
▼
┌─────────────┐
│ WebSocket │ (heimdall-websocket::route)
│ Upgrade │
└──────┬──────┘
│
▼
┌─────────────┐
│ State │
│ Manager │──────┐
└──────┬──────┘ │
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Connection │ │ Channel │
│ Handler │ │ Manager │
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Subscribe/ │ │ Publish │
│ Unsubscribe │ │ Updates │
└─────────────┘ └─────────────┘

Database Architecture​

Heimdall uses a dual-database architecture:

  1. PostgreSQL (Primary) - Core application data (users, sessions, API keys, etc.)
  2. TimescaleDB (Time-Series) - Audit events stored in a hypertable with automatic time-based partitioning

This separation provides:

  • Optimized time-series queries for audit logs with automatic compression
  • Independent scaling for OLTP vs analytics workloads
  • GeoIP enrichment for security monitoring (country, city, region)
  • Automatic data retention policies for compliance

Core Tables (PostgreSQL)​

Note: Simplified sketch — only key columns shown. The authoritative schema lives in crates/heimdall-db/migrations/. Credentials are stored in PlatformAccount (not User), and GPS data lives in TimescaleDB (see below).

-- Users (clean identity; credentials in PlatformAccount)
User
├── id (UUID)
├── username (unique)
├── primary_platform_account_id → PlatformAccount
├── last_login_platform_account_id → PlatformAccount
├── privacy_mode
├── preferred_locale
├── created_at
├── updated_at
└── deleted_at (soft delete)

-- Platform Accounts (OAuth + email/password credentials)
PlatformAccount
├── id (UUID)
├── platform_id → Platform
├── platform_user_id -- OAuth external ID / email address
├── username
├── email
├── password_hash -- Argon2, email platform only
├── email_verified_at
├── user_id → User
└── created_at

-- Sessions (OAuth login flow)
Session
├── id (UUID)
├── user_id → User
├── session_token
├── expires
├── ip_address
├── user_agent
├── last_activity_at
├── provider
└── created_at

-- API Keys (SHA-256 hashed, only prefix stored)
ApiKey
├── id (UUID)
├── key_hash -- SHA-256 of the key
├── key_prefix -- first 11 chars (e.g. "hm_abc12345")
├── name
├── description
├── user_id → User (optional)
├── scopes[]
├── is_active
├── is_system -- system keys (device/service auth)
├── expires_at
├── last_used_at
└── created_at

-- Roles
Role
├── id (TEXT)
├── name (unique)
├── description
├── is_system
├── requires_two_factor
└── created_at

-- Permissions
Permission
├── id (TEXT)
├── resource
├── action
├── description
├── admin_only
├── display_order
└── created_at

-- OAuth Clients
OAuthClient
├── id (UUID) -- client_id (public)
├── secret_hash (hashed) -- NULL for public clients
├── name
├── redirect_uris[]
├── allowed_scopes[]
├── owner_user_id → User
└── created_at

Audit Events (TimescaleDB)​

Audit events are stored in a TimescaleDB hypertable with automatic time-based partitioning and GeoIP enrichment:

-- Audit Events (TimescaleDB Hypertable)
AuditEvent
├── id (UUID)
├── user_id (optional, can be Heimdall UUID or external ID like Discord snowflake)
├── event_type (string)
├── resource_type (optional)
├── resource_id (optional)
├── actor_id (optional, for admin actions)
├── ip_address (optional)
├── user_agent (optional)
├── description (optional)
├── metadata (JSONB)
├── status (success/failure)
├── error_message (optional)
├── country_code (ISO 3166-1 alpha-2, auto-enriched from IP)
├── country_name (auto-enriched from IP)
├── city (auto-enriched from IP)
├── region (auto-enriched from IP)
├── source_service (optional)
├── latitude / longitude / location (PostGIS point, auto-enriched from IP)
├── is_reported (boolean — computed at query time, not a stored column)
└── created_at (partitioning key)

GPS data is also stored in a TimescaleDB hypertable (per-device, optionally linked to a trip):

-- GPS Data (TimescaleDB Hypertable)
GpsData
├── id (UUID)
├── device_id (per-device tracking)
├── trip_id (optional, links point to a trip)
├── latitude
├── longitude
├── altitude (optional)
├── heading
├── speed_kmh / speed_mps / speed_mph / speed_knots
├── pdop / hdop / vdop (GPS precision)
├── timestamp
└── created_at (partitioning key)

Key Features:

  • Hypertable: Automatic time-based partitioning (1-day chunks)
  • GeoIP Enrichment: Country, city, region automatically added from IP address
  • Compression: Automatic compression after 7 days
  • Retention: Configurable data retention policies

Audit Event Types​

Event TypeDescription
Authentication
loginUser signed in
login_failedSign-in attempt failed
logoutUser signed out
password_changedPassword was changed
password_reset_requestedPassword reset requested
Two-Factor Auth
2fa_enabled2FA was enabled
2fa_disabled2FA was disabled
2fa_verified2FA code verified
2fa_backup_codes_regeneratedBackup codes regenerated
Sessions
session_createdNew session created
session_revokedSession was revoked
all_sessions_revokedAll other sessions revoked
Account
user_createdAccount created
user_updatedProfile updated
user_deletedAccount deleted
deletion_scheduledAccount deletion scheduled
deletion_cancelledScheduled deletion cancelled
Account Links
account_linkedPlatform account linked
account_unlinkedPlatform account unlinked
account_reconnectedPlatform account reconnected
primary_account_changedPrimary account changed
OAuth
consent_grantedOAuth consent given
consent_revokedOAuth consent revoked
token_createdOAuth token created
token_revokedOAuth token revoked
client_createdOAuth client created
client_updatedOAuth client updated
client_deletedOAuth client deleted
client_secret_regeneratedOAuth client secret regenerated
API Keys
api_key_createdAPI key was created
api_key_updatedAPI key was updated
api_key_revokedAPI key was revoked
Admin
user_bannedUser was banned
user_unbannedUser was unbanned
role_assignedRole assigned to user
role_removedRole removed from user
permission_changedPermission changed
Data
data_exportedGDPR data export completed
activity_reportedSuspicious activity reported
Bot Events
bot_command_executedBot command executed
bot_config_changedBot configuration changed
bot_moderation_actionBot moderation action
bot_guild_configuredDiscord guild configured
Integrations
integration_connectedPlatform integration connected
integration_reconnectedIntegration re-authorized
integration_disconnectedIntegration disconnected
integration_token_refreshedToken automatically refreshed
integration_status_updatedIntegration status updated

Source Services​

The source_service field tracks which application created the audit event:

Source ServiceDescription
apiDirect API access
idHeimdall ID webapp
backendBackend dashboard
policiesPolicies webapp
discord_botDiscord bot
twitch_botTwitch bot

Junction Tables​

UserRole (user_id, role_id)
ApiKeyRole (api_key_id, role_id)
RolePermission (role_id, permission_id)
PlatformAccount (user_id, platform_id, platform_user_id) -- linked OAuth/email accounts

Deployment Architecture​

Development​

┌───────────────────────────────────────────────────────────────────────────┐
│ Local Development │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ API │ │Backend │ │ ID │ │ Docs │ │Policies│ │
│ │ :3000 │ │ :3001 │ │ :3002 │ │ :3003 │ │ :3004 │ │
│ └───┬────┘ └───┬────┘ └───┬────┘ └────────┘ └────────┘ │
│ │ │ │ │
│ └───────────┼───────────┘ │
│ │ │
│ ┌───────────────┼───────────────────────────────────────┐ │
│ │ │ │ │
│ │ ┌────────────┴────────────┐ ┌────────────────────┐ │ │
│ │ │ Discord Bot │ │ Twitch Bot │ │ │
│ │ │ :3006 │ │ :3007 │ │ Rust Bots │
│ │ └────────────┬────────────┘ └─────────┬──────────┘ │ │
│ │ └──────────────┬──────────┘ │ │
│ └──────────────────────────────┼────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────┴────────────────────────────────┐ │
│ │ Docker Compose │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────┐ │ │
│ │ │ PostgreSQL │ │TimescaleDB │ │ Redis │ │ MailDev │ │ │
│ │ │ :5432 │ │ :5433 │ │ :6379 │ │ :1080 │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ └─────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────┘

Production​

┌───────────────────────────────────────────────────────────────────┐
│ Production Deployment │
├───────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ CDN / Edge Network │ │
│ │ (Cloudflare / Vercel Edge) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────┴────────────────────────────────┐ │
│ │ Load Balancer │ │
│ │ (nginx / Cloud LB) │ │
│ └───────────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────┼────────────────────────────────┐ │
│ │ ┌──────────┐ ┌───────┴────┐ ┌──────────┐ │ │
│ │ │ API Pod │ │ API Pod │ │ API Pod │ │ │
│ │ │ #1 │ │ #2 │ │ #3 │ │ │
│ │ └──────────┘ └────────────┘ └──────────┘ │ │
│ │ (Kubernetes) │ │
│ └───────────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────┼────────────────────────────────┐ │
│ │ ┌─────────────────┐ │ ┌─────────────────┐ │ │
│ │ │ PostgreSQL │ │ │ Redis │ │ │
│ │ │ (Primary) │◄─────┴─────►│ (Cluster) │ │ │
│ │ └────────┬────────┘ └─────────────────┘ │ │
│ │ │ │ │
│ │ ┌────────┴────────┐ │ │
│ │ │ PostgreSQL │ │ │
│ │ │ (Replica) │ │ │
│ │ └─────────────────┘ │ │
│ │ (Managed Database) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Next.js Apps │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Backend │ │ ID │ │ Policies │ │ Docs │ │ │
│ │ │ (Vercel) │ │ (Vercel) │ │ (Vercel) │ │ (Vercel) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────┘

Security Architecture​

Authentication Layers​

  1. Session-based (Next.js apps via NextAuth)

    • HTTP-only secure cookies
    • JWT tokens with short expiry
    • Automatic refresh
  2. Token-based (API access)

    • Bearer tokens in Authorization header
    • API keys for programmatic access
    • OAuth 2.0 for third-party apps
  3. Two-Factor Authentication

    • TOTP-based (RFC 6238)
    • Backup codes for recovery
    • Role-based 2FA requirements

Authorization Model​

Request
│
▼
┌─────────────────────┐
│ Extract Auth Token │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Lookup Token │
│ (JWT or API Key) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Get User/Key │
│ Roles │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Aggregate │
│ Permissions │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Check Permission │
│ (resource:action) │
└──────────┬──────────┘
│
┌─────┴─────┐
│ │
▼ ▼
Allow Deny

Configuration System​

Heimdall uses a 4-tier configuration system (via heimdall-config):

  1. config/default.toml -- Base defaults for all environments
  2. config/{env}.toml -- Environment-specific overrides (e.g., production.toml)
  3. config/local.toml -- Local developer overrides (git-ignored)
  4. HEIMDALL__* env vars -- Runtime overrides (highest priority, double underscore for nesting)

Example: HEIMDALL__DATABASE__URL=postgres://... overrides [database] url in TOML.

Next Steps​