Architecture
System design overview — components, request lifecycle, background workers, and data flow in Vaultaris.
Technology stack
| Layer | Technology |
|---|---|
| Language | Rust (edition 2024) |
| HTTP framework | Axum 0.8 + Tower middleware |
| OpenAPI | aide 0.16 — spec auto-generated from handler signatures |
| Database | PostgreSQL 14+ via SQLx 0.8 (typed queries, async, migrations) |
| Cache / rate limit | Redis 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) |
| Passwords | Argon2id — OWASP-recommended parameters |
| Encryption at rest | AES-256-GCM via vaultaris-crypto crate (EncryptedField<T>) |
| 5 native providers: SMTP, SendGrid, Mailgun, AWS SES (SigV4), Brevo | |
| GeoIP | MaxMind GeoLite2 .mmdb (offline) + ip-api.com fallback |
| Plugin runtime | Native Rust .so / .dylib via libloading + stabby ABI stability |
| Async runtime | Tokio |
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)
| Table | Purpose |
|---|---|
tenants | Top-level isolation unit, branding, settings, license ref |
users | Auth credentials, profile, lockout, freeze state |
roles / permissions | RBAC graph, composite roles, ABAC conditions |
groups / group_members / group_roles | Group hierarchy, membership |
applications | App-scoped resource containers |
oauth_clients | OAuth 2.0 client registrations |
oauth_authorization_codes | Short-lived auth codes (PKCE) |
refresh_tokens | Stored refresh tokens (hashed) |
sessions | Active user sessions (device, IP, user-agent) |
global_sessions | Cross-domain SSO tokens |
device_sessions | Per-device login history |
webauthn_credentials | FIDO2 public keys + sign counters |
mfa_totp_secrets | AES-GCM encrypted TOTP secrets |
audit_logs | Append-only, immutable audit records |
plugins / plugin_instances | Plugin registry and per-tenant activation |
abac_policies | Attribute-based access control rules |
identity_providers | Federated IdP configurations |
licenses | License tier, limits, valid dates |
system_settings | Bootstrap 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.
| Worker | Interval | Purpose |
|---|---|---|
| Expired token cleanup | Every 5 min | Delete expired refresh tokens and authorization codes |
| Failed webhook retry | Every 1 min | Retry outbound webhooks up to configured max attempts |
| Control-plane heartbeat | HEARTBEAT_INTERVAL_SECS (60 s default) | Report status to CP; receive updated license limits; trigger freeze/unfreeze |
| Control-plane telemetry | TELEMETRY_INTERVAL_SECS (900 s default) | Send usage counters to CP (leader node only, fire-and-forget) |
| Demo tenant purge | Periodic | Remove expired demo sandbox tenants |
| Graceful shutdown | Signal (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.