{ "alternatives_considered": [ { "option": "PostgreSQL", "why_rejected": "Requires an external server process — no embedded mode for solo deployment. Schema evolution requires explicit migration tooling. No native document storage for Cedar policies." }, { "option": "SQLite", "why_rejected": "No native namespace/tenant isolation. No document model. Concurrent write performance under multiple async tasks is constrained. No WebSocket server mode for multi-user." }, { "option": "Redis", "why_rejected": "No real persistence guarantees (AOF is not the same as durable embedded storage). Key-value only — no document or relational queries for audit trail or task history." } ], "consequences": { "negative": [ "SurrealDB v2 API uses snake_case builtins; bind() requires owned values; ID fields in structs needed to avoid RecordId parsing issues", "MVCC conflicts under concurrent write load require retry_on_conflict with exponential backoff + jitter on store_secret, store_key, and lease operations", "Full-text search and graph queries are available but deferred to avoid over-engineering" ], "positive": [ "Single storage abstraction: SurrealPool is Clone, shareable across tokio tasks", "No external process in solo mode: embedded RocksDB starts with the service binary", "AI/MCP context: AI service queries audit:events and orchestrator:tasks directly without ETL pipelines", "Test isolation: DbConfig::Memory (in-process Surreal) requires no external binary" ] }, "constraints": [ { "check": { "must_be_empty": true, "paths": [ "core/nulib/", "extensions/" ], "pattern": "surrealdb|surreal|SurrealDB", "tag": "Grep" }, "claim": "CLI code (.nu files) must NOT access SurrealDB directly — all reads/writes from CLI go through service HTTP APIs", "id": "cli-no-surrealdb-direct", "rationale": "Direct SurrealDB access from CLI violates the SOLID boundary (ADR-014). All state mutations must go through the service layer to maintain audit trail and authorization.", "scope": "core/nulib/, extensions/", "severity": "Hard" }, { "check": { "path": "platform/crates/platform-db/", "present": true, "tag": "FileExists" }, "claim": "The five namespaces (orchestrator, vault, control_center, audit, workspace) under the `provisioning` database must not be changed without an ADR", "id": "namespace-layout-fixed", "rationale": "Namespace layout is the boundary contract between services. Changing it without an ADR risks data loss and cross-service coupling.", "scope": "platform/crates/platform-db/", "severity": "Hard" }, { "check": { "cmd": "rg 'store_secret|store_key|create_lease' platform/ --include='*.rs' | grep -v retry", "expect_exit": 1, "tag": "NuCmd" }, "claim": "Operations on store_secret, store_key, and lease operations must use retry_on_conflict with exponential backoff + jitter", "id": "retry-on-mvcc-conflict", "rationale": "SurrealDB MVCC conflicts are expected under concurrent write load. Without retry, concurrent task executions silently fail on lease creation.", "scope": "platform/crates/platform-db/src/retry.rs, platform/secretumvault/", "severity": "Soft" } ], "context": "The platform needs a single persistent data store that can operate embedded (RocksDB, zero external process) in solo mode, run as an external WebSocket server in multi-user deployments without schema changes, support five distinct service namespaces with well-typed schemas (orchestrator, vault, control_center, audit, workspace), store heterogeneous data (task logs append-only, secrets as encrypted blobs, Cedar policies as documents, audit events as time-series, git sync state as mutable), and be queryable by the AI/MCP service for context gathering without a separate analytics database. PostgreSQL requires a server process, SQLite has no native namespacing, Redis has no real persistence. SurrealDB is the only option supporting all five requirements simultaneously.", "date": "2026-02-17", "decision": "SurrealDB is the exclusive persistent store for all platform state. No service reads raw files or environment variables for credentials at runtime — all reads go through SurrealDB (secrets via Vault Service, which stores ciphertext in SurrealDB). Namespace layout under `provisioning` database: orchestrator (tasks, task_events, execution_logs, config_hashes, provider_cache), vault (secrets, keys, leases, secret_versions, audit_trail), control_center (users, sessions, cedar_policies, policy_evaluations), audit (events, metrics), workspace (registrations, deployments, git_sync_state, extensions). Mode selection via DbConfig: Memory (tests), Embedded (solo, RocksDB), Server (multi-user, WebSocket). Schema initialization via DEFINE TABLE IF NOT EXISTS DDL — no migration framework for additive changes.", "id": "adr-013", "ontology_check": { "decision_string": "SurrealDB is the exclusive persistent store; CLI accesses state only via service HTTP APIs; five fixed namespaces under provisioning database", "invariants_at_risk": [ "solid-boundaries", "config-driven-always" ], "verdict": "Safe" }, "rationale": [ { "claim": "Single storage abstraction across solo and multi-user modes", "detail": "SurrealPool is Clone (Arc> internally), shareable across tokio tasks. The same codebase connects to embedded RocksDB in solo mode and WebSocket server in multi-user — only the DbConfig changes." }, { "claim": "No external process in solo mode", "detail": "Embedded RocksDB starts with the service binary. Solo mode requires no external database process, reducing startup dependencies and enabling CI runs without infrastructure." }, { "claim": "AI/MCP context without ETL pipelines", "detail": "The AI service queries audit:events and orchestrator:tasks directly for context. SurrealDB's document+relational model handles heterogeneous schemas without separate analytics infrastructure." }, { "claim": "Test isolation via DbConfig::Memory", "detail": "In-process Surreal requires no external binary — every cargo test run gets a fresh, isolated database. Integration tests run without external infrastructure." } ], "related_adrs": [ "adr-012-nats-event-broker", "adr-014-solid-enforcement", "adr-015-solo-mode-architecture" ], "status": "Accepted", "title": "SurrealDB as the Global Persistent Store" }