Merge branch 'design-checker-launch' into main
2 files changed,
+208
−0
implementation/maintenance-checker-launch.md+54 −0implementation/maintenance-checker-launch.schema.json+154 −0
CI passed — run details
diff --git a/implementation/maintenance-checker-launch.md b/implementation/maintenance-checker-launch.md| new file mode 100644 |
| index 000000000..8a56cae35 |
| --- /dev/null |
| +++ b/implementation/maintenance-checker-launch.md |
| @@ -0,0 +1,54 @@ |
| +# Closed-checkpoint context for compatibility checkers |
| + |
| +This proposed supplement extends approved maintenance-launch8eb with a checker-only version2 context. It implements no worker or Home startup adapter. Source is platform2ace60d24ba51c6981a3bd64420ff7087b722bfd and approved Home checker89e as merged in6bf094416deebb3f29cbe21f820e2eae10091ab3. The preserved source documents contain historical pending-review wording; their recorded approvals, not that wording, establish the parent contracts. This supplement requires its own independent review. |
| + |
| +## Two launch modes |
| + |
| +Version1 remains the existing mutable operator context, with its exact closed fields and mode=rw database connector. A checker never accepts version1 or falls back to that connector. Version2 is exclusively execution_kind="compatibility-check", dispatched by the fixed reviewed app checker argv before serving configuration, migrations or application construction. An operator never accepts version2. Neither mode discovers its authority by probing fd3. Normal serving's executable-only fd3 ABI remains separate and unchanged. |
| + |
| +The inherited pipe is evidence only within the approved trusted-platform/shared-UID boundary. The same fresh human-owner approval, immutable request/attempt/record/artifact/environment bindings, lifecycle claim, admission fence, healthy retained lease and exclusive executor are required. A context, digest or local fixture cannot establish those facts by itself. No request JSON, environment flag, caller pathname, enrollment shortcut or newly minted worker credential can construct a trusted invocation. |
| + |
| +## Version2 closed wire |
| + |
| +The companion JSON Schema describes shape, not authority or complete lexical validation. Version2 contains every version1 field with identical meaning and typed bounds, except version=2, plus required execution_kind, operation_id, expected_state_sha256 and closed_checkpoint. Unknown/duplicate/case-aliased keys, null, trailing JSON, noncanonical integer tokens, invalid UTF-8, invalid route identity, invalid timestamp spelling and excess bytes refuse. Entire fd3 payload remains at most8192 bytes, read with one overflow byte and required EOF. Decode unsigned device/inode directly into uint64 and positive generation into int64, never through float64. Canonical UTC RFC3339Nano means parsing then formatting reproduces the exact string. All IDs use canonical unpadded base64url of32 bytes. All SHA256 values use64 lowercase hexadecimal characters. |
| + |
| +operation_id is the stable platform lifecycle OperationID derived from the trusted predecessor lineage, distinct from request_id/attempt_id and any private Home operation. It is copied from the matched guard, not chosen by the checker caller. expected_state_sha256 binds exact private expected-state bytes inside the already approved checker input. Platform treats those bytes as opaque: the Home adapter verifies their enrolled provenance using the approved checker contract and checks the digest; matching a caller-supplied digest alone establishes no enrollment. Those independently verified expected-state bytes must include exact role/origin; Home compares them to the pinned configuration before database access. Their digest and the full input/environment bindings carry role/origin transitively, with no duplicate platform fields or platform parsing of Home enrollment. The current private checker component has no production provenance provider and cannot manufacture this invocation. The checker input's exact full bytes remain bound by input_sha256. |
| + |
| +closed_checkpoint has exactly these required fields: |
| + |
| +- version: integer1. |
| +- preparation_id: fresh canonical32-byte ID identifying this one worker preparation under this exact checker attempt. It is a correlation ID, not a lease token or durable executor capability. |
| +- completed_at: canonical UTC RFC3339Nano, when all preparation handles have closed and final main-byte/absence observations completed. It must not be later than adapter current time or execution_deadline; the adapter's whole invocation remains bounded by min(execution_deadline, start+30seconds). Wall time does not replace worker monotonic lease checks. |
| +- main_length: canonical positive integer, at most268435456 bytes. |
| +- main_sha256: hash of exactly main_length bytes from the retained verified main descriptor, with EOF/size/identity checks, after all preparation SQLite handles close. |
| +- evidence_sha256: digest of the exact canonical binding below. The outer database_path/device/inode supply main identity; duplicates inside this object are forbidden. |
| + |
| +Canonical preparation binding bytes are Go encoding/json.Marshal (default escaping, no newline) of the ordered array: |
| +["carlos/closed-checkpoint/v1", account, app, host, operation_id, request_id, attempt_id, request_digest, record_identity, artifact_digest, environment_envelope_digest, guard_generation, input_sha256, expected_state_sha256, database_path, database_device, database_inode, execution_deadline, preparation_id, completed_at, main_length, main_sha256]. |
| +Typed integers remain integers in that encoding. evidence_sha256 is SHA256 of those bytes; no recursive field, ad hoc object canonicalization, new signature or new encrypted envelope is introduced. The digest is private because it binds input and database fingerprints. It is never a public receipt, release metadata or log field. Home checks recomputation and echoes the opaque evidence digest only in its bounded private result. Equality correlates this invocation's evidence; it does not prove the preparation happened. |
| + |
| +## How the worker earns that context |
| + |
| +The worker must construct a private, unexported prepared-checker object only after the approved Home checker database_opening.worker_order succeeds. Its owner holds the same actual Held object, exclusive executor claim and verified artifact/environment/DB bindings through the whole interval. The prepared-checker object stores preparation_id, exact binding bytes and completed proof objects; its only launch method consumes it once. It is not reconstructed from serialized context. No public constructor accepts booleans, digest strings or exported receipt-shaped JSON in place of these proof objects. |
| + |
| +Concretely, checked old main/sidecar and predecessor executor exclusion plus old replication Close precede lease acquisition. Under the retained Held, Held.Checkpoint completes SyncAndWait and checked store Close. A single existing-only ordinary mode=rw preparation connection then uses pinned modernc FileControlPersistWAL("main",0) with readback0, integrity_check exactly ok, and wal_checkpoint(TRUNCATE) exactly busy=0/log=0/checkpointed=0. Checked final connection/DB close precedes main hashing and actual absence checks for WAL, SHM and rollback journal. No manual sidecar unlink, empty-file exception or unchecked cleanup is allowed. The readonly data namespace includes parent directory and is established before checker spawn. No other writer, checkpoint, replica reopen, adoption or checker overlaps this interval. |
| + |
| +Actual internal/lease/checkpoint.go exposes only CompletedAt and SafeUntil. SafeUntil is meaningful only with its in-process monotonic component; Held exports no persistent lease ID and CheckpointReceipt is not stop/cleanup/absence evidence. Keep the same actual Held reference and continue SafeUntil/health supervision. preparation_id is not a replacement lease identity. Construction must also retain independently checked stop/executor/namespace/preparation results from the future worker. Those concrete worker types and durable provenance publication remain unimplemented dependencies; this wire proposal neither defines a universal worker credential nor supplies missing executor proof. After restart/takeover, no old prepared object/context/digest is reusable; independently proved predecessor exclusion and a new approved attempt with fresh preparation are required. |
| + |
| +The worker preserves the resume protocol's ResumeRequested guard phase while checker execution is an internal substate. It never substitutes Executing or publishes success before the checker. It retains exclusive ownership and independently rechecks main identity/length and sidecar absence after checked checker process-tree death and output capture, before performing subsequent approved post-checker integrity/checkpoint/restore and serving handoff. Any failure keeps admission denied. A post-checker checkpoint may recreate sidecars only after the immutable checker interval ends; its result is separate final-resume evidence. |
| + |
| +## Child startup and read-only baseline |
| + |
| +Use exactly the approved maintenance fd layout:0 exact private stdin,1 private output pipe,2 /dev/null,3 bounded context pipe,4 retained read-only executable. Execute /proc/self/fd/4 with no pathname fallback. The worker retains its own artifact descriptor. Read fd3 under cancellation/deadline, require EOF, validate version/mode and fixed shape, and CHECK close(fd3). Before any DB pin/open, require fd4 O_RDONLY regular, open /proc/self/exe read-only, compare device/inode with fd4, and stream SHA256 from THAT /proc/self/exe descriptor against artifact_digest. Check close of the self-executable descriptor and fd4. Explicit fstat(fd3) and fstat(fd4) must now return EBADF; no intervening open may reuse them before this check. Mode/identity/hash/close failure refuses before DB access. This is the explicit checker amendment to Home's inherited-descriptor baseline; it does not relax the mutable operator connector. |
| + |
| +After that verification, read/validate exact private stdin, input hash, expected-state provenance/digest, fixed checker role/origin and execution deadline under the approved snapshot. Acquire Home's read-only pinned main, verify one-link identity/path/length and absent sidecars, and prehash through that same pin. Only then freeze the baseline and open the exact checker DSN mode=ro&immutable=1/defaultVFS. The driver must introduce exactly one new O_RDONLY main descriptor matching the pin, with no lost/reused baseline descriptor or unrelated writable/temp/main descriptor. The retained pin belongs in the baseline and cannot satisfy the delta. All existing approved per-query/close/sidecar/deadline checks remain. No checker mutations, global hooks, migration repair, checkpoint or mode=rw fallback exist. |
| + |
| +The worker's namespace and lease guarantees are prerequisites, not properties inferred from immutable=1. The Home adapter's path/main byte observations cannot replace proof that other writers are excluded. After SQL closes successfully, posthash the same pinned descriptor and match prehash plus worker hash/length; verify absence and checked pin close before emitting success. Private result carries the exact existing checker bindings and evidence_sha256/main length/hash. Worker accepts only its own captured child pipe and matching live prepared object; arbitrary result ciphertext or successful old output is not completion provenance. |
| + |
| +## Acceptance before wiring |
| + |
| +Schema/decoder cases include both versions, null/unknown/duplicate/case alias fields, uint64 above2^53 through maximum, overflow/negative/fraction/exponent forms, exact size bounds, malformed IDs/digests/timestamps, and changed binding fields. Freeze canonical digest vectors with maximum integers and escaping; every single binding change alters/refuses evidence. Missing v2 checker context refuses; v1 mutable input still uses only mode=rw. |
| + |
| +Actual subprocess tests must execute the pinned inode after staged pathname replacement; validate fd4 O_RDONLY and /proc/self/exe equality/hash; check fd3/fd4 EBADF before baseline; reject close failure, leaked/reused descriptors, wrong image and cancellation. Test a clean checkpointed WAL-mode main with no sidecars in the actual readonly namespace and pinned modernc immutable connector. Hot or zero-byte WAL/SHM/journal, wrong identity/hash/length, extra FD, incomplete cleanup, absent namespace, deadline or lease loss and predecessor executor uncertainty refuse. The worker proof constructors must not be callable from request data; replayed preparation and mismatched operation/attempt/input/env/artifact fail even with otherwise well-formed context. |
| + |
| +Real integrated worker tests must show the same retained lease/executor through preparation, checker and post-checker transition, blocked stdin/context/output cancellation, no public byte/hash leakage and no resume on checker failure. Local fixtures prove mechanics only. Full native and cross CI, actual Fable whole-code review and native Linux runtime evidence remain mandatory before mounting. Neither this design nor its schema enables a checker command or maintenance worker. |
diff --git a/implementation/maintenance-checker-launch.schema.json b/implementation/maintenance-checker-launch.schema.json| new file mode 100644 |
| index 000000000..6d6e38127 |
| --- /dev/null |
| +++ b/implementation/maintenance-checker-launch.schema.json |
| @@ -0,0 +1,154 @@ |
| +{ |
| + "$schema": "https://json-schema.org/draft/2020-12/schema", |
| + "$id": "urn:carlos:maintenance:checker-launch:v2", |
| + "title": "Private checker launch context v2", |
| + "description": "Shape only. Exact UTF-8 byte bounds, lexical integer canonicality, duplicates, shared route validators, canonical IDs/timestamps and trusted construction are normative in maintenance-checker-launch.md. This schema grants no authority.", |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "execution_kind", |
| + "account", |
| + "app", |
| + "host", |
| + "operation_id", |
| + "request_id", |
| + "attempt_id", |
| + "request_digest", |
| + "record_identity", |
| + "artifact_digest", |
| + "environment_envelope_digest", |
| + "input_sha256", |
| + "expected_state_sha256", |
| + "guard_generation", |
| + "database_path", |
| + "database_device", |
| + "database_inode", |
| + "execution_deadline", |
| + "closed_checkpoint" |
| + ], |
| + "properties": { |
| + "version": { |
| + "const": 2 |
| + }, |
| + "execution_kind": { |
| + "const": "compatibility-check" |
| + }, |
| + "account": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 253 |
| + }, |
| + "app": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 253 |
| + }, |
| + "host": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 253 |
| + }, |
| + "operation_id": { |
| + "type": "string", |
| + "pattern": "^[A-Za-z0-9_-]{43}$" |
| + }, |
| + "request_id": { |
| + "type": "string", |
| + "pattern": "^[A-Za-z0-9_-]{43}$" |
| + }, |
| + "attempt_id": { |
| + "type": "string", |
| + "pattern": "^[A-Za-z0-9_-]{43}$" |
| + }, |
| + "request_digest": { |
| + "type": "string", |
| + "pattern": "^[a-f0-9]{64}$" |
| + }, |
| + "record_identity": { |
| + "type": "string", |
| + "pattern": "^[a-f0-9]{64}$" |
| + }, |
| + "artifact_digest": { |
| + "type": "string", |
| + "pattern": "^[a-f0-9]{64}$" |
| + }, |
| + "environment_envelope_digest": { |
| + "type": "string", |
| + "pattern": "^[a-f0-9]{64}$" |
| + }, |
| + "input_sha256": { |
| + "type": "string", |
| + "pattern": "^[a-f0-9]{64}$" |
| + }, |
| + "expected_state_sha256": { |
| + "type": "string", |
| + "pattern": "^[a-f0-9]{64}$" |
| + }, |
| + "guard_generation": { |
| + "type": "integer", |
| + "minimum": 1, |
| + "maximum": 9223372036854775807 |
| + }, |
| + "database_path": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 1024, |
| + "pattern": "^/[^\\u0000]*$" |
| + }, |
| + "database_device": { |
| + "type": "integer", |
| + "minimum": 1, |
| + "maximum": 18446744073709551615 |
| + }, |
| + "database_inode": { |
| + "type": "integer", |
| + "minimum": 1, |
| + "maximum": 18446744073709551615 |
| + }, |
| + "execution_deadline": { |
| + "type": "string", |
| + "format": "date-time", |
| + "maxLength": 30 |
| + }, |
| + "closed_checkpoint": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "preparation_id", |
| + "completed_at", |
| + "main_length", |
| + "main_sha256", |
| + "evidence_sha256" |
| + ], |
| + "properties": { |
| + "version": { |
| + "const": 1 |
| + }, |
| + "preparation_id": { |
| + "type": "string", |
| + "pattern": "^[A-Za-z0-9_-]{43}$" |
| + }, |
| + "completed_at": { |
| + "type": "string", |
| + "format": "date-time", |
| + "maxLength": 30 |
| + }, |
| + "main_length": { |
| + "type": "integer", |
| + "minimum": 1, |
| + "maximum": 268435456 |
| + }, |
| + "main_sha256": { |
| + "type": "string", |
| + "pattern": "^[a-f0-9]{64}$" |
| + }, |
| + "evidence_sha256": { |
| + "type": "string", |
| + "pattern": "^[a-f0-9]{64}$" |
| + } |
| + } |
| + } |
| + } |
| +} |