16 KiB
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.
Revision 2 (luna review F1–F7): the identity precondition restated in identity-contract terms with a conversion-local acknowledgment record this contract owns (F1); a durable, keyed preparation state with a Standalone-safe representation rule, an in-transaction re-check fence, and an exact flip boundary (F2); the §5.4 unknown-value rule stated directly without the contradictory non-exhaustiveness clause (F3); the D14 boundary bound here with a stable column-allowlist witness instead of delegated to an unratified layout (F4); the conversion witness matrix extended to every §4.2/§4.4 condition (F5); the mode-record writer coverage imported concretely from contract 1 §6.3 with a named schema, closed writer set, crafted-write probe, and mode-resolution assertion (F6); the mode read command flagged as a §12.1 drafting addition rather than a D8 mandate (F7). Ownership language aligned with contract 3 revision 2: mode is recorded at bootstrap and read by the wizard as input.
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 branching (contract 3 §2), identity/SSO
(identity-lifecycle.md), custody and per-user brain mechanics
(contract 7, custody-schema.md), tool mapping
(tool-gateway-mapping.md).
1. Definitions
- Mode: the platform-wide deployment mode, exactly one of
standaloneorenterprise. The vocabulary is closed in v1; extension (e.g. a federation mode) is by amendment to this contract, never ad hoc. - Conversion: the one-way transition
standalone → enterprise. No other mode transition exists. - Conversion preconditions: the verifiable conditions of §4.2 that must all hold before the mode record may change.
- Preparation unit: one re-runnable piece of pre-conversion work — the migration of one secret to the Vault backend, or the partition of one user's brain content (§4.3).
2. Mode is a canonical recorded property
- Mode is recorded canonically in the platform database at bootstrap
as the operator's install-time choice (D3: modes are "chosen at
install time"). The record is a single-row keyed record
(
platform_mode: mode value, recorded-at timestamp, bootstrap epoch reference); this contract owns it, the bootstrap writer performs the one v1 write (§6.2), and the wizard reads it as input (contract 3 §2.3). Mode 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. - The record is readable by any authenticated user through a Gateway
command with CLI exposure. This read command is a drafting
addition ratified with this contract (PRD §12.1), not a D8
mandate: D8 binds only that any surface exposing the value goes
through official tooling. When a webUI surface consumes the read, a
mapping row is added to
tool-gateway-mapping.mdby amendment — the same route §4.4 already binds for the conversion command. Components branch on the read value only. - 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) |
The Standalone brains row states the default layout, not the only valid one: the D14 per-user split is a MAY in Standalone with keeping it the recommended default (PRD §7, contract 7 §6), and Vault-backed secrets are equally valid Standalone configuration. Both prepared states are therefore themselves valid Standalone states — the fact §4.3 relies on.
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
- Direction and terminality. The only transition is
standalone → enterprise.enterprise → standalonedoes 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). - Preconditions (all verified before the record changes):
- Secrets: OpenBao/Vault is configured and reachable, and every required secret is served from the Vault backend — none 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 that is active in identity-contract terms — authenticated capability, not banned, not deactivated (identity §2, §5). And the conversion request carries a configuration acknowledgment: the current canonical values of registration mode and per-provider JIT enablement (identity §2.2, §4.1), echoed back in the request. A mismatch between the echoed values and the canonical values at verification refuses the conversion. This acknowledgment record is conversion-local, owned by this contract, and stored with the §4.4 audit event as the precondition evidence; it adds no identity-contract obligation and no mode-specific identity default — identity's own defaults remain valid states.
- Preparation state and the flip boundary. Preparatory work is
tracked durably: each preparation unit (§1.4) records its
completion in a preparation table keyed by (bootstrap epoch, unit
identity — the secret's path, the user's id), written in the same
transaction as the unit's own effect where the unit's backend
allows it, and reconciled from the backend's actual state where it
does not (a secret already served by Vault, a brain already split,
is complete regardless of the table). Units are at-most-once per
key and re-runnable across attempts. Standalone-safe
representation: every preparation unit moves the deployment into
a state that is itself valid Standalone configuration (§3 note), so
an interrupted preparation leaves a fully operational Standalone
deployment reading its state through the ordinary contracts — no
rollback, fencing, or special Standalone read path is needed, and
no component behavior may key on "preparation in progress".
The flip: one transaction that (a) locks the mode record, (b)
re-verifies every §4.2 precondition after acquiring the lock, and
(c) writes the mode record and the §4.4 audit event. Any re-check
failure aborts with no write. External state that changes after the
re-check but before commit is bounded by the transaction window;
an external backend (Vault) failing after conversion is an
Enterprise runtime fault handled by §3's fail-closed steady-state
rule, not a conversion defect. An interrupted or failed conversion
leaves the record
standaloneand the platform fully operational; there is no intermediate mode and no half-converted state observable through the record. - Authority and audit. Conversion is a platform-administrator
command carrying an explicit irreversibility acknowledgment in its
request (distinct from the §4.2 configuration acknowledgment). 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 including the configuration acknowledgment) in the same transaction as the record change; the event survives indefinitely. A refused attempt emits a refusal event naming the failed precondition class and actor, with no mode-change event.
5. v1 obligations (non-foreclosure)
v1 ships Standalone only (D11); the conversion command is deferred work. v1 still MUST:
- Record the mode per §2 at bootstrap, with
enterprisea reserved, refused value for bootstrap — v1 bootstrap acceptsstandaloneonly. The wizard reads the record (contract 3 §2.3); nothing in v1 writes it after bootstrap. - Keep the §2.3 immutability rule: no v1 surface mutates the mode record.
- Not foreclose conversion: the v1 platform database holds no sensitive user content — sensitive categories live in the owning user's brain, and postgres holds structure, consent records, and pointers only (the D14 boundary, PRD §7). Custody mechanics are contract 7's; this contract binds the boundary itself here so v1 cannot ship a layout that makes the §4.2 brain precondition unsatisfiable, and §6.3 gives it a stable witness that does not depend on contract 7's internals. Conversion implementation additionally requires contract 7 ratified.
- Not foreclose federation: v1 components accept exactly the two §1.1 values wherever a mode value is parsed and refuse any other value before side effects — a refused configuration, not undefined behavior and not a crash mid-operation. Forward compatibility lives in storage and architecture, not in parser speculation: the mode record's storage is not structurally locked to two values (no database-level two-value enum), and any future value (e.g. a federation mode) is defined by a versioned amendment to this contract before any component accepts it. The PRD defers federation's shape entirely; this contract does not presume it arrives as a third mode value.
6. Verification requirements
Binding on the implementing PRs:
- Mode-record witness (v1): after bootstrap the mode is readable
via the Gateway command and CLI and equals the bootstrap-recorded
choice; bootstrap with mode
enterpriseis refused; bootstrap with any unknown mode value is refused before side effects (§5.4). - Writer-coverage witness (v1): the mode record's writer set is
closed by the same three-prong static assertion contract 1 §6.3(b)
defines — symbol, class-table literal, and raw-execution prongs
with its allowlist composition rules — scoped to the
platform_modetable, with a writer allowlist containing exactly the bootstrap writer in v1 (and exactly plus the conversion command at the conversion milestone). Companions: a crafted direct write attempted in a test fails and leaves the record unchanged; a mode-resolution assertion that no shipped component derives mode from feature state (mode reads occur only through the §2.2 read surface — static assertion over Gateway, CLI, bootstrap, and repository sources). - D14-boundary witness (v1): a column-allowlist assertion in the style of contract 1 §6.2 that the platform database schema contains no sensitive-content column — the §5.3 boundary — stable regardless of contract 7's internals (contract 7 §7 carries the full custody witnesses).
- No-downgrade witness (conversion milestone): with mode
enterprise, a conversion request tostandalone(and any crafted mode-write) is refused with the precondition/state error class and no record change. - Precondition witnesses (conversion milestone), each refused with no record change and no partial mode effect, parameterized over both OpenBao and Vault where secrets are involved: (a) secrets backend unreachable; (b) one required secret still flat-file backed (migration incomplete); (c) one unpartitioned user brain in a multi-user deployment where every other user is partitioned; (d) no active platform administrator (the only admin banned or deactivated); (e) configuration acknowledgment missing or mismatching the canonical registration/JIT values; (f) actor not a platform administrator (authorization refusal); (g) irreversibility acknowledgment absent. And the steady-state rule: an Enterprise-mode component started against a flat-file secrets configuration refuses to start (§3).
- Interruption and fence witnesses (conversion milestone): fault
injection aborting conversion after each preparation unit and
between preparation and flip leaves the record
standaloneand the platform operational in Standalone semantics (§4.3 Standalone-safety), and a re-attempt completes without duplicating prepared state (at-most-once keys); a precondition invalidated after preparation but before the flip (a secret reverted to flat-file) is caught by the in-transaction re-check and refused. - Audit witnesses (conversion milestone): a completed conversion has exactly one mode-change audit event, same-transaction with the record change (transaction linkage asserted), carrying actor, prior mode, new mode, and the precondition evidence reference including the configuration acknowledgment; a failed attempt has a refusal event naming the failed precondition class and no mode-change event; the mode-change event remains queryable after subsequent unrelated audit activity (retention probe).
- Mapping witness (conversion milestone): the conversion command
and the mode read command each have their
tool-gateway-mapping.mdrow (added by amendment per §2.2/§4.4) before the commands ship.
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, theenterprisevalue is reserved and refused, and the conversion command itself is deferred to the Enterprise milestone, consistent with D11's deferred list. v1 carries three obligations beyond the record: the closed writer assertion, the D14 column boundary, and the unknown-value refusal (§6.1–§6.3) — these are the non-foreclosure floor, not hidden conversion work. 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.