provisioning-code/.ncl-cache/809748e7e731e5921411e88aef697dd42ab44776fddccbbb69e181980f762637.json

102 lines
6.7 KiB
JSON

{
"alternatives_considered": [
{
"option": "Simplified mono-binary for solo, full services for multi-user",
"why_rejected": "Creates two code paths. Testing in solo mode does not validate multi-user behavior. Scripts written for solo mode require adaptation for multi-user. Doubles the maintenance surface."
},
{
"option": "Feature flags at compile time (cfg(solo)) to disable auth",
"why_rejected": "Compile-time flags prevent running the same binary in both modes. Deployment would require two separate builds. A runtime flag (--mode solo) is more operationally flexible."
}
],
"consequences": {
"negative": [
"Solo mode requires starting three service binaries (vs one monolith) — managed by service-manager.nu",
"The age key on disk is the only credential that bypasses Vault — its path must be chmod 600",
"nats-server must be in $PATH for solo mode startup"
],
"positive": [
"Any script or integration written for solo mode works in multi-user without modification — only connection strings change",
"The auth bypass is isolated to one function (solo_auth_middleware) — auditing solo mode auth is a grep away",
"SurrealDB and NATS data persist across restarts in solo mode (RocksDB + JetStream storage to disk)",
"CI can run the full integration test suite against the solo mode harness without external infrastructure"
]
},
"constraints": [
{
"check": {
"cmd": "rg 'solo_auth_middleware' --include='*.rs' platform/ | grep -v '#\\[cfg(test)'",
"expect_exit": 0,
"tag": "NuCmd"
},
"claim": "solo_auth_middleware must only be activated via --mode solo runtime flag, never via environment variable or compile-time feature",
"id": "solo-mode-runtime-flag-only",
"rationale": "A runtime flag is explicit and auditable in process listings. An environment variable or compile-time flag creates an invisible bypass that cannot be detected without reading code or config.",
"scope": "platform/crates/control-center/src/lib.rs",
"severity": "Hard"
},
{
"check": {
"cmd": "rg 'master.age|vault.*key' --include='*.rs' platform/ | grep -v 'chmod|0o600|0600'",
"expect_exit": 1,
"tag": "NuCmd"
},
"claim": "The age key at ${data_dir}/vault/master.age must be created with mode 0600 and must be the only file-based secret in the platform",
"id": "age-key-file-permissions",
"rationale": "The age key is the bootstrap secret — the only credential that bypasses Vault. Strict file permissions are the only protection. Any additional file-based secrets would violate the single-secret constraint.",
"scope": "platform/secretumvault/src/solo.rs",
"severity": "Hard"
},
{
"check": {
"must_be_empty": false,
"paths": [
"platform/crates/orchestrator/"
],
"pattern": "nats-server|nats_server",
"tag": "Grep"
},
"claim": "Orchestrator must start nats-server -js as a managed child process with TCP availability wait (10s timeout) and SIGTERM on shutdown",
"id": "nats-server-child-lifecycle",
"rationale": "Unmanaged nats-server processes leak across service restarts and leave stale JetStream state. The 10s TCP wait prevents race conditions between Orchestrator and the NATS server on startup.",
"scope": "platform/crates/orchestrator/src/nats.rs",
"severity": "Soft"
}
],
"context": "The platform must run on a single operator's laptop for local development, testing, and single-operator production deployments. Two options were available: (1) Simplified mode — stripped-down binary bypassing services, writing directly to disk/files, skipping NATS and SurrealDB; (2) Full architecture with relaxed auth — same services, same NATS subjects, same SurrealDB schema, but auth middleware replaced with a no-op that auto-creates an admin session. Option 1 creates two separate code paths: solo vs multi-user. Scripts, integrations, and the CLI behave differently per mode. Testing in solo mode cannot validate multi-user behavior. Option 2 preserves a single code path with auth as the only runtime difference.",
"date": "2026-02-17",
"decision": "Solo mode uses the full architecture with relaxed auth. Every service (Orchestrator, Control Center, Vault, Extension Registry, AI/MCP) runs as the same binary with the same NATS subjects and the same SurrealDB schema. Runtime differences: SurrealDB uses embedded RocksDB in solo vs WebSocket server in multi-user; NATS uses nats-server -js child process in solo vs external cluster; auth middleware is solo_auth_middleware (auto-session, no JWT) in solo vs auth_middleware (JWT + Cedar) in multi-user; Vault auto-unseals with local age key in solo vs Shamir threshold or KMS; Cedar default-permits local user in solo vs full policy evaluation. solo_auth_middleware injects fixed UserContext { roles: [admin], mfa_verified: true, user_id: Uuid::nil() } and is gated behind --mode solo runtime flag.",
"id": "adr-015",
"ontology_check": {
"decision_string": "Solo mode uses full architecture with solo_auth_middleware as the only auth bypass, gated behind --mode solo runtime flag",
"invariants_at_risk": [
"solid-boundaries"
],
"verdict": "Safe"
},
"rationale": [
{
"claim": "Single code path eliminates solo/multi-user behavioral divergence",
"detail": "Any script or integration written for solo mode works in multi-user without modification — only the connection strings change. This makes solo mode a valid staging environment for multi-user behavior."
},
{
"claim": "solo_auth_middleware is isolated and auditable",
"detail": "The auth bypass is in one function, gated behind a runtime flag, explicitly tested. Auditing solo mode auth is a grep away: rg 'solo_auth_middleware'. This is safer than multiple ad-hoc bypasses scattered across services."
},
{
"claim": "SurrealDB and NATS data persist across restarts in solo mode",
"detail": "RocksDB + JetStream storage persist to disk. Solo mode is not ephemeral — state survives service restarts, enabling realistic local testing of long-running task scenarios."
},
{
"claim": "CI can run integration tests against the solo mode harness without external infrastructure",
"detail": "The solo mode harness (embedded RocksDB, child nats-server) runs in CI without network or external service dependencies. Full integration test coverage without infrastructure overhead."
}
],
"related_adrs": [
"adr-012-nats-event-broker",
"adr-013-surrealdb-global-store",
"adr-014-solid-enforcement"
],
"status": "Accepted",
"title": "Solo Mode — Full Architecture with Relaxed Auth"
}