Repository navigation
docs: Relay requirements for approval, authority and execution state (M4b, R6 to R10) - #44
Merged
Merged
Conversation
added 2 commits
November 8, 2026 14:07
…(M4b, R6 to R10) The module now proves a request is internally consistent and echoes its execution_digest (M4a). This records, as requirements toward the Relay and as a concept-level spec, what has to be added so an execution is also authorized, happens at most once and leaves evidence: an authenticated run-bound execution context (R6), approval verification naming the execution_digest before the first provider call (R7), an atomic durable reservation with a write-ahead record before each provider request and an UNKNOWN state that is never resent automatically and leaves only through recorded reconciliation (R8), a ProofTrace that keeps requested, authorized, sent, confirmed and uncertain apart and names the decision context (R9), and destroy and invoke under the same model (R10). Each item has the current behaviour read from the Relay at 55a899f, the required behaviour, a negative test and an acceptance condition; what was not read is marked NOT VERIFIED. The seven acceptance cases and the crash and module-refusal cases are written as tests for later. Documentation only: no code, no Relay change. --- [signing-metadata] key = cic-my-sign-key signature = vault:v1:MEUCIF4XuTubkyAMdUO0odGU1S3GaCjDiKMlkhemaYaBPXteAiEAv/jXy04OQ1OgLLaoYKceeaxIC/xD9GWNs+M/WvAL958= hash-algorithm = sha256 digest = 0cPd/Geh8e9QRlNKY7G0bO0EdurpuFeWxpAwGicECLk= [certificate] -----BEGIN CERTIFICATE----- MIICBjCCAaygAwIBAgIUSnRMR6RPnEbg296XWPOqq/u5PCwwCgYIKoZIzj0EAwIw QzELMAkGA1UEBhMCSFUxGTAXBgNVBAoMEENlbnRyYWxJbmZyYUNvcmUxGTAXBgNV BAMMEENJQyBEZXZlbG9wZXIgQ0EwHhcNMjYwMzIwMTMyMjU5WhcNMjYxMjMxMTMy MjU5WjBFMQswCQYDVQQGEwJIVTEZMBcGA1UECgwQQ2VudHJhbEluZnJhQ29yZTEb MBkGA1UEAwwSR2Fib3IgWm9sdGFuIFNpbmtvMFkwEwYHKoZIzj0CAQYIKoZIzj0D AQcDQgAEIG2CVmTfmLB9pLLclj7YmP2eedAjklpy4LGrU2ijoiy6Xqpuybv7OgJe i+ez31s65NEV8+X/ByeX1cstR988z6N8MHowCQYDVR0TBAIwADAdBgNVHQ4EFgQU yZN6AIX/TNnIJ9GwAa/NRN3ujHAwHwYDVR0jBBgwFoAUXn6CHYzPUqU4JVP8g+OS WeDYjhcwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEF BQcDBDAKBggqhkjOPQQDAgNIADBFAiEA+bFzXRoJ4PCQbhAAtpkcMjt0vNj5rEW0 lOMBGDNyaWkCIB1vmM7PcZzv/c9bIrxF5kqv6QXomouhByUfeNUTbpKW -----END CERTIFICATE-----
…fail-closed (M4b, review F1 and F2) F1: the cic-flow host functions are independent of each other and of any plan: sign signs the bytes the guest hands it, actuate forwards the guest's own method, URL, headers and body. A valid execution_digest and approval therefore prove nothing about which HTTP request leaves. R7 now states as a required security property that, before every actuate that can cause a side effect, the relay checks from its own authenticated context that the actual method, target, body and step belong to the reserved, approved execution, that a sign serving no such step is refused, and that a digest or trace id supplied by the guest is not authority. Two new negative tests: a different request under a valid approval, and a direct call of the host function outside an approved context. The spec gets a section on the host boundary. F2: the reservation and the write-ahead record are safety state, not telemetry: if they cannot be made durable no provider request is sent, and they are kept apart from optional trace processing. The cabinet's recorder at 55a899f is optional, runs once after all steps and continues on a failed write, so it is a post-hoc best-effort record (read: core/cabinet/service.go:99, :105-106, :299-307). A test for a failing store is added. Documentation only. --- [signing-metadata] key = cic-my-sign-key signature = vault:v1:MEUCIHBcFbHkfWq+LzDdJk57zY3PfvrbbJpozZ9KRfJQBraqAiEA7aV/FmgWI4XDnGfiqQSUEJvuBS/s8hrbFh5HtrCscJc= hash-algorithm = sha256 digest = YG1uxcICA5NtGtUbOksRFXtrjvAqM/OnyocQX8e+bNw= [certificate] -----BEGIN CERTIFICATE----- MIICBjCCAaygAwIBAgIUSnRMR6RPnEbg296XWPOqq/u5PCwwCgYIKoZIzj0EAwIw QzELMAkGA1UEBhMCSFUxGTAXBgNVBAoMEENlbnRyYWxJbmZyYUNvcmUxGTAXBgNV BAMMEENJQyBEZXZlbG9wZXIgQ0EwHhcNMjYwMzIwMTMyMjU5WhcNMjYxMjMxMTMy MjU5WjBFMQswCQYDVQQGEwJIVTEZMBcGA1UECgwQQ2VudHJhbEluZnJhQ29yZTEb MBkGA1UEAwwSR2Fib3IgWm9sdGFuIFNpbmtvMFkwEwYHKoZIzj0CAQYIKoZIzj0D AQcDQgAEIG2CVmTfmLB9pLLclj7YmP2eedAjklpy4LGrU2ijoiy6Xqpuybv7OgJe i+ez31s65NEV8+X/ByeX1cstR988z6N8MHowCQYDVR0TBAIwADAdBgNVHQ4EFgQU yZN6AIX/TNnIJ9GwAa/NRN3ujHAwHwYDVR0jBBgwFoAUXn6CHYzPUqU4JVP8g+OS WeDYjhcwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEF BQcDBDAKBggqhkjOPQQDAgNIADBFAiEA+bFzXRoJ4PCQbhAAtpkcMjt0vNj5rEW0 lOMBGDNyaWkCIB1vmM7PcZzv/c9bIrxF5kqv6QXomouhByUfeNUTbpKW -----END CERTIFICATE-----
sinkog
marked this pull request as ready for review
November 8, 2026 13:30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Documentation only (no code, no Relay change): the requirements the module needs from the Relay so that an execution is not only internally consistent (M4a, merged) but also authorized, happens at most once, and leaves evidence. The module can prove a request matches its plan and echo an
execution_digest; it cannot prove that anyone allowed it, that it has not run before, or what happened to it.docs/design/relay-requirements.md: new section R6 to R10, each with current behaviour (read from the Relay at55a899f, not executed), required behaviour, a negative test and an acceptance condition. Allneeded, not yet raised inCIC-Relay.docs/design/specs/approval-and-execution-state.md(new, status concept): what an approval names (execution_digest, approver, policy decision id, scope, validity, the module artifact digest verified at binding), the execution state of one approval, the ProofTrace evidence, the entry points, and the acceptance tests.docs/design/README.mdindex entry.The three points the spec fixes
sign/actuateof the execution; of any number of concurrent requests for one approval at most one wins. A write-ahead record precedes every provider request. A reservation is released only when no request was attempted.Review F1 and F2 (commit
5b0a6ed)F1 - the approval must bind the actual host call, not only the module's entry point.
cic-flow.signsigns whatever bytes the guest hands it andcic-flow.actuateforwards the guest's own method, URL, headers (itsAuthorizationverbatim) and body (read at55a899f), so a validexecution_digestand a verified approval prove nothing about which HTTP request leaves. R7 now states as a required security property that, before everyactuatethat can cause a side effect, the relay checks from its own authenticated context that the actual method, target, body and step belong to the reserved, approved execution (the requests an execution may make come from the approved execution, never from the guest); that asignserving no such step is refused; and that a guest-supplied digest or trace id is not authority. The module's M4a gate covers only its ownexecutepath and cannot protect the host boundary. New negative tests: a different request under a valid approval, and a direct call ofcic-flow.actuate/signoutside an approved context. New spec section "The host boundary". How the relay does it stays its own design.F2 - the safety record is fail-closed. The reservation and the write-ahead record are safety state, not telemetry: if they cannot be made durable, no provider request is sent; they are kept apart from optional trace processing (display, aggregation, export). New read fact: the cabinet's ProofTrace recorder is optional (
nilskips it,core/cabinet/service.go:99,:105-106) and runs once, after all steps, with a failed write logged and the workflow continuing (:299-307): a post-hoc, best-effort record, not a write-ahead one. New negative test: the store fails at the reservation and at the write-ahead record, no provider request in either case.Relay facts used (read at
55a899f, not executed)core/cabinet/service.go:228); the cabinet dispatches onlyinit,process,get,notifyto a guest (:234-240).cic-flowhost functions getcicFlowDeps{sign, doer, egress}: no run, principal or plan (cmd/relay/cic_flow_host_funcs.go:51-55).signandactuateaccept atrace_idand never use it; the guest chooses it, so it cannot be an authority.actuatereturns every transport error as one error text, so "never sent" and "sent, answer lost" look alike to the guest.cic-flowhost functions.core/nexus/ois(Intent,Policy.Authorize) has non-test importers only incore/nexus/recorderandtools/statechain-demo;cmd/relayandcore/cabinetdo not use it: the model exists, the link to the module's execution breaks there.NOT VERIFIED (said so in the documents): whether the Relay has a durable store and its guarantees; whether it sees the operation name (
execute/destroy/invoke) before the guest runs; which correlation method exists for a lost asynchronous answer.Acceptance tests written for later (no Relay implementation here)
The seven required cases (and, after the review, three more: a different request under a valid approval, the host function called directly, and a durable record that cannot be written) (no or invalid approval; approval for another
execution_digest; expired or revoked; two concurrent uses; lost OCI answer; changed module artifact;destroy/invokewithout approval), plus: reconciliation needed to leave UNKNOWN, crash after the reservation, crash after the write-ahead record, a module that refuses with zero sends, and the shape of the trace. Each states when "no provider request" holds (zerosignand zeroactuate).Module-side follow-up (concept, not built)
destroyandinvokehave no plan and no digest, so the module cannot yet hand the Relay a value to approve for them. A digest over the same framing (operation, kind, target, and forinvokethe canonical body) is recorded as a concept item.Not in this PR
No Relay code, no module code, no
module.wasmrebuild or signature, no production-integration claim. Nothing here is implemented in the Relay today. No merge without explicit owner GO.🤖 Generated with Claude Code