Skip to content

System Architecture

AuthNexus is composed of three core binaries and one admin frontend, designed for multi-tenant authentication and operations at scale.

Process Overview

client/SDK ──mTLS TCP (custom binary protocol)── server_app

                                                      │ /cp/v2/* (mTLS HTTP)

admin frontend ──HTTP──> control_plane_app ───> Control DB (SQLite/PG)
                          (admin + CP unified)         Runtime DB (separate)

control_plane_app

The unified management process. It hosts two sets of HTTP endpoints on a single server:

  • /admin/v1/* -- Admin API for the web dashboard (agent management, PKI, users, cards, applications, cloud functions, security, reports, settlement).
  • /cp/v2/* -- Southbound interface for business nodes (checkin, command pull, config sync, SSE events, OCSP).

The admin HTTP API defaults to loopback-only (127.0.0.1:9090). Production deployments expose it through a reverse proxy. The CP southbound endpoint binds to 0.0.0.0:9091 to accept remote node connections over mTLS. A separate plain HTTP server on port 9092 handles OCSP responses.

On first startup, the CP enters setup-only mode until the four CAs are initialized via the web-based setup wizard. In this state, only /admin/v1/setup/* endpoints are available; /cp/* endpoints are not served.

server_app

The business TCP node. It handles SDK connections over a custom binary protocol on top of TLS 1.3 with mutual TLS authentication. All hot-path operations (login, heartbeat, card key validation, cloud functions, push notifications) flow through this process. HTTP is not used on the business data path.

Each server node connects back to the Control Plane via the four communication channels to receive commands, configuration updates, security deltas, and OCSP responses.

authnexus_sdk

A C++ static library for client integration. It provides a synchronous API and internally manages three threads: a reader thread (packet routing), a notify thread (push callback dispatch), and an optional heartbeat thread. See SDK Integration.

admin_frontend

A Vue 3 + TypeScript + Naive UI single-page application. It communicates exclusively through the /admin/v1/* REST API. Supports a demo mode via MSW (Mock Service Worker) that intercepts all API calls in-browser, allowing full feature demonstration without any backend process. Demo mode is activated by the VITE_DEMO_MODE=true environment variable and does not pollute the production build -- MSW, mocks, and seed data are dynamically imported only when enabled.

Database Architecture

AuthNexus uses two separate databases, even when both are SQLite:

DatabaseOwnerPurpose
Control DBcontrol_plane_appAgent accounts, applications, card types, card keys, users, PKI state, configurations, audit logs, settlement
Runtime DBserver_appSession state, blacklist cache, auth epoch, runtime security data

Both SQLite and PostgreSQL backends are supported. Schema files live in schema/:

  • sqlite_control_plane_schema.sql / postgres_control_plane_schema.sql
  • sqlite_server_schema.sql / postgres_server_schema.sql

The database abstraction layer (src/core/db/) provides a unified interface through database_interface.h, connection_pool.h, batch_executor.h, and transaction_guard_interface.h. Application code never interacts with SQLite or PostgreSQL APIs directly -- the abstraction layer handles connection pooling, transaction management, and SQL dialect differences.

Schema Management

Schema files are copied next to the compiled binaries via a CMake POST_BUILD step. Both processes auto-apply their schema on startup. The SQLite and PostgreSQL schemas are maintained in parallel and kept in sync.

Thread Domains (server_app)

The server process strictly partitions work into isolated thread domains. No domain shares threads with another.

DomainPurpose
io_threadsNetwork I/O, connection coroutine scheduling, per-session strand
logic_threadsHandler business logic (decode, JSON, CPU work). Physically isolated from I/O via LogicDispatcher::dispatch()
db_threadsBlocking database operations (runtime.db().run(fn))
crypto_threadsPassword hashing, certificate-intensive CPU work (runtime.crypto().run(fn))
cloud_function_threadsLua sandbox execution, backpressure via cloud_function_max_in_flight
cp_io_threadsCP HTTP client burst pool (checkin, config pull, OCSP fetch). Does not host SSE long connections
sse_pool (fixed: 1)Dedicated to the Channel 2 SSE long connection. Physically isolated from cp_io
BackgroundRuntimeTaskScheduler, ControlPlaneAgent, DeltaPuller, and other background services. Each IBackgroundService gets an independent asio::strand

Automatic Thread Scaling

The --auto flag (default) selects thread counts based on detected CPU cores:

CoresIOLogicDBCryptoCP_IOCloudFuncBG
1--21111112
3--41221122
5--82432132
9+n/8n/2n/4n/82n/43

Values are clamped to sensible minimums and maximums. Manual overrides are available via CLI flags (-i, --logic-threads, -d, -c, --cp-io, --bg-threads).

Coroutine Executor Model

AuthNexus uses Asio C++20 coroutines with a strict two-rule executor model:

  1. Where a coroutine runs is decided at spawn time: asio::co_spawn(target_executor, body, token).
  2. Mid-coroutine offloading uses async::run_on: co_await async::run_on(target, fn) runs fn on target and resumes the caller on its original executor.

There is no "switch executor" primitive. Historical helpers like async::switch_to were removed because co_await asio::post(target, use_awaitable) is a no-op on strand-spawned coroutines. A source-code audit test (SourceCodeAudit.NoRawCoAwaitPostUseAwaitableInSourceTree) scans the entire src/ tree and fails the build if any raw co_await asio::{post|dispatch|defer}(.., use_awaitable) pattern is found.

The practical implication: handler code dispatches work to other pools via typed wrappers like runtime.db().run(fn), runtime.crypto().run(fn), and runtime.cloud_function().run(fn), all of which use async::run_on internally. Direct co_await asio::post is never used in business code.

Configuration Types

The Control Plane manages six configuration types, tracked in the config_registry:

Config TypeScopeDescription
server_runtime_settingsPer-node onlyServer runtime parameters (timeouts, limits)
app_policyPer-app, globalApplication-level policies
app_variablesPer-app, globalCloud variables readable by SDK
app_client_ca_bundlePer-app, globalClient CA trust bundle for mTLS
app_mtls_trust_bundlePer-app, globalmTLS trust bundle published to nodes
cp_agent_runtimePer-node onlyCP agent runtime parameters (timeouts, retry, degraded threshold)

Per-node-only types (server_runtime_settings, cp_agent_runtime) have no global layer. Nodes bootstrap from node_agent.json and receive per-node overrides via config pull.

Staged Startup (StageRunner)

Both server_app and control_plane_app use a StageRunner for phased initialization. Each stage is an independent function with its own logging and error propagation. On failure, stages unwind in reverse order for safe cleanup.

Server startup stages (abbreviated):

  1. Logging initialization
  2. Token generation
  3. Database connection and schema
  4. Token managers
  5. Security subsystem
  6. Caches
  7. Logic dispatcher
  8. Push notification system
  9. Cloud function runtime
  10. TCP server binding
  11. Scheduler tasks
  12. Config manager injection
  13. Runtime settings and app policies
  14. CP agent connection
  15. Runtime security sync

The business TLS port is not opened until the CP Agent has successfully applied critical configurations (fail-closed).

Key Source Directories

DirectoryRole
src/core/Shared library -- DB abstraction, protocol codec, TLS/PKI, token management, caching, password hashing, rate limiting, card key logic, Lua cloud functions, runtime, ZIP, JSON utilities
src/server/Server process -- executor, session management, protocol handlers, network layer, security enforcement, CP client, configuration, performance
src/control_plane/CP process -- admin HTTP routes, CP southbound routes, business services, caching
src/sdk/C++ client SDK -- synchronous API, internal reader/notify/heartbeat threads
src/admin_frontend/Vue 3 admin dashboard with MSW demo mode
src/website/VitePress marketing and documentation site
tests/GoogleTest source files (*_tests.cpp), auto-discovered by CMake GLOB
schema/SQLite and PostgreSQL schema files for both databases

Multi-Tenant Model

AuthNexus is inherently multi-tenant at the application level. Each application (app_id) has isolated:

  • User accounts and sessions
  • Card keys and card types
  • Cloud functions and cloud variables
  • Client certificates and mTLS trust bundles
  • Policies and configurations

Platform administrators (kind = 'platform_admin') manage all applications. Application agents (kind = 'app_agent') are scoped to their granted applications via the agent_app_grants table. This M:N relationship allows flexible agent-to-application assignment without coupling agent identity to a single app.

Next Steps