vaultaris /docs

Architecture

System design overview — components, request lifecycle, background workers, and data flow in Vaultaris.

Technology stack

LayerTechnology
LanguageRust (edition 2024)
HTTP frameworkAxum 0.8 + Tower middleware
OpenAPIaide 0.16 — spec auto-generated from handler signatures
DatabasePostgreSQL 14+ via SQLx 0.8 (typed queries, async, migrations)
Cache / rate limitRedis via redis-rs (optional, graceful fallback)
JWT signing (OAuth)Rotating ed25519 / ECDSA P-256 keys from JWK set
JWT signing (internal)HMAC-SHA256 (MFA tokens, setup tokens)
PasswordsArgon2id — OWASP-recommended parameters
Encryption at restAES-256-GCM via vaultaris-crypto crate (EncryptedField<T>)
Email5 native providers: SMTP, SendGrid, Mailgun, AWS SES (SigV4), Brevo
GeoIPMaxMind GeoLite2 .mmdb (offline) + ip-api.com fallback
Plugin runtimeNative Rust .so / .dylib via libloading + stabby ABI stability
Async runtimeTokio

Request lifecycle

Incoming HTTP
  │
  ├─ Rate limit middleware (Redis sliding-window / in-memory fallback)
  ├─ CORS middleware
  ├─ HTTP tracing (request ID, structured spans)
  ├─ Request timeout (30 s hard limit)
  │
  ├─ Router
  │   ├─ Public routes   (health, OAuth, OIDC discovery, hosted login, setup)
  │   ├─ Auth routes     (Bearer token → AuthenticatedUser extractor)
  │   │   └─ Tenant isolation middleware (tenant_id from header or JWT claim)
  │   └─ Service-token routes (billing-shadow admin, constant-time comparison)
  │
  ├─ Handler
  │   ├─ DTO deserialization + validation
  │   ├─ License feature/limit check
  │   ├─ Domain service call
  │   │   ├─ PostgreSQL queries (SQLx, parameterized only)
  │   │   └─ Audit log write
  │   ├─ Email dispatch (async, fire-and-forget)
  │   └─ Plugin hook fire (async)
  │
  └─ Response (JSON)

Database schema (key tables)

TablePurpose
tenantsTop-level isolation unit, branding, settings, license ref
usersAuth credentials, profile, lockout, freeze state
roles / permissionsRBAC graph, composite roles, ABAC conditions
groups / group_members / group_rolesGroup hierarchy, membership
applicationsApp-scoped resource containers
oauth_clientsOAuth 2.0 client registrations
oauth_authorization_codesShort-lived auth codes (PKCE)
refresh_tokensStored refresh tokens (hashed)
sessionsActive user sessions (device, IP, user-agent)
global_sessionsCross-domain SSO tokens
device_sessionsPer-device login history
webauthn_credentialsFIDO2 public keys + sign counters
mfa_totp_secretsAES-GCM encrypted TOTP secrets
audit_logsAppend-only, immutable audit records
plugins / plugin_instancesPlugin registry and per-tenant activation
abac_policiesAttribute-based access control rules
identity_providersFederated IdP configurations
licensesLicense tier, limits, valid dates
system_settingsBootstrap state, CP credentials

19 versioned migrations embedded in the binary, applied automatically on startup.

Background workers

Workers start at boot. Leader election via PostgreSQL advisory locks prevents duplicate execution on multi-node deployments.

WorkerIntervalPurpose
Expired token cleanupEvery 5 minDelete expired refresh tokens and authorization codes
Failed webhook retryEvery 1 minRetry outbound webhooks up to configured max attempts
Control-plane heartbeatHEARTBEAT_INTERVAL_SECS (60 s default)Report status to CP; receive updated license limits; trigger freeze/unfreeze
Control-plane telemetryTELEMETRY_INTERVAL_SECS (900 s default)Send usage counters to CP (leader node only, fire-and-forget)
Demo tenant purgePeriodicRemove expired demo sandbox tenants
Graceful shutdownSignal (SIGTERM/SIGINT)Drain in-flight requests via tokio::sync::watch

Multi-node consistency

Nodes are stateless. All durable state lives in PostgreSQL and Redis. Per-node ephemeral state: node_id (UUID, random at boot), api_latency_ms (rolling average), in-memory GeoIP cache, in-memory rate-limit counters (Redis path is primary).

Leader election: the node holding the PostgreSQL advisory lock runs leader-only background jobs (telemetry, some cleanup). Lock is released on graceful shutdown.

OpenAPI generation

aide + schemars auto-generate the OpenAPI 3.1 spec from handler signatures. Routes registered on ApiRouter appear in the spec; plain Router routes (billing-shadow admin, internal) are excluded. Spec served at /api/v1/docs/openapi.json; Scalar UI at /api/v1/docs.

Plugin runtime

Plugins are native Rust dynamic libraries. The PluginLibraryCache calls dlopen once per library path (process-wide); Arc<Library> is shared across tenants. dlclose happens when the last tenant uninstalls the plugin.

ABI stability is enforced by stabby + the #[plugin_trait] proc-macro, which desugars async trait methods to extern "C" functions returning DynFuture. Plugins compiled against one Vaultaris release can be loaded by subsequent releases without recompilation as long as the abi_version in the manifest matches.

Per-tenant config is stored under plugins.<plugin_id> in the tenant's advanced settings JSON. The active schemas endpoint (GET /api/v1/tenants/{id}/plugins/active-schemas) returns merged field metadata for dashboard UI rendering.