Revise maintenance protocol after independent review
2 files changed,
+1523
−0
implementation/maintenance-protocol/proposal.json+345 −0implementation/maintenance-protocol/schemas.json+1178 −0
CI passed — run details
diff --git a/implementation/maintenance-protocol/proposal.json b/implementation/maintenance-protocol/proposal.json| new file mode 100644 |
| index 000000000..8925b9e79 |
| --- /dev/null |
| +++ b/implementation/maintenance-protocol/proposal.json |
| @@ -0,0 +1,345 @@ |
| +{ |
| + "status": "PROPOSED interface choices, not authority approval; no implementation or deployment", |
| + "sources": [ |
| + { |
| + "name": "approved-maintenance", |
| + "revision": "6a81dacfe4949520efce8fb55dec1dbec8e43bfd", |
| + "path": "/home/paulca/amadan.net/carlos/platform/.claude/worktrees/overnight-maintenance-design/implementation/maintenance-exec.md", |
| + "sections": [ |
| + "CLI/API surface", |
| + "Private transport", |
| + "Environment pinning", |
| + "Durable guard and lifecycle", |
| + "Resume" |
| + ] |
| + }, |
| + { |
| + "name": "launch-delta-unreviewed", |
| + "revision": "caf47fb", |
| + "path": "/home/paulca/amadan.net/carlos/platform/.claude/worktrees/overnight-maintenance-launch/implementation/maintenance-launch.md", |
| + "note": "4605a54 base approved; caf47 delta has no approval verdict" |
| + }, |
| + { |
| + "name": "platform-foundations", |
| + "revision": "11a303996d5503d29ede77598243ddc66f735279", |
| + "root": ".", |
| + "paths": [ |
| + "internal/maintenance/envelope.go", |
| + "internal/maintenance/input.go", |
| + "internal/maintenance/result.go", |
| + "internal/maintenanceguard/types.go", |
| + "internal/maintenanceguard/store.go", |
| + "internal/privatefile/privatefile.go", |
| + "internal/provision/maintenance.go", |
| + "internal/console/authcli.go:411", |
| + "internal/console/stepup.go:249", |
| + "internal/console/stepup.go:396", |
| + "internal/console/apiconfig.go:251", |
| + "internal/edge/storedoor.go:100", |
| + "internal/host/agent.go:2772" |
| + ], |
| + "note": "This proposal branch is based on this merged revision; these exact existing primitive sources are included in its review archive." |
| + }, |
| + { |
| + "name": "Home-prepared-protocol", |
| + "revision": "7e282e2a363b9cc90cd74dd97809e6613a699af8", |
| + "root": "/home/paulca/amadan.net/oficina/home/.claude/worktrees/overnight-provisioner-prepare", |
| + "paths": [ |
| + "internal/provisioner/prepare_linux.go", |
| + "internal/provisioner/installations_linux.go", |
| + "internal/operator/protocol.go", |
| + "internal/operator/open_linux.go", |
| + "cmd/oficina/main_linux.go" |
| + ] |
| + } |
| + ], |
| + "approved_requirements": [ |
| + "Exact account/app/primary-host scope; no first-visitor authority", |
| + "Private JSON stdin <=65536; argv 1..32 and aggregate <=8192; result <=65536; default timeout300s hard600s", |
| + "Existing CME1 P256/HKDF-SHA256/AES-GCM binding and CMI1 exact-byte framing; no new crypto", |
| + "Input encrypts to deployment secrets public key; result to durable per-request CLI key; console never gets private decrypting key", |
| + "Fresh browser current-owner review, immutable preflight/env/artifact/record/recipient pins; bearer is not fresh consent", |
| + "No execution until matching durable guard+record projection, lifecycle, checked lease/stop/DB/containment prerequisites", |
| + "Private payload expiry24h; immutable nonsecret receipt retained; lost/unpublished output is unavailable or ambiguous", |
| + "Explicit retry and resume; no implicit command rerun or fence clearing" |
| + ], |
| + "new_wire_choices": [ |
| + "All endpoint paths and schema definitions", |
| + "Client-generated separate random request_id and attempt_id before encryption", |
| + "Target GET and two-step server preflight then browser approval", |
| + "Retry creates a linked new PLATFORM request/attempt, with unchanged APP operation bytes; no new app-level authorization", |
| + "Closed pending-local JSON format and explicit file flags", |
| + "Specific resume proposal object and status vocabulary", |
| + "Environment plaintext closed map-entry schema inside existing DirectionEnvironment envelope; this plaintext shape is proposed, not an existing codec", |
| + "Proposed worker publication interface, intentionally unavailable pending authenticated enrollment design", |
| + "Server-derived stable lifecycle OperationID and constant-depth authoritative lineage-head CAS, separate from platform request/attempt IDs and private app operation ID" |
| + ], |
| + "endpoints": [ |
| + { |
| + "method": "GET", |
| + "path": "/api/cli/apps/{account}/{app}/instances/{host}/maintenance/target", |
| + "request_schema": null, |
| + "response_schema": "target", |
| + "authorization": "human-owner-cli", |
| + "success_http_status": [ |
| + 200 |
| + ] |
| + }, |
| + { |
| + "method": "POST", |
| + "path": "/api/cli/apps/{account}/{app}/instances/{host}/maintenance/requests", |
| + "request_schema": "prepare", |
| + "response_schema": "prepared", |
| + "authorization": "human-owner-cli", |
| + "success_http_status": [ |
| + 201, |
| + 200 |
| + ] |
| + }, |
| + { |
| + "method": "GET", |
| + "path": "/api/cli/apps/{account}/{app}/instances/{host}/maintenance/requests/{request_id}", |
| + "request_schema": null, |
| + "response_schema": "status", |
| + "authorization": "human-owner-cli", |
| + "success_http_status": [ |
| + 200 |
| + ] |
| + }, |
| + { |
| + "method": "GET", |
| + "path": "/api/cli/apps/{account}/{app}/instances/{host}/maintenance/requests/{request_id}/result", |
| + "request_schema": null, |
| + "response_schema": "result", |
| + "authorization": "human-owner-cli", |
| + "success_http_status": [ |
| + 200 |
| + ] |
| + }, |
| + { |
| + "method": "POST", |
| + "path": "/api/cli/apps/{account}/{app}/instances/{host}/maintenance/requests/{request_id}/retry", |
| + "request_schema": "retry", |
| + "response_schema": "prepared", |
| + "authorization": "human-owner-cli", |
| + "success_http_status": [ |
| + 201, |
| + 200 |
| + ] |
| + }, |
| + { |
| + "method": "POST", |
| + "path": "/api/cli/apps/{account}/{app}/instances/{host}/maintenance/requests/{request_id}/resume", |
| + "request_schema": "resume", |
| + "response_schema": "resume_proposed", |
| + "authorization": "human-owner-cli", |
| + "success_http_status": [ |
| + 202, |
| + 200 |
| + ] |
| + }, |
| + { |
| + "method": "GET", |
| + "path": "/a/{sqid}/apps/{app}/instances/{host}/maintenance/{request_id}/review", |
| + "request_schema": null, |
| + "response_schema": "HTML immutable nonsecret review", |
| + "authorization": "browser-current-owner", |
| + "success_http_status": [ |
| + 200 |
| + ] |
| + }, |
| + { |
| + "method": "POST", |
| + "path": "/a/{sqid}/apps/{app}/instances/{host}/maintenance/{request_id}/approve", |
| + "request_schema": "approval_form", |
| + "response_schema": "303 Location: /a/{sqid}/apps/{app}/instances/{host}/maintenance/{request_id}/review (same new request; review page renders current status)", |
| + "authorization": "session-CSRF-current-owner-fresh-auth", |
| + "success_http_status": [ |
| + 303 |
| + ] |
| + } |
| + ], |
| + "wire_rules": { |
| + "schemas": "schemas.json; every object additionalProperties=false, all listed fields required; nullable only where declared", |
| + "json": "UTF-8; reject duplicate keys, case aliases, trailing data, invalid/noncanonical encodings, out-of-range/noninteger numbers; parse int64/uint64 without float64. Request JSON schema is not a runtime parser implementation.", |
| + "scope": "Canonical configured HTTPS console origin, exact controlname account/app/host; path selectors and every body selector agree. No alias, wildcard, path route, selector default, environment override or executable path.", |
| + "bytes": "Envelope strings are unchanged canonical StdBase64 CME1; envelope digest is SHA256 of exact ASCII envelope bytes, declared new wire convention. Public hashes never cover argv/stdin/environment plaintext. Recipient is SHA256 of raw P256 public point per existing RecipientFingerprint.", |
| + "caps": { |
| + "prepare_body": 131072, |
| + "status_body": 16384, |
| + "result_body": 98304, |
| + "approval_form_body": 4096, |
| + "worker_environment_publication_body": 2097152, |
| + "private_pending_body": 196608 |
| + }, |
| + "headers": { |
| + "all": "Cache-Control:no-store,no-transform; X-Content-Type-Options:nosniff; exact JSON Content-Type where JSON", |
| + "review": "Referrer-Policy:no-referrer; same-origin form action; no private payload or result data in HTML", |
| + "result": "No caller-specified redirect or ciphertext URL; response uses authenticated console connection" |
| + }, |
| + "errors_http": { |
| + "400": "invalid-request", |
| + "401": "not-signed-in", |
| + "403": "service credentials refused before membership-dependent details; fixed generic body", |
| + "404": "not-found for inaccessible/missing account/app/host/request", |
| + "409": "conflict or result-unavailable", |
| + "412": "precondition-failed", |
| + "410": "payload-expired", |
| + "429": "rate-limited; bounded Retry-After", |
| + "503": "unsupported or unavailable; no worker provenance means unavailable, never assumed success" |
| + }, |
| + "approval_forms": "application/x-www-form-urlencoded; exact fields in approval_form, one value each; reject unknown/duplicate fields. csrf uses existing console field and is never copied into durable approval metadata. Browser Origin/CSRF checks must match normal console/router contract; any stronger Origin rule needs explicit integration tests.", |
| + "metadata_digest": "New proposal: deterministic Go encoding/json serialization of the declared version-1 typed object in schemas.json property order, no indentation or appended newline, normal Go HTML escaping; sorted unique required-contract arrays and sorted unique environment entry names. Compare exact stored bytes for reference digests; no float64 intermediates.", |
| + "approval_endpoint": "The single review/approve path uses the NEW request_id for operation, retry, or resume. Server immutable request kind determines the required operation or compatibility/resume checks. Predecessor is metadata, never substituted into the approval path. Missing/mismatched kind/pins refuses.", |
| + "browser_scope": "Use existing accountApp sqid routing and resolve sqid to exact account before comparing stored scope; sqid is not an account slug. Approval redirects 303 to the same new request review URL /a/{sqid}/apps/{app}/instances/{host}/maintenance/{request_id}/review. Owner and fresh-auth gate order is unchanged.", |
| + "predecessor_agreement": "Initial POST /requests requires predecessor=null. Retry and resume require non-null predecessor; predecessor.id equals path request_id and its digest equals that exact predecessor request reference (not an outcome ID). Server resolves that request to its immutable authoritative outcome and current lineage head; every body selector must agree. Missing outcome, stale predecessor or mismatch refuses. New body request_id is distinct from predecessor/path request_id.", |
| + "result_semantics": "GET /result resolves status.outcome from authoritative current request metadata. Pending resume, including compatibility-checked/resume-ready, returns409 result-unavailable; it never returns the intermediate checker receipt as resumed. Before a final outcome status.result_state=pending and result_envelope_digest=null; historical compatibility/evidence refs may be non-null. For final resume outcome, return that final outcome/ref and exact retained checker envelope whose digest is copied unchanged from compatibility_outcome; server verifies both immutable receipts and linkage under the same request/attempt/scope/recipient. No checker output yet or lost output yields409 unavailable; expired private payload yields410. Final failed/ambiguous resume may expose an existing validated checker result but state remains failed/ambiguous and CLI must not report resumed. Operation result semantics remain exact command ciphertext under its final outcome. Result envelope alone never establishes outcome or admission." |
| + }, |
| + "cli": [ |
| + { |
| + "command": "carlos maintenance prepare --account A --app APP --host H --input ABS_FILE --pending ABS_FILE --label LABEL --timeout 300 -- ARG...", |
| + "behavior": "Resolve authenticated target, read private 0600 regular bounded exact JSON, generate IDs/key, SealInput, durably save pending, then upload. Explicit shell-free argv only; worker supplies executable and DB path. No private argv in public status/logging." |
| + }, |
| + { |
| + "command": "carlos maintenance status --account A --app APP --host H --request ID", |
| + "behavior": "Read-only current-owner status. Optional bounded watch polling; Ctrl-C stops polling, not operation." |
| + }, |
| + { |
| + "command": "carlos maintenance result --account A --app APP --host H --request ID --pending ABS_FILE --output ABS_FILE", |
| + "behavior": "Current-owner fetch of authenticated receipt plus exact ciphertext, local binding verification and OpenResult, exclusive durable private output. Never stdout; no arbitrary ciphertext input flag." |
| + }, |
| + { |
| + "command": "carlos maintenance retry --account A --app APP --host H --request OLD_ID --pending OLD_FILE --new-pending ABS_FILE", |
| + "behavior": "Fetch authenticated immutable predecessor outcome; verify exact retained signed input bytes by private hash; generate new platform IDs/key/envelope under new binding, save new pending before POST. New owner approval mandatory. Does not generate new Home operation or signature." |
| + }, |
| + { |
| + "command": "carlos maintenance resume --account A --app APP --host H --request OLD_ID --artifact VERSION --required-contract NAME... --check-input ABS_FILE --pending ABS_FILE --timeout 300 -- CHECK_ARG...", |
| + "behavior": "Allocate new transport request/attempt and recipient key; SealInput preserves private non-serving checker argv and exact JSON stdin; save pending before resume POST. Fixed executable is the pinned serving release. Same browser owner gate approves exact checker ciphertext, artifact/environment/record and required contracts. No CLI approval. Checker command selection must match the reviewed app compatibility contract; arbitrary migration/server argv cannot be relabeled as a checker." |
| + } |
| + ], |
| + "ordering": [ |
| + "Target GET is read-only, human-current-owner authorized. Public deployment encryption key comes from authenticated console Config.SecretsPub source, not silent terminal env override. Pin target artifact signed manifest and record identity/revision; target response is not preflight or approval.", |
| + "Client creates independent cryptographic32-byte request/attempt IDs and per-request result key. Read exact input privately; SealInput with binding Version1/DirectionInput and deployment recipient. App operation IDs remain only inside private signed stdin.", |
| + "Build exact prepare JSON once. Store private key, IDs, fingerprint, exact body, input path/private hash in exclusive0600 pending file outside repositories. Check file sync/close and parent sync before POST. Interrupted file is preserved; a read is not durability proof. Future pending loader must validate all bindings and execute a checked durability barrier before resend.", |
| + "Authenticated POST creates immutable server request bytes and server metadata once. Same request ID with identical body returns same request/digest/expiry. Any differing bytes/scope/recipient/attempt gives409. Lost response resends identical body; do not re-encrypt. Client-supplied predecessor is only a reference and never execution authorization.", |
| + "Coordinator assigns exactly one authenticated enrolled box to immutable request/attempt. Preflight reads existing DB/record/artifact/config without stopping serving, restoring, seeding, or EnsureAdminToken. Worker returns encrypted DirectionEnvironment snapshot plus authoritative preflight facts; console cannot decrypt the map.", |
| + "Immutable request digest is SHA256 of the deterministic server encoding of the validated prepare object; it includes randomized ciphertext/digests but no raw stdin hash. Exact upload bytes remain the idempotency comparison. Each preflight/publication/approval reference digest binds its exact stored immutable bytes. Server builds immutable approval candidate digest from request reference, assignment, preflight reference, exact pins and result recipient; browser displays these nonsecret facts and fixed warning that inputs remain private. Never render arbitrary argv or plaintext hashes. The candidate MUST also bind server-derived lifecycle_operation_id, exact expected lineage-head revision (explicit absent sentinel for initial operation), and exact predecessor request and resolved outcome references (explicit null for initial operation). These typed fields participate in candidate_digest, not merely page display. candidate_revision identifies this immutable candidate, not an independently refreshable head. Step-up replay rereads those exact expectations and CAS cannot substitute a newer head.", |
| + "POST approval: session, CSRF using existing form field csrf (never persisted; step-up injects it from current cookie), current owner byte-identical404, requireFreshAuth last. Existing stepup can replay the session-bound single-use held POST through router; exact candidate digest/revision survives and every owner/pin check reruns. Missing pins never mean approve latest. Browser intent is the original explicit POST, not CLI token auth_time.", |
| + "Worker rechecks current owner authorization and immutable candidate, exact record revision and private merged environment equality, artifact signature/hash, guard/projection/lifecycle and lease/containment before durable executing state and one spawn. The approved private snapshot is actual command environment. Any changed source or pin refuses; no refresh of approval in place.", |
| + "Capture bounded stdout; independently verify Wait, process-tree cleanup, lease, DB, checkpoint/restore and semantic receipts. SealResult is only encryption. Publication and terminal outcome require the authenticated-worker path below; missing publication never becomes invented empty success.", |
| + "CLI fetch verifies authenticated server outcome and stored reference digest, assigned worker provenance established server-side, request/attempt/scope, approved record/artifact/environment/input ciphertext hashes, recipient and terminal outcome, then decrypts. Save output durably before optional explicit key cleanup; failed output retains pending key.", |
| + "Home completion adapter additionally checks existing signed app receipt against retained installation/role/generation/authorization stream and exact prepared operation. It finalizes encrypted result record and independent floor before accepted-head CAS. A local parseable file, CME1 success, or browser approval alone cannot advance Home accepted state." |
| + ], |
| + "worker_provenance": { |
| + "status": "UNRESOLVED DESIGN DEPENDENCY; no endpoint may publish accepted execution until this is implemented and reviewed", |
| + "existing_options": [ |
| + { |
| + "source": "internal/edge/storedoor.go:100 and TunnelHub.VerifyDynamic", |
| + "facts": "Dynamic fleet name plus bearer validated against current fleet box table; fleet account mapping checked. Store door excludes control/maintenance and instance record writes. A narrowly scoped typed endpoint could reuse verified fleet identity, but token/enrollment is not universal and cached revocation semantics require explicit review." |
| + }, |
| + { |
| + "source": "internal/host/agent.go:2772 openDeployStoreEnv", |
| + "facts": "Shared/root boxes currently prefer deployment bucket/directory credentials. These are broad store access, not an authenticated per-box worker result channel. They cannot establish exact worker origin by themselves." |
| + } |
| + ], |
| + "minimum_new_interface": { |
| + "authentication": "AuthenticateWorker(request) must derive current deployment_origin, box_id and enrollment_generation from real supported enrollment, never body strings. Authority to create enrollment and revocation propagation remain unresolved. Do not mint universal credentials or reuse human/service CLI token.", |
| + "assignment": "Read immutable server assignment bound to deployment/box/account/app/host/request/attempt, current placement and current owner approval. Wrong box, revoked enrollment, stale generation or lost lifecycle cannot publish.", |
| + "paths": [ |
| + "POST /internal/maintenance/assignments/{assignment_id}/preflight", |
| + "POST /internal/maintenance/assignments/{assignment_id}/outcome" |
| + ], |
| + "preflight_body": "schemas.json preflight_publication; principal/assignment supplied by server context, body copies must agree. Proposed environment_private plaintext is bounded to existing MaxEnvelopePlaintext=1048576, with unique variable names; empty values allowed. This plaintext schema needs review and adds no new encryption format.", |
| + "outcome_body": "schemas.json outcome_publication; publication_id is random immutable receipt identity, reused byte-exact after lost response. All reference digests rederived and checked; no generic bucket-key or result-upload CLI endpoint. Missing result envelope cannot assert success; empty application results depend on reviewed app contract, Home rejects empty. Checked worker coordinator, not an HTTP boolean, establishes execution_evidence.", |
| + "publication": "Store private encrypted payload create-only, finalize immutable receipt with its digest and exact approved refs, then CAS current attempt terminal pointer only for assigned generation. Crash before pointer leaves orphan ciphertext/receipt, no accepted outcome. One publisher; identical retry idempotent, changed receipt conflict. Compatibility-check success in a resume request is intermediate: CAS only the historical compatibility_outcome reference, never the final status.outcome pointer. The resume_checker_transport publication and result_semantics rules specify the subsequent final receipt and unchanged ciphertext linkage.", |
| + "readback": "Console result handler resolves current attempt and immutable receipt through trusted internal store and verifies receipt/payload digests plus authentication provenance. Human CLI may read ciphertext but cannot publish outcome. No signed offline worker receipt format is invented here." |
| + }, |
| + "opaque_references": "worker_assignment, worker_evidence, execution_evidence and resume_ready_evidence are opaque server-internal immutable references. Their storage encoding and validator are dependencies of the future enrolled-worker coordinator, not wire objects defined here and not assertions of an existing assignment store. Server computes/checks ID and SHA256 over exact retained internal bytes. CLI compares authenticated references for binding only; it cannot independently validate their contents or present them as credentials. No worker token, signature format, endpoint authority or offline proof is introduced." |
| + }, |
| + "retry_and_cancellation": { |
| + "upload_disconnect": "Unknown acceptance: recover same pending and repeat exact original prepare. No operation starts without browser approval.", |
| + "preflight_timeout": "Remains unavailable/refused without app mutation; no signature, Home expiry, input expiry or approval expiry extension.", |
| + "execution_disconnect": "HTTP client disconnect does not cancel durable worker ownership. Worker timeout/lease loss invokes checked stop and publishes failed/ambiguous when possible; never automatically retries.", |
| + "retry": "New platform request linked to immutable predecessor receipt. Same exact app stdin/argv required: client validates retained private hash; worker independently compares decrypted predecessor input with new input while private predecessor payload exists. Expired/missing predecessor payload refuses retry; operator may prepare a separately reviewed recovery request preserving Home idempotency, never rewrite old authority. No fresh Home signature is generated by carlos.", |
| + "concurrency": "One exact active request per retained lifecycle ownership. Concurrent retries may prepare but only approved matching claim can execute; no last-writer-wins head. Existing incomplete lifecycle claim must be recovered explicitly rather than stolen.", |
| + "resume": "Original operation may have succeeded, failed, or become ambiguous; resume eligibility requires checked stop/containment and a NEW independently successful app-specific non-serving compatibility-check command from the pinned serving release, under the retained guard/lifecycle/lease. Private checker argv/stdin use unchanged CMI1+DirectionInput; stdout uses CaptureResult+SealResult to the newly durable per-request CLI key. Approval pins exact checker ciphertext, artifact/manifest, environment snapshot, record identity/revision, predecessor and required contracts. Validate checker exit plus app-specific compatibility result, then integrity and final checkpoint/restore evidence. Publish immutable resume-ready evidence while guard remains in the existing ResumeRequested phase; do not invent a new guard enum. Only then start exact serving artifact and hand lease to replication, verify health/version, and CAS matching resumed guard+projection. Failure keeps serving admission closed. Migration success is not compatibility proof; checker cannot rerun the original mutation.", |
| + "expiry": "Server immutable payload expiry24h from first accepted upload; idempotent resend never extends it. Proposed preflight review expiry5min; expiry requires fresh preflight/new candidate and explicit review, never silently renew old consent. Execution must begin before candidate expiry; child and operation deadlines independently bounded." |
| + }, |
| + "Home_integration": { |
| + "actual": "7e native prepare emits exact signed JSON on inherited anonymous write pipe only, after local journal+floor durability, with public state local-request. It is not a regular-file writer or network submit command.", |
| + "needed_adapter": "A future native submission adapter supervises that pipe, captures <=65536 exact bytes with cancellation, writes exclusive private input file, then invokes shell-free carlos prepare or its library. No terminal redirect of secrets, no LLM handling. On capture cancellation replay exact saved Home request.", |
| + "actions_ready": [ |
| + "directory.initialize", |
| + "credential.owner-seed", |
| + "directory.seed-team", |
| + "directory.provision-consumer" |
| + ], |
| + "actions_refused_until_evidence": [ |
| + "directory.verify-consumer", |
| + "directory.activate-consumer" |
| + ], |
| + "launch": "caf47 unreviewed context owns fd3 and verified artifact fd4; platform hashes exact stdin and binds request/record/artifact/env plus DB identity/deadline. Home verifies private signed stdin, role/origin/action from approved pinned environment, exact opened SQLite file and self executable. Home cannot independently compare worker-owned expected environment/record digests against invented duplicated flags.", |
| + "authority": "Home operator signing/floor, platform browser owner approval, worker execution provenance and application result verification are separate checks; none substitutes for another.", |
| + "completion_classification": "Only authenticated execution_kind=operation outcomes for the exact prepared Home mutation may advance its accepted authorization head. Compatibility-check and resume receipts are separate proof/history and cannot be consumed as successful mutation results." |
| + }, |
| + "dependencies_before_mount": [ |
| + "Review this new wire proposal; caf47 launch delta remains unapproved", |
| + "Concrete shared/root and fleet worker authentication/enrollment with revocation and exact assignment publication path", |
| + "Immutable request/approval/attempt storage and CAS terminal state machine; receipt validators and durable private-payload expiry", |
| + "Complete all ordinary writer/lifecycle/config/adoption/always-awake admission fences; record serialization alone is insufficient", |
| + "Actual worker preflight environment capture/compare and signed artifact, existing DB, lease/cgroup/checkpoint/restore/semantic-receipt executor", |
| + "Checked resume and durable supported-contract floors across startup/adoption/rollback", |
| + "Native pending-file format/loader checked resume-durability barrier and Home submission/completion adapter; supported non-Linux custody still absent", |
| + "Independent whole-code review, native/cross/CI and isolated deployed rehearsals before any real DB operator invocation", |
| + "Actual app-specific non-serving compatibility checker in pinned serving artifact, private checker-result validator, and resume-ready evidence publication; prior mutation success cannot satisfy this" |
| + ], |
| + "acceptance_cases": [ |
| + "Changed request/attempt/scope/direction/recipient rejects existing envelope; existing golden vectors unchanged", |
| + "65536 input/result exact boundary succeeds; 65537 refuses; JSON/metadata/base64 expansion is accounted separately", |
| + "Crash or sync failure before pending durability makes zero upload calls; reopen alone cannot permit upload; same-ID changed bytes conflict", |
| + "Lost upload reply plus repeated exact request returns same IDs/digest/expiry, not duplicate command", |
| + "Every service role refused; nonowner byte-identical404; stale browser with validCSRF still steps up; missingOrigin/crosssite POST refuses; ownership changed during stepup refuses", |
| + "Changed approval digest/revision, record ETag, artifact hash, box overlay or recipient prevents spawn; no EnsureAdminToken or restore side effect in preflight", |
| + "Worker publication by CLI bearer, wrong/revoked/unassigned box or naked result ciphertext never produces accepted outcome; orphan payload/receipt does not advance terminal pointer", |
| + "Cancelled stdout capture, child never readsstdin, descendant holds pipe, overflow, nonzero exit, lost lease all terminate/check containment and preserve ambiguity correctly", |
| + "Result wrong authenticated receipt/ref/ciphertext digest/binding refuses before output; exclusive output existing/symlink/failedsync never overwrites or deletes pending key", |
| + "Retry binds immutable predecessor, unchanged app bytes, new explicit approval, same app operation; no counter/signature regeneration, no expiry extension", |
| + "Resume cannot run mutation; every partial guard/projection/startup/replication write keeps admission closed until both resumed records and health agree", |
| + "Home actual prepared pipe to private input and byte-exact stdin preserved; received ciphertext without authenticated outcome cannot finalize encrypted result/floor/accepted head", |
| + "Existing record without projection remains compatible; malformed projection protects existing registry state without new serving authority", |
| + "Fresh actual shared-box/fleet enrollment and revocation rehearsal required; static synthetic WorkerPrincipal fixtures are not provenance proof", |
| + "Failed/ambiguous original mutation can reach resume only after independent compatibility checker plus integrity/checkpoint success; successful original migration without new checker cannot resume", |
| + "Checker wrong argv/serving artifact/ciphertext/recipient/environment/record pins prevents launch; checker failure, malformed output, timeout or lease loss prevents resume-ready", |
| + "Retry/resume new request+attempt retain same trusted lifecycle OperationID; forged root, missing head, stale predecessor or simultaneous forked approval cannot replace claim or execute", |
| + "Lineage lookup uses bounded current trusted references, never arbitrary-depth predecessor traversal; crash during head/guard/projection updates stays fenced", |
| + "Maximal 32-argument CMI1 frame73868 bytes plus CME1 overhead97 yields98620 base64 characters, accepted by both input schema fields; old98616 cap rejects it.", |
| + "Browser sqid resolves through accountApp; mismatched stored account refuses before fresh-auth and 303 returns exact review route.", |
| + "Changing lifecycle OperationID, expected head revision or predecessor outcome invalidates candidate_digest and held-POST approval; no latest-head substitution.", |
| + "Retry null predecessor and retry/resume predecessor differing from path reject before draft creation; predecessor digest means request reference, not outcome reference.", |
| + "Resume-ready has no final outcome and GET result returns409; final resumed outcome links immutable checker outcome/evidence and returns unchanged checker envelope. Failed handoff never reports resumed or clears guard." |
| + ], |
| + "lifecycle_lineage": { |
| + "stable_identity": "Server derives lifecycle_operation_id from the retained trusted initial request anchor (proposed equal to initial platform request_id) and existing Guard.OperationID/lifecycle claim. New retry/resume request_id and attempt_id never create a new app claim or replace OperationID before Resumed. Caller has no lifecycle-operation-ID write field.", |
| + "lookup_bound": "At most four bounded trusted objects: current lifecycle claim/head, current guard, root anchor, exact current predecessor receipt. Head/root references are server-maintained CAS data outside app prefix; no recursive traversal of caller-supplied predecessor links. Missing/inconsistent lineage refuses. History remains immutable but is not walked to authorize a request.", |
| + "head_transition": "Preparation can create competing drafts without changing active lineage. Approval compares exact expected head revision and predecessor receipt, then CASes single current head for the new request/attempt, bound to same lifecycle operation and guard generation. A lost CAS or stale forked approval refuses before execution. Crash retains explicit incomplete transition for idempotent recovery; no timeout unlock or replacement claim. Candidate digest binds stable lifecycle_operation_id, exact expected head revision and predecessor request plus server-resolved outcome references; step-up replay cannot replace these with current values.", |
| + "source_boundary": "This lookup/head shape is a proposed wire/integration choice; approved guard.validAdvance already requires unchanged OperationID until Resumed. Atomic cross-object transition and crash recovery still need implementation/review; this is not a claim of cross-object atomicity." |
| + }, |
| + "resume_checker_transport": { |
| + "input": "Exactly maintenance.Input{Arguments:CHECK_ARG,Stdin:private JSON}, EncodeInput/SealInput to approved deployment key with new request/attempt and DirectionInput. Worker supplies executable and database path; no caller environment or fd/path authority. Generic limits remain 32args/8192 aggregate/65536 stdin.", |
| + "output": "Exact <=65536 bytes on dedicated stdout, captured to EOF and encrypted with existing SealResult/DirectionResult to new request recipient. App-specific checker JSON/schema and compatibility validation belong to reviewed serving artifact contract. Platform does not accept an arbitrary compatible=true body or a caller-uploaded checker receipt.", |
| + "authority": "Approved serving artifact must actually expose the intended non-serving checker; no guessed Home command or unmounted dispatcher is treated as implemented. Worker executes/checks it under the same fence/lease and authenticated provenance as mutations; independent integrity/checkpoint follows success.", |
| + "publication": "Checker success publishes immutable compatibility-checked outcome with execution_kind=compatibility-check and private checker result. status.compatibility_outcome retains that reference; status.outcome stays null while final resume is pending. Resume-ready evidence links that exact outcome, immutable approval candidate, final checkpoint/restore and exact serving pins. status.resume_ready_evidence is set only after validated publication, with guard still ResumeRequested. After handoff, exact health/version and matched resumed guard/projection, publish a NEW immutable outcome state=resumed, execution_kind=resume, referencing compatibility_outcome and resume_ready_evidence; CAS status.outcome to it. No receipt is overwritten. Final result_envelope_digest is the SAME checker ciphertext digest, encrypted under the same resume request/attempt/key; no second command/output or re-encryption. Historical compatibility receipt stays reachable from final outcome. Failure after checker success may publish final failed/ambiguous resume outcome with existing compatibility/evidence links but never resumed; serving stays fenced.", |
| + "expiry": "Fresh approval and exact checker input expiry are checked independently. No retry/resume extends prior Home authorization, payload expiry, worker lease or operation timeout." |
| + }, |
| + "revision_note": "R2 addresses Fable seven blockers on bae085e; now based on merged11a3039 with exact primitive sources included. Interface-only proposal; no runtime implementation, worker authority or deployment approval.", |
| + "status_and_guard_mapping": { |
| + "authority": "Status is per request workflow metadata, never serving admission authority. Guard phase plus matching projection remains authoritative. Draft retry/resume does not change the predecessor guard. The mapping below distinguishes workflow overlays and guard phases; do not infer absence or clear a guard from draft/refusal/expiry.", |
| + "awaiting-preflight, awaiting-approval": "No new guard transition. Initial request has no fence; retry/resume retains the current predecessor guard and denial.", |
| + "approved": "Owner gesture accepted, before durable requested fence (initial/retry) or ResumeRequested transition (resume); no command may launch from this status. Existing fence remains intact.", |
| + "requested, quiescing, fenced, executing, succeeded, failed, ambiguous": "Operation workflow states map to identical guard phases after matching durable references. Resume checker execution/failure is instead a substate under ResumeRequested: guard must not move backward to Executing/Failed. State alone never authorizes serving.", |
| + "resume-requested": "Guard ResumeRequested after fresh resume approval and required CAS; all checker, validation and serving handoff steps remain denied.", |
| + "resume-ready": "Workflow substate only; guard remains ResumeRequested. Immutable checker outcome and resume-ready evidence exist but no resumed claim.", |
| + "resumed": "Only after exact serving health/version, lease handoff, and both matching guard Resumed and record projection. No premature success pointer.", |
| + "refused, expired": "Pre-execution workflow refusal/expiry creates no fence and clears none. A previous active guard remains unchanged. After fence acceptance approval expiry cannot undo it; command/lease failures map failed/ambiguous for operations or stay ResumeRequested for resume." |
| + } |
| +} |
diff --git a/implementation/maintenance-protocol/schemas.json b/implementation/maintenance-protocol/schemas.json| new file mode 100644 |
| index 000000000..d7b2e62d3 |
| --- /dev/null |
| +++ b/implementation/maintenance-protocol/schemas.json |
| @@ -0,0 +1,1178 @@ |
| +{ |
| + "$schema": "https://json-schema.org/draft/2020-12/schema", |
| + "title": "Proposed maintenance REST and local custody objects; NOT approved or mounted", |
| + "$defs": { |
| + "id": { |
| + "type": "string", |
| + "pattern": "^[A-Za-z0-9_-]{43}$", |
| + "x-validation": "maintenanceguard.ValidID, including canonical last base64 bits" |
| + }, |
| + "digest": { |
| + "type": "string", |
| + "pattern": "^[a-f0-9]{64}$" |
| + }, |
| + "v": { |
| + "const": 1 |
| + }, |
| + "time": { |
| + "type": "string", |
| + "format": "date-time", |
| + "maxLength": 35, |
| + "x-validation": "UTC RFC3339Nano; exact parse; server-generated except immutable client command timeout" |
| + }, |
| + "scope": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "console_origin", |
| + "account", |
| + "app", |
| + "host" |
| + ], |
| + "properties": { |
| + "console_origin": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 253 |
| + }, |
| + "account": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 64 |
| + }, |
| + "app": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 64 |
| + }, |
| + "host": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 253 |
| + } |
| + } |
| + }, |
| + "reference": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "id", |
| + "digest" |
| + ], |
| + "properties": { |
| + "id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "digest": { |
| + "$ref": "#/$defs/digest" |
| + } |
| + } |
| + }, |
| + "recipient": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "public_key", |
| + "fingerprint" |
| + ], |
| + "properties": { |
| + "public_key": { |
| + "type": "string", |
| + "minLength": 88, |
| + "maxLength": 88 |
| + }, |
| + "fingerprint": { |
| + "$ref": "#/$defs/digest" |
| + } |
| + } |
| + }, |
| + "config_source": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "kind", |
| + "revision" |
| + ], |
| + "properties": { |
| + "kind": { |
| + "enum": [ |
| + "default", |
| + "named", |
| + "instance", |
| + "box-overlay" |
| + ] |
| + }, |
| + "revision": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 256 |
| + } |
| + } |
| + }, |
| + "target": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "scope", |
| + "deployment_recipient", |
| + "record_identity", |
| + "record_revision", |
| + "artifact_version", |
| + "artifact_digest", |
| + "manifest_digest", |
| + "platform_version" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "scope": { |
| + "$ref": "#/$defs/scope" |
| + }, |
| + "deployment_recipient": { |
| + "$ref": "#/$defs/recipient" |
| + }, |
| + "record_identity": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "record_revision": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 256 |
| + }, |
| + "artifact_version": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 64 |
| + }, |
| + "artifact_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "manifest_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "platform_version": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 64 |
| + } |
| + } |
| + }, |
| + "prepare": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "request_id", |
| + "attempt_id", |
| + "operation_label", |
| + "target", |
| + "result_recipient", |
| + "input_envelope", |
| + "input_envelope_digest", |
| + "timeout_seconds", |
| + "predecessor" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "request_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "operation_label": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 64 |
| + }, |
| + "target": { |
| + "$ref": "#/$defs/target" |
| + }, |
| + "result_recipient": { |
| + "$ref": "#/$defs/recipient" |
| + }, |
| + "input_envelope": { |
| + "type": "string", |
| + "maxLength": 98620, |
| + "minLength": 1 |
| + }, |
| + "input_envelope_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "timeout_seconds": { |
| + "type": "integer", |
| + "minimum": 1, |
| + "maximum": 600 |
| + }, |
| + "predecessor": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + } |
| + } |
| + }, |
| + "prepared": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "request_id", |
| + "attempt_id", |
| + "request_digest", |
| + "state", |
| + "status_url", |
| + "review_url", |
| + "payload_expires_at", |
| + "lifecycle_operation_id" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "request_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "request_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "state": { |
| + "enum": [ |
| + "awaiting-preflight", |
| + "awaiting-approval" |
| + ] |
| + }, |
| + "status_url": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 1024 |
| + }, |
| + "review_url": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 1024 |
| + }, |
| + "payload_expires_at": { |
| + "$ref": "#/$defs/time" |
| + }, |
| + "lifecycle_operation_id": { |
| + "$ref": "#/$defs/id" |
| + } |
| + } |
| + }, |
| + "preflight": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "request", |
| + "attempt_id", |
| + "worker_assignment", |
| + "scope", |
| + "record_identity", |
| + "record_revision", |
| + "artifact_version", |
| + "artifact_digest", |
| + "manifest_digest", |
| + "input_envelope_digest", |
| + "result_recipient_fingerprint", |
| + "environment_envelope_digest", |
| + "config_sources", |
| + "worker_evidence", |
| + "expires_at", |
| + "lifecycle_operation_id", |
| + "execution_kind" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "request": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "worker_assignment": { |
| + "$ref": "#/$defs/reference", |
| + "description": "Opaque server-internal immutable reference; encoding and authenticated producer validation remain unresolved worker-coordinator dependencies. CLI comparison is not provenance verification." |
| + }, |
| + "scope": { |
| + "$ref": "#/$defs/scope" |
| + }, |
| + "record_identity": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "record_revision": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 256 |
| + }, |
| + "artifact_version": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 64 |
| + }, |
| + "artifact_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "manifest_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "input_envelope_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "result_recipient_fingerprint": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "environment_envelope_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "config_sources": { |
| + "type": "array", |
| + "maxItems": 8, |
| + "items": { |
| + "$ref": "#/$defs/config_source" |
| + } |
| + }, |
| + "worker_evidence": { |
| + "$ref": "#/$defs/reference", |
| + "description": "Opaque server-internal immutable reference; encoding and authenticated producer validation remain unresolved worker-coordinator dependencies. CLI comparison is not provenance verification." |
| + }, |
| + "expires_at": { |
| + "$ref": "#/$defs/time" |
| + }, |
| + "lifecycle_operation_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "execution_kind": { |
| + "enum": [ |
| + "operation", |
| + "compatibility-check" |
| + ] |
| + } |
| + } |
| + }, |
| + "approval_form": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "csrf", |
| + "request_id", |
| + "attempt_id", |
| + "candidate_digest", |
| + "candidate_revision" |
| + ], |
| + "properties": { |
| + "csrf": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 256 |
| + }, |
| + "request_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "candidate_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "candidate_revision": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 512 |
| + } |
| + } |
| + }, |
| + "status": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "scope", |
| + "request", |
| + "attempt_id", |
| + "revision", |
| + "state", |
| + "preflight", |
| + "approval", |
| + "outcome", |
| + "result_state", |
| + "result_envelope_digest", |
| + "payload_expires_at", |
| + "updated_at", |
| + "lifecycle_operation_id", |
| + "compatibility_outcome", |
| + "resume_ready_evidence" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "scope": { |
| + "$ref": "#/$defs/scope" |
| + }, |
| + "request": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "revision": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 512 |
| + }, |
| + "state": { |
| + "enum": [ |
| + "awaiting-preflight", |
| + "awaiting-approval", |
| + "approved", |
| + "requested", |
| + "quiescing", |
| + "fenced", |
| + "executing", |
| + "succeeded", |
| + "failed", |
| + "ambiguous", |
| + "resume-requested", |
| + "resumed", |
| + "refused", |
| + "expired", |
| + "resume-ready" |
| + ] |
| + }, |
| + "preflight": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + }, |
| + "approval": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + }, |
| + "outcome": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + }, |
| + "result_state": { |
| + "enum": [ |
| + "pending", |
| + "available", |
| + "unavailable", |
| + "expired" |
| + ] |
| + }, |
| + "result_envelope_digest": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + }, |
| + "payload_expires_at": { |
| + "$ref": "#/$defs/time" |
| + }, |
| + "updated_at": { |
| + "$ref": "#/$defs/time" |
| + }, |
| + "lifecycle_operation_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "compatibility_outcome": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + }, |
| + "resume_ready_evidence": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + } |
| + }, |
| + "allOf": [ |
| + { |
| + "if": { |
| + "properties": { |
| + "state": { |
| + "const": "resumed" |
| + } |
| + } |
| + }, |
| + "then": { |
| + "properties": { |
| + "outcome": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "compatibility_outcome": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "resume_ready_evidence": { |
| + "$ref": "#/$defs/reference" |
| + } |
| + } |
| + } |
| + }, |
| + { |
| + "if": { |
| + "properties": { |
| + "state": { |
| + "const": "resume-ready" |
| + } |
| + } |
| + }, |
| + "then": { |
| + "properties": { |
| + "outcome": { |
| + "type": "null" |
| + }, |
| + "compatibility_outcome": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "resume_ready_evidence": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "result_state": { |
| + "const": "pending" |
| + }, |
| + "result_envelope_digest": { |
| + "type": "null" |
| + } |
| + } |
| + } |
| + } |
| + ] |
| + }, |
| + "outcome": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "scope", |
| + "request", |
| + "attempt_id", |
| + "worker_assignment", |
| + "preflight", |
| + "approval", |
| + "record_identity", |
| + "record_revision", |
| + "artifact_digest", |
| + "environment_envelope_digest", |
| + "input_envelope_digest", |
| + "recipient_fingerprint", |
| + "guard_generation", |
| + "guard_digest", |
| + "state", |
| + "execution_evidence", |
| + "result_envelope_digest", |
| + "completed_at", |
| + "lifecycle_operation_id", |
| + "execution_kind", |
| + "compatibility_outcome", |
| + "resume_ready_evidence" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "scope": { |
| + "$ref": "#/$defs/scope" |
| + }, |
| + "request": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "worker_assignment": { |
| + "$ref": "#/$defs/reference", |
| + "description": "Opaque server-internal immutable reference; encoding and authenticated producer validation remain unresolved worker-coordinator dependencies. CLI comparison is not provenance verification." |
| + }, |
| + "preflight": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "approval": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "record_identity": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "record_revision": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 256 |
| + }, |
| + "artifact_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "environment_envelope_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "input_envelope_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "recipient_fingerprint": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "guard_generation": { |
| + "type": "integer", |
| + "minimum": 1, |
| + "maximum": 9223372036854775807 |
| + }, |
| + "guard_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "state": { |
| + "enum": [ |
| + "succeeded", |
| + "failed", |
| + "ambiguous", |
| + "resumed", |
| + "compatibility-checked" |
| + ] |
| + }, |
| + "execution_evidence": { |
| + "$ref": "#/$defs/reference", |
| + "description": "Opaque server-internal immutable reference; encoding and authenticated producer validation remain unresolved worker-coordinator dependencies. CLI comparison is not provenance verification." |
| + }, |
| + "result_envelope_digest": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + }, |
| + "completed_at": { |
| + "$ref": "#/$defs/time" |
| + }, |
| + "lifecycle_operation_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "execution_kind": { |
| + "enum": [ |
| + "operation", |
| + "compatibility-check", |
| + "resume" |
| + ] |
| + }, |
| + "compatibility_outcome": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + }, |
| + "resume_ready_evidence": { |
| + "anyOf": [ |
| + { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + } |
| + }, |
| + "allOf": [ |
| + { |
| + "if": { |
| + "properties": { |
| + "state": { |
| + "const": "resumed" |
| + } |
| + } |
| + }, |
| + "then": { |
| + "properties": { |
| + "execution_kind": { |
| + "const": "resume" |
| + }, |
| + "compatibility_outcome": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "resume_ready_evidence": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "result_envelope_digest": { |
| + "$ref": "#/$defs/digest" |
| + } |
| + } |
| + } |
| + }, |
| + { |
| + "if": { |
| + "properties": { |
| + "state": { |
| + "const": "compatibility-checked" |
| + } |
| + } |
| + }, |
| + "then": { |
| + "properties": { |
| + "execution_kind": { |
| + "const": "compatibility-check" |
| + }, |
| + "compatibility_outcome": { |
| + "type": "null" |
| + }, |
| + "resume_ready_evidence": { |
| + "type": "null" |
| + }, |
| + "result_envelope_digest": { |
| + "$ref": "#/$defs/digest" |
| + } |
| + } |
| + } |
| + }, |
| + { |
| + "if": { |
| + "properties": { |
| + "execution_kind": { |
| + "const": "operation" |
| + } |
| + } |
| + }, |
| + "then": { |
| + "properties": { |
| + "compatibility_outcome": { |
| + "type": "null" |
| + }, |
| + "resume_ready_evidence": { |
| + "type": "null" |
| + }, |
| + "state": { |
| + "enum": [ |
| + "succeeded", |
| + "failed", |
| + "ambiguous" |
| + ] |
| + } |
| + } |
| + } |
| + } |
| + ] |
| + }, |
| + "result": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "outcome", |
| + "outcome_reference", |
| + "result_envelope" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "outcome": { |
| + "$ref": "#/$defs/outcome" |
| + }, |
| + "outcome_reference": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "result_envelope": { |
| + "type": "string", |
| + "maxLength": 87512, |
| + "minLength": 1 |
| + } |
| + } |
| + }, |
| + "resume": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "request_id", |
| + "attempt_id", |
| + "predecessor", |
| + "expected_status_revision", |
| + "target", |
| + "required_contracts", |
| + "result_recipient", |
| + "checker_input_envelope", |
| + "checker_input_envelope_digest", |
| + "timeout_seconds" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "request_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "predecessor": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "expected_status_revision": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 512 |
| + }, |
| + "target": { |
| + "$ref": "#/$defs/target" |
| + }, |
| + "required_contracts": { |
| + "type": "array", |
| + "maxItems": 16, |
| + "items": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 64 |
| + } |
| + }, |
| + "result_recipient": { |
| + "$ref": "#/$defs/recipient" |
| + }, |
| + "checker_input_envelope": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 98620 |
| + }, |
| + "checker_input_envelope_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "timeout_seconds": { |
| + "type": "integer", |
| + "minimum": 1, |
| + "maximum": 600 |
| + } |
| + } |
| + }, |
| + "resume_proposed": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "request", |
| + "attempt_id", |
| + "lifecycle_operation_id", |
| + "state", |
| + "review_url", |
| + "status_url" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "request": { |
| + "$ref": "#/$defs/reference" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "lifecycle_operation_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "state": { |
| + "enum": [ |
| + "awaiting-preflight", |
| + "awaiting-approval" |
| + ] |
| + }, |
| + "review_url": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 1024 |
| + }, |
| + "status_url": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 1024 |
| + } |
| + } |
| + }, |
| + "error": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "code" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "code": { |
| + "enum": [ |
| + "invalid-request", |
| + "not-signed-in", |
| + "not-found", |
| + "conflict", |
| + "precondition-failed", |
| + "payload-expired", |
| + "result-unavailable", |
| + "unsupported", |
| + "rate-limited", |
| + "unavailable" |
| + ] |
| + } |
| + } |
| + }, |
| + "pending_local": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "scope", |
| + "request_id", |
| + "attempt_id", |
| + "recipient_private_key", |
| + "recipient_fingerprint", |
| + "private_input_path", |
| + "private_input_sha256", |
| + "exact_request_body_base64", |
| + "request_kind" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "scope": { |
| + "$ref": "#/$defs/scope" |
| + }, |
| + "request_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "recipient_private_key": { |
| + "type": "string", |
| + "minLength": 44, |
| + "maxLength": 44 |
| + }, |
| + "recipient_fingerprint": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "private_input_path": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 4096 |
| + }, |
| + "private_input_sha256": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "exact_request_body_base64": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 174764 |
| + }, |
| + "request_kind": { |
| + "enum": [ |
| + "operation", |
| + "retry", |
| + "resume" |
| + ] |
| + } |
| + } |
| + }, |
| + "environment_private": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "entries", |
| + "box_overlay_identity" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "entries": { |
| + "type": "array", |
| + "maxItems": 512, |
| + "items": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "name", |
| + "value" |
| + ], |
| + "properties": { |
| + "name": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 256 |
| + }, |
| + "value": { |
| + "type": "string", |
| + "maxLength": 65536 |
| + } |
| + } |
| + } |
| + }, |
| + "box_overlay_identity": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 512 |
| + } |
| + } |
| + }, |
| + "preflight_publication": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "publication_id", |
| + "preflight", |
| + "environment_envelope" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "publication_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "preflight": { |
| + "$ref": "#/$defs/preflight" |
| + }, |
| + "environment_envelope": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 1398232 |
| + } |
| + } |
| + }, |
| + "outcome_publication": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "publication_id", |
| + "outcome", |
| + "result_envelope" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "publication_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "outcome": { |
| + "$ref": "#/$defs/outcome" |
| + }, |
| + "result_envelope": { |
| + "anyOf": [ |
| + { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 87512 |
| + }, |
| + { |
| + "type": "null" |
| + } |
| + ] |
| + } |
| + } |
| + }, |
| + "retry": { |
| + "type": "object", |
| + "additionalProperties": false, |
| + "required": [ |
| + "version", |
| + "request_id", |
| + "attempt_id", |
| + "operation_label", |
| + "target", |
| + "result_recipient", |
| + "input_envelope", |
| + "input_envelope_digest", |
| + "timeout_seconds", |
| + "predecessor" |
| + ], |
| + "properties": { |
| + "version": { |
| + "$ref": "#/$defs/v" |
| + }, |
| + "request_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "attempt_id": { |
| + "$ref": "#/$defs/id" |
| + }, |
| + "operation_label": { |
| + "type": "string", |
| + "minLength": 1, |
| + "maxLength": 64 |
| + }, |
| + "target": { |
| + "$ref": "#/$defs/target" |
| + }, |
| + "result_recipient": { |
| + "$ref": "#/$defs/recipient" |
| + }, |
| + "input_envelope": { |
| + "type": "string", |
| + "maxLength": 98620, |
| + "minLength": 1 |
| + }, |
| + "input_envelope_digest": { |
| + "$ref": "#/$defs/digest" |
| + }, |
| + "timeout_seconds": { |
| + "type": "integer", |
| + "minimum": 1, |
| + "maximum": 600 |
| + }, |
| + "predecessor": { |
| + "$ref": "#/$defs/reference" |
| + } |
| + }, |
| + "description": "Same prepare fields; predecessor is required non-null and its ID must equal the route request_id. Digest binds predecessor request bytes; server resolves its outcome." |
| + } |
| + } |
| +} |