From be32b693d0700e061f3c77dfc2b6b8ff11c8fb36 Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 18:58:12 -0500 Subject: [PATCH] docs: deployment mode and conversion contract (S2 contract 6) --- docs/requirements/mode-conversion.md | 165 +++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 docs/requirements/mode-conversion.md diff --git a/docs/requirements/mode-conversion.md b/docs/requirements/mode-conversion.md new file mode 100644 index 00000000..f5a1719e --- /dev/null +++ b/docs/requirements/mode-conversion.md @@ -0,0 +1,165 @@ +# Deployment Mode and Conversion Contract (D3) + +Status: DRAFT — awaiting ratification (webui-audit S2, contract 6 of 9). +Authority: PRD D3 (Part I §3) — two modes chosen at install time, +Standalone and Enterprise, with the mode table (brains, user-data +isolation, secrets, conversion); Standalone → Enterprise conversion is +**one-way** and Enterprise is a **terminal state**. PRD D14 (Part I §7) +— the per-user brain split is optional in Standalone and keeping it is +the recommended default because it preserves forward-compatibility with +the one-way conversion. PRD D11 (Part I §9) — v1 ships the Standalone +flow only; Enterprise conversion is explicitly deferred. PRD D3 +federation clause — federation is intentionally not fully designed, +deferred, and nothing in v1 may foreclose it. + +This contract binds the mode as a canonical platform property (§2), the +per-mode obligations and which contract owns each (§3), the conversion +transition (§4), the v1 non-foreclosure obligations (§5), and their +witnesses (§6). Domain semantics stay with their owning contracts: +wizard mode choice and branching (contract 3 §2), identity/SSO +(`identity-lifecycle.md`), custody and per-user brain mechanics +(contract 7), tool mapping (`tool-gateway-mapping.md`). + +## 1. Definitions + +1. **Mode**: the platform-wide deployment mode, exactly one of + `standalone` or `enterprise`. The vocabulary is closed in v1; + extension (e.g. a federation mode) is by amendment to this contract, + never ad hoc. +2. **Conversion**: the one-way transition `standalone → enterprise`. + No other mode transition exists. +3. **Conversion preconditions**: the verifiable conditions of §4.2 that + must all hold before the mode record may change. + +## 2. Mode is a canonical recorded property + +1. Mode is recorded canonically in the platform database at bootstrap, + as the explicit operator choice contract 3 §2.3 requires. It is + never derived from feature state (presence of Vault, count of + brains, count of users), and no component may infer a different + mode than the record states. +2. The record is readable through the Gateway (and CLI, D8) by any + authenticated user; components branch on the read value only. +3. The record is immutable except by the §4 conversion transition. + Editing it by direct database access, config file, environment + variable, or wizard re-run is non-conformant (contract 3 §2.3: + changing mode later is conversion, not a wizard re-run). + +## 3. Per-mode obligations (owner map) + +The PRD mode table binds four rows; this contract assigns each an +owning contract so no obligation is unowned and none is bound twice: + +| Obligation | Standalone | Enterprise | Owner | +| ------------------- | -------------------------------------- | -------------------------------------------------- | --------------------------------------------- | +| Brains | one mosaic-brain (system + user files) | system brain for config + one brain per user | contract 7 (custody/brain mechanics) | +| User-data isolation | single user | no user-data leakage between users; sharing opt-in | contract 7 (enforced by architecture, D14) | +| Secrets | OpenBao/Vault or flat files | OpenBao/Vault REQUIRED | this contract (§4.2 gate; steady-state check) | +| Conversion | may convert to Enterprise, one-way | terminal state | this contract (§4) | + +In Enterprise steady state, a flat-file secrets backend is +non-conformant; the platform refuses to start Enterprise-mode +components against a flat-file secrets configuration (fail-closed, not +warn-and-run). + +## 4. Conversion transition + +1. **Direction and terminality.** The only transition is + `standalone → enterprise`. `enterprise → standalone` does not exist: + there is no command, no admin override, and no support path. An + attempt is refused with the precondition/state error class of the + command envelope (`tool-gateway-mapping.md` §4.2). +2. **Preconditions (all verified before the record changes):** + - Secrets: OpenBao/Vault is configured and reachable, and no + required secret is served from a flat-file backend. Secret + migration completes before conversion; this contract does not + define the migration tooling, only the gate. + - Brains: the per-user brain split required by the Enterprise row of + §3 is established for every existing user (or the deployment + already kept the split, the D14 recommended default). Brain + partitioning mechanics are contract 7; this contract binds only + that the split is complete before the mode flips. + - Identity: at least one platform administrator account exists and + the identity contract's registration controls are in a + deliberately chosen state (`identity-lifecycle.md` §2) — recorded + as re-affirmed at conversion, since Enterprise changes their + consequences. +3. **Atomicity of the flip.** The mode record changes in one + transaction, only after every §4.2 precondition verifies. An + interrupted or failed conversion leaves the record `standalone` and + the platform fully operational in Standalone semantics; there is no + intermediate mode and no half-converted state observable through + the record. Preparatory work (secret migration, brain partitioning) + is re-runnable and may complete across multiple attempts, in the + same at-most-once, keyed style contract 3 §4 binds for wizard + seeding. +4. **Authority and audit.** Conversion is a platform-administrator + command carrying an explicit irreversibility acknowledgment in its + request. It is an official Gateway/CLI command (D8): when built, it + is added to the tool↔Gateway mapping by amendment + (`tool-gateway-mapping.md` §3.3). The transition emits an audit + event (actor, prior mode, new mode, precondition evidence + reference) in the same transaction as the record change; the event + survives indefinitely. + +## 5. v1 obligations (non-foreclosure) + +v1 ships Standalone only (D11); the conversion command is deferred +work. v1 still MUST: + +1. Record the mode per §2 (the wizard already binds the choice, + contract 3 §3.2) with `enterprise` a reserved, refused value for + bootstrap — v1 bootstrap accepts `standalone` only. +2. Keep the §2.3 immutability rule: no v1 surface mutates the mode + record. +3. Not foreclose conversion: v1 data layouts follow the custody + contract's pointer/consent structure (contract 7) so the per-user + brain split remains establishable, matching the D14 recommendation. +4. Not foreclose federation: the closed mode vocabulary is extensible + by amendment, and no v1 component may treat the mode enum as + exhaustive in a way that fails closed against a future added value + at parse time (unknown mode values are a refused configuration, not + undefined behavior). + +## 6. Verification requirements + +Binding on the implementing PRs: + +1. **Mode-record witness (v1):** the mode is readable via Gateway and + CLI after bootstrap and equals the wizard-recorded choice; + bootstrap with mode `enterprise` is refused; no other write path to + the record exists (covered by a writer assertion in the style of + contract 1 §6.3, scoped to the mode record). +2. **Immutability witness (v1):** every mutation surface shipped in v1 + leaves the mode record unchanged (asserted over the Gateway + command inventory); direct-write attempts through shipped code + paths do not exist. +3. **No-downgrade witness (conversion milestone):** with mode + `enterprise`, a conversion request to `standalone` (and any crafted + mode-write) is refused with the precondition/state error class and + no record change. +4. **Precondition witnesses (conversion milestone):** conversion with + (a) an unreachable Vault, (b) a required secret still flat-file + backed, or (c) an unpartitioned user brain is refused with no + record change and no partial mode effect. +5. **Interruption witness (conversion milestone):** fault injection + aborting conversion after preparatory work leaves the record + `standalone`, the platform operational, and a re-attempt able to + complete without duplicating prepared state. +6. **Audit witness (conversion milestone):** a completed conversion + has exactly one audit event, same-transaction with the record + change; a failed attempt has a refusal event and no mode-change + event. + +## Ruling request + +Ratify sections 1–6 as written, with one decision embedded: + +- Decision (§5): v1 implements the **mode record and its immutability + only** — bootstrap records `standalone`, the `enterprise` value is + reserved and refused, and the conversion command itself is deferred + to the Enterprise milestone, consistent with D11's deferred list. + Alternative if rejected: build the conversion command inside v1 — + rejected because D11 scopes v1 to the Standalone slice and + conversion depends on contract 7 custody mechanics that are + themselves not in the v1 slice.