Define schema binding versions and keep preparation unmounted
1 file changed,
+27
−10
implementation/legacy-schema-binding/proposal.json+27 −10
CI cancelled — superseded by a newer push — run details
diff --git a/implementation/legacy-schema-binding/proposal.json b/implementation/legacy-schema-binding/proposal.json| index 3be70ed..4f2455e 100644 |
| --- a/implementation/legacy-schema-binding/proposal.json |
| +++ b/implementation/legacy-schema-binding/proposal.json |
| @@ -1,5 +1,5 @@ |
| { |
| - "status": "Proposed; independent Fable review required before implementation. No runtime authority is enabled by this proposal.", |
| + "status": "Revision2, addressing actual claude-fable-5-1 CHANGES REQUIRED on9ece6a7; independent re-review required. No runtime authority enabled.", |
| "task": "Home61: bind historical-empty schema recognition to independently enrolled, signed and verified native launches, and repeat it in readonly result verification.", |
| "source_baseline": "549a277773e03c42166c3ebde84589b4a2156359 on feat-legacy-schema-profile. This design branch starts from main7d68e87 as AGENTS requires; baseline implementation is a dependency, not implicitly approved for merge.", |
| "observed": { |
| @@ -17,7 +17,7 @@ |
| "directory-historical-empty-v1" |
| ], |
| "meaning": "Each immutable profile binds its role, full exact migration ID/checksum set, full exact SQLite schema objects (including ledger and replication objects), explicitly empty table set, and validation-policy version. A changed schema/policy requires a new profile identity and digest; names never silently acquire new meanings.", |
| - "digest": "SHA256 lower-case hex of deterministic canonical JSON manifest bytes. Freeze the manifest, original migration provenance and expected digest in source, and test against real sequential migration replay. Historical ordering is BootSchema then directory runtime migrations, with identical-SQL duplicate IDs deduplicated by first occurrence; never use migrate.Merge directly on overlapping sets.", |
| + "digest": "SHA256 lower-case hex over the exact embedded UTF-8 manifest file bytes, including its single trailing LF. Encoding is encoding/json.MarshalIndent(manifest, \"\", \" \") plus LF, with standard HTML escaping enabled, not RFC8785 or go-json-experiment Deterministic. Fixed struct field order: version, id, role, validation_policy, migrations, objects, empty_tables. Migration fields: id, checksum; object fields: Type, Name, Table, SQL (matching current frozen schema objects). Sort migrations by bytewise ID, objects by Type then Name, and empty_tables bytewise; use non-null arrays, version integer1, no omitted fields, duplicate entries or arbitrary maps. Strict decoder rejects unknown/duplicate fields and compares exact re-encoded bytes to the embedded source. Golden bytes/digests freeze this convention; do not normalize whitespace before hashing.", |
| "historical_empty_tables": [ |
| "people", |
| "signin_links", |
| @@ -26,26 +26,39 @@ |
| "passkey_pending", |
| "passkey_recovery_codes" |
| ], |
| - "sessions": "Preserve the shared sessions table and its data exactly. Historical profile does not prove session confidentiality, key custody or credential absence outside the six tables. Existing readonly session/key continuity checks remain mandatory. Any session cleanup or custody transition is separate reviewed work.", |
| - "separation": "Keep diagnostic_profiles and its accepted-embed guard unchanged. New admission manifests are a separate catalog; do not import diagnostic result types, log readers or startup state into admission. A bounded pure schema comparator can be shared after review, but observations cannot authorize profile selection." |
| + "sessions": "Preserve shared sessions and data exactly. Existing readonly checker does not scan session rows: checked immutable database bytes provide non-mutation evidence, while enrolled key fingerprints prove only key continuity. Rows minted by the earlier combined Home can remain valid directory sessions until their normal expiry; this change does not revoke them or establish their historical custody. Any session cleanup/custody transition needs separate review.", |
| + "separation": "Keep diagnostic_profiles and its accepted-embed guard unchanged. New admission manifests are a separate catalog; do not import diagnostic result types, log readers or startup state into admission. A bounded pure schema comparator can be shared after review, but observations cannot authorize profile selection.", |
| + "fresh_consistency": "Existing compatibility_profiles/credential.json and directory.json remain the mounted fresh schema source of truth and keep their current exact embed guard. New catalog fresh manifests coexist as commitments to those exact object arrays, plus the same compiled migration/checksum sets. Mandatory equality tests compare each fresh manifest to its existing frozen file and role migration replay; any drift fails CI. Stage A does not replace or edit the existing files. Historical manifest independently freezes the proven diagnostic objects and provenance, with mandatory equality and replay tests; diagnostic output is never consumed.", |
| + "comparator_location": "Private admission-only schema checker lives in internal/home/schema_commitment_linux.go and is reachable only from tests during stage A. It uses the selected catalog manifest and bounded exact comparison/occupancy logic. No imports from operator into legacy_schema_diagnostic.go and no calls into admission from diagnostic startup. Existing mounted operatorSchemaCheck and compatibility checker stay unchanged in stage A. Stage B may make both real adapters call the same admission checker only after its own design/source review and canary gate; no silent comparator replacement in stage A." |
| }, |
| "enrollment": { |
| "new_version": "InstallationPlan version2 adds schema_profile and schema_manifest_digest to each role target. Closed JSON and exact role/profile/digest matching. Version1 retains its existing fresh-only semantics; it cannot authorize historical compatibility.", |
| "authority": "The existing EnrollPlan is local preparation only. Its encrypted signed journal commits the profile, release artifact and protected environment digest, but does not assert owner approval, held fence, worker eligibility or correct key custody.", |
| "immutability": "Prepared operations must agree with the enrolled target. Never rewrite an existing plan, fill missing fields from an inspected database, or overwrite an in-flight/ambiguous operation to change its profile. A profile transition requires a separately reviewed reenrollment/lifecycle operation; none is introduced here.", |
| - "manifest_availability": "The full known manifest must be included in the reviewed release. Unknown IDs, digest mismatch, wrong role, partial profiles, empty fields and attempts to select historical on a credential role refuse." |
| + "manifest_availability": "The full known manifest must be included in the reviewed release. Unknown IDs, digest mismatch, wrong role, partial profiles, empty fields and attempts to select historical on a credential role refuse.", |
| + "release_changes": "A new additive migration requires a new profile identity/digest and reviewed release enrollment. Existing v2 plans remain pinned and refuse the new schema; this matches their existing release/artifact immutability. No automatic plan upgrade or reenrollment verb in this task." |
| }, |
| "signed_request": { |
| "version2": "Add the same schema_profile and schema_manifest_digest pair to version2 operator Authorization and version2 InspectChallenge. Verify equality with independently pinned expectations before database open. Include them in signature input, authorization head and journal replay checks; inspect uses its existing distinct signature context.", |
| - "version1": "Preserve exact existing canonical v1 bytes with explicit version-specific wire structs/decoders. Existing v1 signature/recovery tests must remain valid. V1 always means existing fresh-only behavior, never a fallback from a rejected v2 request.", |
| + "version1": "Use explicit version-specific wire structs/decoders and version agreement matrix; retain exact v1 canonical bytes, signature contexts and recovery behavior. Golden captured v1 fixtures verify unchanged decoding/signature input; do not depend on fresh ECDSA signatures reproducing bytes. No downgrade/fallback after any v2 refusal.", |
| "pins": "Extend verified configuration with explicit profile commitments. A v2 request must match v2 protected command configuration and the enrolled plan. Do not trust a signed field alone: a trusted provisioner key may sign for several targets, so role/origin/installation/registry/device/action binding and exact enrolled profile all still apply.", |
| - "recovery": "Retain existing authentic expired request recovery semantics. Repeat the selected exact schema and occupancy check before returning an idempotent result; unexpected drift or new legacy rows remains refused even on replay. No resigning or profile substitution for ambiguous operations." |
| + "recovery": "Retain existing authentic expired request recovery semantics. Repeat the selected exact schema and occupancy check before returning an idempotent result; unexpected drift or new legacy rows remains refused even on replay. No resigning or profile substitution for ambiguous operations.", |
| + "version_matrix": { |
| + "operator_input_v1": "InstallationPlan1 -> commandConfig1 -> wrapper.version1 -> envelope.v1 -> Authorization.version1. No schema commitment fields; existing fresh-only behavior. Exact existing wire bytes unchanged.", |
| + "operator_input_v2": "InstallationPlan2 -> commandConfig2 -> wrapper.version2 -> envelope.v1 -> Authorization.version2. The generic signature envelope stays v1 and existing signature context stays operator-input/v1; payload schema and wrapper become v2. Schema commitments mandatory and equal to enrolled/protected pins.", |
| + "operator_inspect_v1": "InstallationPlan1 -> commandConfig1 -> envelope.v1 -> InspectChallenge.version1; existing distinct operator-inspect/v1 signature context.", |
| + "operator_inspect_v2": "InstallationPlan2 -> commandConfig2 -> envelope.v1 -> InspectChallenge.version2; same distinct operator-inspect/v1 context, mandatory equal profile commitments. No wrapper around inspection.", |
| + "mismatches": "Refuse v1 request under v2 config, v2 request under v1 config, v2 plan signing v1 authorization/challenge, and v1 plan signing v2 authorization/challenge; also refuse envelope.v2, mismatched wrapper/payload versions and commitment fields on any v1 wire.", |
| + "operation_plan": "OperationPlan stays version1 with existing input formats. The enrolled installation plan alone selects authorization protocol version. Operation input cannot choose a protocol/profile or override the enrolled target.", |
| + "launchctx": "These application protocol versions are unrelated to launchctx mutable-context v1 and readonly-context v2. Keep platform launch wire unchanged." |
| + } |
| }, |
| "launch_binding": { |
| "operator": "The platform launch already binds fd0 input SHA256, artifact digest and protected environment envelope digest. Keep its shared transport wire unchanged. The independently verified platform enrollment/owner/fence/lease path must bind those exact values to the plan, including the environment containing protected command profile pins. Home verifies signed input and matching protected pins before constructing the database connector.", |
| "missing_authority": "Current descriptor checks and a non-nil callback do not establish that platform authority. Until the runtime integration verifies the plan/environment/artifact/attempt binding plus isolation/IAM eligibility, no production code path may select the historical profile. Do not add a HOME_ALLOW_LEGACY flag or relax the currently mounted operatorSchemaCheck.", |
| "readonly": "Readonly expectation gains the exact immutable schema commitment inside its verified launch/enrollment evidence, not an unsigned optional argument. It must agree with the operation commitment and enrolled target on both pre-open and final verification. Also retain pinned database bytes, inode/device, artifact, key fingerprints, operator state/head, witness, generation and attempt identity checks.", |
| - "unavailable": "The readonly production verifier is not implemented today. Keep the checker unmounted and historical operator selection unavailable until the verified adapter exists. Synthetic tests may exercise a private implementation, but must not be described as runtime authorization." |
| + "unavailable": "The readonly production verifier is not implemented today. Keep the checker unmounted and historical operator selection unavailable until the verified adapter exists. Synthetic tests may exercise a private implementation, but must not be described as runtime authorization.", |
| + "readonly_uninitialized": "Readonly compatibility continues to require initialized operator state and an enrolled witness. It is a post-operation/resume check, not initial adoption preflight. Uninitialized historical state is refused by this checker; initial adoption uses the separate approved signed inspection/operator transaction with fresh owner/fence/lease authority. No relaxation of state/witness checks." |
| }, |
| "database_checks": { |
| "before_mutation": "After verified launch selection, run exact ledger/schema checks and all six empty-table EXISTS checks on the same connection inside the existing BEGIN IMMEDIATE transaction before replay or mutation. Keep foreign-key checks, replication shape, row and byte budgets, state/sequence/key checks. No schema migration or data cleanup from operator/checker.", |
| @@ -57,7 +70,8 @@ |
| "stages": [ |
| { |
| "stage": "A: offline commitments and schema checker", |
| - "deliverable": "Versioned catalog, local plan and signed wire binding with private schema checks and adversarial tests. Production operator accepts existing fresh-only profiles; readonly checker remains unmounted. No claim that task61 as a whole is complete." |
| + "deliverable": "A: unmounted primitives only. Private v2 manifest, plan, authorization/inspection decoders and constructors plus private schema checker, exercised only by synthetic tests. No production CLI, journal, command, serving or readonly call-site changes. Existing parseCommandConfig remains version1-only; public Verifier.Verify refuses wrapper/envelope/authorization v2 and cannot call private v2 decoders; EnrollPlan/prepareRequest keep version1-only. No v2 fresh acceptance is shipped here. Future stage B connects the private machinery under its own reviewed integration. This does not complete task61.", |
| + "guard": "Source-level AST dependency guard roots at cmd/home main, RunOperatorCommand, RunCommand, parseCommandConfig, Verifier.Verify, SignRequest, EnrollPlan, prepareRequest and inspectCompatibilityDatabase; fail if any reachable production path references private v2 constructors/decoders or historical admission catalog/checker. Explicitly freeze existing version1-only dispatch checks. Tests may call private constructors. Do not treat a single historical allowlist as isolation." |
| }, |
| { |
| "stage": "B: verified adapter integration", |
| @@ -77,7 +91,10 @@ |
| "Post-diagnostic occupancy and post-operation drift still refuse; no diagnostic output or request flag can reach admission.", |
| "Failure before transaction writes or cached result return; BEGIN uncertainty and rollback failure evict; cancellation and closure errors refuse; readonly final bytes unchanged.", |
| "Guard that production dispatch remains fresh-only until the real verified adapter is connected. Tests for adapter must cover missing/stale/incorrect plan, owner approval, held fence, lease, environment, artifact, attempt identity, profile and readonly expectation.", |
| - "Required full Home tests, race, formatting, vet, build, exact source CI and Fable source review for each implemented stage; no runtime or public-gateway claim from codec tests." |
| + "Required full Home tests, race, formatting, vet, build, exact source CI and Fable source review for each implemented stage; no runtime or public-gateway claim from codec tests.", |
| + "AST reachability/source guards prove v2 constructors, historical manifests and admission checker are unreachable from all mounted command, CLI, journal and readonly roots in stage A; signed v2 input and v2 config refuse at current public entrypoints.", |
| + "Golden manifest exact-byte SHA256, re-encoding equality, fresh frozen-profile equality and historical source-provenance equality; ordering/escaping/trailing newline mutations fail.", |
| + "Explicit version matrix including envelope staying1, mixed wrappers/payloads, cross-plan/config request versions, both mutation and signed inspection, and OperationPlan unable to select protocol version." |
| ], |
| "platform_work_to_check": { |
| "126": "isolated-exec build readiness, dev-fleet rollout, explicit migration of existing instances, observed nested/ready layout and peer data/process isolation including the console. External routes need supervisor isolation evidence. Revert/downgrade must refuse with active worker authority.", |