102 lines
6.7 KiB
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"
|
|
}
|