@serve.zone/interfaces 31.9.0 → 32.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/changelog.md +476 -0
  2. package/dist_ts/00_commitinfo_data.js +3 -3
  3. package/dist_ts/appstore/types.d.ts +4 -6
  4. package/dist_ts/appstore/types.js +5 -5
  5. package/dist_ts/data/backup.d.ts +3 -26
  6. package/dist_ts/data/cluster.d.ts +36 -11
  7. package/dist_ts/data/cluster.js +33 -22
  8. package/dist_ts/data/clusteringress.d.ts +8 -0
  9. package/dist_ts/data/clusteringress.js +13 -8
  10. package/dist_ts/data/clusternode.d.ts +121 -74
  11. package/dist_ts/data/clusternode.js +109 -132
  12. package/dist_ts/data/coremail.d.ts +3 -7
  13. package/dist_ts/data/coremail.js +2 -2
  14. package/dist_ts/data/coremail.runtime.d.ts +0 -1
  15. package/dist_ts/data/coremail.runtime.js +5 -22
  16. package/dist_ts/data/deploymentadoption.d.ts +0 -2
  17. package/dist_ts/data/deploymentadoption.js +40 -6
  18. package/dist_ts/data/deploymentoperation.js +16 -3
  19. package/dist_ts/data/deploymentpreflight.d.ts +0 -2
  20. package/dist_ts/data/deploymentpreflight.js +1 -1
  21. package/dist_ts/data/immutableimage.d.ts +38 -21
  22. package/dist_ts/data/immutableimage.js +45 -5
  23. package/dist_ts/data/index.d.ts +1 -1
  24. package/dist_ts/data/isolatedrestore.d.ts +139 -21
  25. package/dist_ts/data/isolatedrestore.golden.d.ts +0 -1
  26. package/dist_ts/data/isolatedrestore.golden.js +6 -9
  27. package/dist_ts/data/isolatedrestore.js +176 -40
  28. package/dist_ts/data/mail.d.ts +1 -1
  29. package/dist_ts/data/networkaddressplan.d.ts +8 -8
  30. package/dist_ts/data/networkaddressplan.js +4 -4
  31. package/dist_ts/data/networkauthority.d.ts +20 -22
  32. package/dist_ts/data/networkauthority.js +26 -28
  33. package/dist_ts/data/networkcredential.d.ts +10 -10
  34. package/dist_ts/data/networkcredential.js +16 -16
  35. package/dist_ts/data/networkdelivery.d.ts +18 -18
  36. package/dist_ts/data/networkdelivery.js +16 -16
  37. package/dist_ts/data/networklease.d.ts +18 -7
  38. package/dist_ts/data/networklease.js +29 -7
  39. package/dist_ts/data/networknode.d.ts +2 -2
  40. package/dist_ts/data/networkprojection.d.ts +59 -60
  41. package/dist_ts/data/networkprojection.js +53 -57
  42. package/dist_ts/data/networkprotection.d.ts +27 -28
  43. package/dist_ts/data/networkprotection.js +36 -36
  44. package/dist_ts/data/networkrouter.d.ts +11 -11
  45. package/dist_ts/data/networkrouter.js +1 -1
  46. package/dist_ts/data/networksigning.d.ts +16 -18
  47. package/dist_ts/data/networksigning.js +20 -21
  48. package/dist_ts/data/nodecredential.d.ts +4 -6
  49. package/dist_ts/data/nodeenrollment.d.ts +35 -40
  50. package/dist_ts/data/nodeenrollment.js +57 -72
  51. package/dist_ts/data/privatenetwork.d.ts +21 -24
  52. package/dist_ts/data/privatenetwork.js +39 -54
  53. package/dist_ts/data/runtimeassignment.d.ts +50 -34
  54. package/dist_ts/data/runtimeassignment.js +79 -45
  55. package/dist_ts/data/runtimeconfig.d.ts +21 -22
  56. package/dist_ts/data/runtimeconfig.js +24 -24
  57. package/dist_ts/data/runtimesession.d.ts +100 -62
  58. package/dist_ts/data/runtimesession.js +98 -79
  59. package/dist_ts/data/runtimeterminalreceipt.d.ts +28 -29
  60. package/dist_ts/data/runtimeterminalreceipt.js +42 -42
  61. package/dist_ts/data/secret.d.ts +18 -25
  62. package/dist_ts/data/secret.js +12 -52
  63. package/dist_ts/data/servicecredential.d.ts +14 -16
  64. package/dist_ts/data/servicecredential.js +17 -28
  65. package/dist_ts/data/serviceruntime.d.ts +7 -7
  66. package/dist_ts/data/serviceruntime.js +4 -4
  67. package/dist_ts/data/sparknodereport.d.ts +27 -14
  68. package/dist_ts/data/sparknodereport.js +35 -28
  69. package/dist_ts/data/webpush.d.ts +0 -1
  70. package/dist_ts/index.d.ts +2 -1
  71. package/dist_ts/index.js +3 -2
  72. package/dist_ts/platform/objectstorageretention.d.ts +21 -24
  73. package/dist_ts/platform/objectstorageretention.js +10 -17
  74. package/dist_ts/platform/storage.d.ts +1 -3
  75. package/dist_ts/platform/storagemigration.d.ts +0 -7
  76. package/dist_ts/platform/storagemigration.golden.d.ts +0 -1
  77. package/dist_ts/platform/storagemigration.golden.js +8 -12
  78. package/dist_ts/platform/storagemigration.js +6 -51
  79. package/dist_ts/platform/types.d.ts +2 -2
  80. package/dist_ts/private/canonicaljson.d.ts +9 -1
  81. package/dist_ts/private/canonicaljson.js +14 -2
  82. package/dist_ts/private/dataproperties.d.ts +62 -0
  83. package/dist_ts/private/dataproperties.js +117 -0
  84. package/dist_ts/private/networkcontract.d.ts +10 -10
  85. package/dist_ts/private/networkcontract.js +1 -1
  86. package/dist_ts/private/releaseversion.d.ts +37 -0
  87. package/dist_ts/private/releaseversion.js +48 -0
  88. package/dist_ts/private/runtimepolicy.d.ts +2 -2
  89. package/dist_ts/private/runtimepolicy.js +1 -1
  90. package/dist_ts/private/tokenlist.d.ts +9 -0
  91. package/dist_ts/private/tokenlist.js +23 -0
  92. package/dist_ts/protocol.d.ts +137 -0
  93. package/dist_ts/protocol.js +255 -0
  94. package/dist_ts/requests/admin.d.ts +8 -8
  95. package/dist_ts/requests/backup.d.ts +102 -112
  96. package/dist_ts/requests/cluster.d.ts +10 -10
  97. package/dist_ts/requests/clusteringress.d.ts +9 -0
  98. package/dist_ts/requests/clusteringress.js +1 -1
  99. package/dist_ts/requests/config.d.ts +3 -3
  100. package/dist_ts/requests/corestore.d.ts +0 -3
  101. package/dist_ts/requests/corestore.js +5 -9
  102. package/dist_ts/requests/network.d.ts +30 -30
  103. package/dist_ts/requests/node.d.ts +15 -28
  104. package/dist_ts/requests/pallet.d.ts +13 -13
  105. package/dist_ts/requests/runtimesession.d.ts +53 -41
  106. package/dist_ts/requests/secret.d.ts +4 -5
  107. package/dist_ts/requests/secret.js +1 -5
  108. package/dist_ts/runtime.corestore.d.ts +56 -74
  109. package/dist_ts/runtime.corestore.databaseexportpackage.d.ts +30 -36
  110. package/dist_ts/runtime.corestore.databaseexportpackage.golden.d.ts +11 -12
  111. package/dist_ts/runtime.corestore.databaseexportpackage.golden.js +54 -71
  112. package/dist_ts/runtime.corestore.databaseexportpackage.js +9 -25
  113. package/dist_ts/runtime.corestore.js +35 -135
  114. package/dist_ts/runtime.corestore.relay.js +2 -7
  115. package/dist_ts/runtime.d.ts +38 -48
  116. package/dist_ts/runtime.js +20 -77
  117. package/dist_ts/runtime.networkprojection.d.ts +4 -4
  118. package/dist_ts/runtime.networkprojection.js +18 -18
  119. package/dist_ts/runtime.workloadinit.d.ts +23 -24
  120. package/dist_ts/runtime.workloadinit.js +6 -13
  121. package/package.json +2 -2
  122. package/readme.md +421 -270
  123. package/ts/00_commitinfo_data.ts +3 -3
  124. package/ts/appstore/types.ts +4 -6
  125. package/ts/data/backup.ts +3 -38
  126. package/ts/data/cluster.ts +51 -34
  127. package/ts/data/clusteringress.ts +12 -12
  128. package/ts/data/clusternode.ts +204 -232
  129. package/ts/data/coremail.runtime.ts +4 -21
  130. package/ts/data/coremail.ts +3 -7
  131. package/ts/data/deploymentadoption.ts +46 -4
  132. package/ts/data/deploymentoperation.ts +16 -1
  133. package/ts/data/deploymentpreflight.ts +0 -2
  134. package/ts/data/immutableimage.ts +71 -24
  135. package/ts/data/index.ts +1 -1
  136. package/ts/data/isolatedrestore.golden.ts +5 -9
  137. package/ts/data/isolatedrestore.ts +339 -54
  138. package/ts/data/mail.ts +1 -1
  139. package/ts/data/networkaddressplan.ts +13 -13
  140. package/ts/data/networkauthority.ts +42 -46
  141. package/ts/data/networkcredential.ts +24 -24
  142. package/ts/data/networkdelivery.ts +27 -27
  143. package/ts/data/networklease.ts +40 -13
  144. package/ts/data/networknode.ts +2 -2
  145. package/ts/data/networkprojection.ts +119 -124
  146. package/ts/data/networkprotection.ts +54 -55
  147. package/ts/data/networkrouter.ts +14 -14
  148. package/ts/data/networksigning.ts +29 -32
  149. package/ts/data/nodecredential.ts +4 -6
  150. package/ts/data/nodeenrollment.ts +96 -114
  151. package/ts/data/privatenetwork.ts +58 -74
  152. package/ts/data/runtimeassignment.ts +143 -94
  153. package/ts/data/runtimeconfig.ts +45 -46
  154. package/ts/data/runtimesession.ts +185 -138
  155. package/ts/data/runtimeterminalreceipt.ts +72 -73
  156. package/ts/data/secret.ts +28 -76
  157. package/ts/data/servicecredential.ts +33 -43
  158. package/ts/data/serviceruntime.ts +11 -11
  159. package/ts/data/sparknodereport.ts +48 -36
  160. package/ts/data/webpush.ts +0 -1
  161. package/ts/index.ts +2 -0
  162. package/ts/platform/objectstorageretention.ts +40 -54
  163. package/ts/platform/storage.ts +1 -3
  164. package/ts/platform/storagemigration.golden.ts +7 -11
  165. package/ts/platform/storagemigration.ts +5 -55
  166. package/ts/platform/types.ts +2 -2
  167. package/ts/private/canonicaljson.ts +13 -1
  168. package/ts/private/dataproperties.ts +139 -0
  169. package/ts/private/networkcontract.ts +9 -9
  170. package/ts/private/releaseversion.ts +70 -0
  171. package/ts/private/runtimepolicy.ts +2 -2
  172. package/ts/private/tokenlist.ts +30 -0
  173. package/ts/protocol.ts +364 -0
  174. package/ts/requests/admin.ts +11 -11
  175. package/ts/requests/backup.ts +112 -117
  176. package/ts/requests/cluster.ts +13 -13
  177. package/ts/requests/clusteringress.ts +9 -0
  178. package/ts/requests/config.ts +3 -3
  179. package/ts/requests/corestore.ts +4 -16
  180. package/ts/requests/network.ts +40 -40
  181. package/ts/requests/node.ts +21 -38
  182. package/ts/requests/pallet.ts +17 -17
  183. package/ts/requests/runtimesession.ts +63 -51
  184. package/ts/requests/secret.ts +5 -10
  185. package/ts/runtime.corestore.databaseexportpackage.golden.ts +77 -95
  186. package/ts/runtime.corestore.databaseexportpackage.ts +51 -73
  187. package/ts/runtime.corestore.relay.ts +1 -10
  188. package/ts/runtime.corestore.ts +118 -268
  189. package/ts/runtime.networkprojection.ts +30 -30
  190. package/ts/runtime.ts +83 -158
  191. package/ts/runtime.workloadinit.ts +36 -48
  192. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.d.ts +0 -902
  193. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.d.ts +0 -35
  194. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.js +0 -315
  195. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.js +0 -3315
  196. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.d.ts +0 -291
  197. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.d.ts +0 -30
  198. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.js +0 -408
  199. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.js +0 -1126
  200. package/ts/runtime.cloudlylegacydeploymentsettlement.golden.ts +0 -421
  201. package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.ts +0 -510
  202. package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.ts +0 -2470
  203. package/ts/runtime.cloudlylegacydeploymentsettlement.ts +0 -6630
package/readme.md CHANGED
@@ -14,10 +14,10 @@ pnpm add @serve.zone/interfaces
14
14
 
15
15
  ## Public API
16
16
 
17
- The root export exposes five namespaces:
17
+ The root export exposes six namespaces:
18
18
 
19
19
  ```typescript
20
- import { appstore, data, platform, platformservice, requests } from '@serve.zone/interfaces';
20
+ import { appstore, data, platform, platformservice, protocol, requests } from '@serve.zone/interfaces';
21
21
  ```
22
22
 
23
23
  | Namespace | Purpose |
@@ -25,6 +25,7 @@ import { appstore, data, platform, platformservice, requests } from '@serve.zone
25
25
  | `appstore` | App Store catalog, manifest, service requirement, and upgrade contracts. |
26
26
  | `data` | Durable platform object shapes such as clusters, services, deployments, images, domains, DNS entries, secrets, users, status, settings, backups, registries, BaseOS metadata, and task executions. |
27
27
  | `requests` | TypedRequest contracts for Cloudly and serve.zone control-plane RPC methods. |
28
+ | `protocol` | The handshake two peers exchange when a session opens: what each side speaks, the oldest peer it accepts, and the named refusal when they cannot serve one session. |
28
29
  | `platform` | Current platform-service contracts for email, SMS, push notifications, letters, AI, databases, object storage, logging, backups, and SIP. |
29
30
  | `platformservice` | Legacy platform-service namespace kept for older consumers that still depend on the previous layout. |
30
31
 
@@ -44,8 +45,8 @@ import {
44
45
  release identity, attestation, approval, authority, digest, and validator
45
46
  contracts. Its runtime dependency graph is limited to the immutable-image digest
46
47
  validator; unlike `@serve.zone/interfaces/runtime`, it does not load the general
47
- runtime graph, `plugins.js`, SmartCrypto, TypedRequest, Corestore, or settlement
48
- runtime modules.
48
+ runtime graph, `plugins.js`, SmartCrypto, TypedRequest, or Corestore runtime
49
+ modules.
49
50
 
50
51
  ### Directional Image Streams
51
52
 
@@ -94,7 +95,7 @@ these shared contracts alone do not implement an endpoint.
94
95
  unauthenticated request shape must update. Its response remains the public
95
96
  `IClusterNode`, never a persisted backend document.
96
97
 
97
- `data.INodeCredentialMetadataV1` separates `spark` and `pallet` purposes and
98
+ `data.INodeCredentialMetadata` separates `spark` and `pallet` purposes and
98
99
  exposes lifecycle state, generation, rotation-required status and session epoch,
99
100
  but no bearer, hash, socket peer or controller process identity. Infrastructure
100
101
  credentials do not grant organization or workload permissions.
@@ -115,16 +116,39 @@ before enabling rotation or Pallet enrollment.
115
116
 
116
117
  ### Spark Host Reporting
117
118
 
118
- `data.sparkNodeHeartbeatV2Contract` defines the HTTPS POST endpoint
119
- `/spark/v2/nodes/heartbeat` and transport byte limits. Its exact request snapshot
120
- contains a Spark-purpose bearer and `ISparkNodeReportV2`: fractional host CPU,
121
- memory and disk observations, Linux host facts, and the verified bundle version,
122
- source commit and manifest digest. It has no Docker or container-count fields.
123
- Local runtime readiness remains distinct from unverified workload readiness.
119
+ `data.sparkNodeHeartbeatContract` defines the HTTPS POST endpoint
120
+ `/spark/nodes/heartbeat`, transport byte limits and `protocolRefusalStatus`, the
121
+ status a refused offer is answered with. Its exact request snapshot contains a
122
+ Spark-purpose bearer, the sender's `protocol` offer and `ISparkNodeReport`:
123
+ fractional host CPU, memory and disk observations, Linux host facts, and the
124
+ verified bundle version, source commit and manifest digest. It has no Docker or
125
+ container-count fields. Local runtime readiness remains distinct from unverified
126
+ workload readiness.
127
+
128
+ The route reads the bounded body, reads the offer with `protocol.readProtocolOffer`,
129
+ negotiates it against its own `sparkNode` offer and answers a refusal with
130
+ `protocolRefusalStatus` and an `IProtocolRefusal` body — all before the node is
131
+ authenticated, so a peer of another major is refused by name instead of by a
132
+ credential verdict it cannot act on. The receipt carries the controller's own
133
+ offer, so an accepted node judges the session it just reported into.
134
+
135
+ The Swarm-era Spark runtime keeps its own four routes in
136
+ `data.sparkSwarmNodeContracts`, each stating the same three facts as the route
137
+ above — `heartbeat` (`/spark/swarm-nodes/heartbeat`), `metricsSample`, `actionResult`
138
+ and `swarmObservation`, every one of them an `{ endpoint, maxRequestBytes,
139
+ maxResponseBytes }` — so one bounded read serves every Spark route. The bounds
140
+ differ because the bodies do: a heartbeat states the node's whole runtime
141
+ description, a metrics-sample answer carries every action queued for that node, and
142
+ one observation carries up to 1024 observed Swarm nodes. That is a second runtime,
143
+ not an older version of this one: a worker in `coreflow-node` mode posts those
144
+ bodies and a pallet node posts this one, and the family retires with coreflow
145
+ rather than with this release. `ISparkSwarmNodeHeartbeatRequest` is now owned here,
146
+ so Cloudly and Spark no longer carry private copies of it, and every Swarm-era body
147
+ — request and answer alike — carries `protocol` and is judged in the same order.
124
148
 
125
149
  Cloudly must authenticate the bearer against current Spark authority and fence
126
150
  credential generation and session epoch transactionally before recording host
127
- liveness. `ISparkNodeHeartbeatReceiptV2` contains only server-derived acceptance
151
+ liveness. `ISparkNodeHeartbeatReceipt` contains only server-derived acceptance
128
152
  evidence; the sender must match its node ID and credential generation to its exact
129
153
  active identity. Neither a receipt nor the sender's observation time grants
130
154
  current authority or workload readiness. Persist only the report and receipt in
@@ -138,27 +162,28 @@ listener, credentials, persistence, freshness or shutdown.
138
162
 
139
163
  ### Independent Node Enrollment
140
164
 
141
- `requests.node` defines `getNodeEnrollmentStateV1` for current Spark-authenticated
142
- adoption preconditions and `enrollNodeV1` for atomic Spark/Pallet enrollment. These
165
+ `requests.node` defines `getNodeEnrollmentState` for current Spark-authenticated
166
+ adoption preconditions and `enrollNode` for atomic Spark/Pallet enrollment. These
143
167
  contracts do not implement endpoints, authentication, a database or a daemon.
144
168
 
145
169
  The enrollment proposal binds the canonical HTTPS Cloudly origin, hostname,
146
170
  enrollment identity, original bootstrap proof hash and an exact ordered pair of
147
171
  independently prepared Spark/Pallet credential hashes and CAS counters. Fresh Jump
148
172
  starts both at zero; existing-node adoption rotates current Spark and requires
149
- Pallet authority to be absent. `snapshotNodeEnrollmentV1` returns a detached,
150
- frozen snapshot; `computeNodeEnrollmentV1Digest` hashes domain-separated strict
151
- canonical JSON. Neither helper generates or persists credential material.
173
+ Pallet authority to be absent. `snapshotNodeEnrollment` returns a detached,
174
+ frozen snapshot; `computeNodeEnrollmentDigest` hashes strict canonical JSON
175
+ under `nodeEnrollmentContract.digestDomain`, `serve.zone/node-enrollment`.
176
+ Neither helper generates or persists credential material.
152
177
 
153
178
  Transport proof is excluded from the digest to permit exact lost-response
154
179
  recovery. Bootstrap proof must hash to the original proof hash and is allowed only
155
180
  before a receipt exists. Pending-Spark proof must hash to the prepared Spark hash
156
- and is allowed only for an existing exact receipt. `verifyNodeEnrollmentProofV1`
181
+ and is allowed only for an existing exact receipt. `verifyNodeEnrollmentProof`
157
182
  checks this binding and mode only: Cloudly must derive receipt state itself and
158
183
  fence live bootstrap/credential authority in the same owned transaction. Current
159
184
  Jump codes are 12 random bytes encoded as 16 canonical base64url characters.
160
185
 
161
- `bindNodeEnrollmentAcknowledgementV1` checks the complete digest, both owners,
186
+ `bindNodeEnrollmentAcknowledgement` checks the complete digest, both owners,
162
187
  exact next generations and unchanged adopted node ID, returning a detached frozen
163
188
  acknowledgement. Authenticate Cloudly before accepting it. The acknowledgement is
164
189
  immutable commit evidence, not ongoing authority or workload readiness. Server
@@ -168,20 +193,20 @@ local activation independently and converge after restart; there is no shared
168
193
  plaintext vault or cross-local-database ACID claim. Never log proof/request bodies
169
194
  or hashes, put them in URLs, or expose them through transport hooks.
170
195
 
171
- `requests.pallet` defines the node-local `preparePalletNodeEnrollmentV1`,
172
- `bindPalletNodeEnrollmentV1` and `activatePalletNodeEnrollmentV1` methods. They
196
+ `requests.pallet` defines the node-local `preparePalletNodeEnrollment`,
197
+ `bindPalletNodeEnrollment` and `activatePalletNodeEnrollment` methods. They
173
198
  belong only on the protected root-owned Unix control socket, never a network
174
199
  router. Preparation requests initial Pallet authority (zero generation and
175
200
  session epoch) and returns only its independently retained credential hash.
176
- `snapshotPalletNodeEnrollmentPreparationV1` and
177
- `bindPalletNodeEnrollmentPreparationV1` validate that exact exchange.
201
+ `snapshotPalletNodeEnrollmentPreparation` and
202
+ `bindPalletNodeEnrollmentPreparation` validate that exact exchange.
178
203
 
179
204
  Local confirmations explicitly distinguish a durable pending binding from the
180
- exact current active Pallet identity. `bindPalletNodeEnrollmentBoundV1` verifies
205
+ exact current active Pallet identity. `bindPalletNodeEnrollmentBound` verifies
181
206
  the whole enrollment digest and its pre-assignment node ID (null for fresh Jump).
182
- `snapshotPalletNodeEnrollmentActivationV1` captures the exact activation request,
207
+ `snapshotPalletNodeEnrollmentActivation` captures the exact activation request,
183
208
  rejecting extra wrapper fields as well as malformed nested data.
184
- `bindPalletNodeEnrollmentActiveV1` takes that complete activation request and
209
+ `bindPalletNodeEnrollmentActive` takes that complete activation request and
185
210
  verifies the full Cloudly acknowledgement, including both owners and the assigned
186
211
  node ID. Both confirmation binders return detached frozen
187
212
  snapshots captured before hashing. These are pure content checks: authenticate
@@ -189,7 +214,7 @@ the local transport separately, and have Pallet prove its current owned state
189
214
  before issuing a confirmation. A historical receipt is insufficient, and Pallet
190
215
  activation does not imply Spark activation or workload readiness.
191
216
 
192
- `palletNodeEnrollmentV1Contract` specifies one four-byte unsigned big-endian
217
+ `palletNodeEnrollmentContract` specifies one four-byte unsigned big-endian
193
218
  length-prefixed UTF-8 JSON frame per direction, followed by write-half-close, at
194
219
  most 32768 request bytes and 16384 response bytes excluding the prefix. Implement
195
220
  the listener with half-open response support, strict EOF/trailing-byte rejection,
@@ -199,14 +224,14 @@ No bearer, generic rotation operation or database access is part of this IPC API
199
224
 
200
225
  ### Service Machine Credentials
201
226
 
202
- `requests.admin.getServiceMachineCredentialV1` inspects redacted metadata;
203
- `mutateServiceMachineCredentialV1` creates, rotates or revokes authority for one
227
+ `requests.admin.getServiceMachineCredential` inspects redacted metadata;
228
+ `mutateServiceMachineCredential` creates, rotates or revokes authority for one
204
229
  exact existing service and organization. `ensure` requires absent authority and
205
230
  a null expected generation. Rotation and revocation require the exact current
206
231
  generation. Retain the mutation ID and complete input for retry; a receipt replay
207
232
  returns historical metadata without restoring authority.
208
233
 
209
- `IServiceMachineGrantV1` currently grants only `platform:session`. This is separate
234
+ `IServiceMachineGrant` currently grants only `platform:session`. This is separate
210
235
  from deployment grants, human membership and infrastructure permissions. The
211
236
  backend must authenticate each JWT against the current active credential
212
237
  generation, expiry and exact service ownership. Structural snapshot helpers do
@@ -224,7 +249,7 @@ each request against live canonical membership and policy, reserve aliases
224
249
  transactionally, reject cross-organization attachments, and retain historical
225
250
  receipts. These contracts do not implement the backend or node enforcement.
226
251
 
227
- `data.buildPrivateNetworkFqdnV1` produces
252
+ `data.buildPrivateNetworkFqdn` produces
228
253
  `<alias>.n-<26 lowercase base32 characters>.o-<26 lowercase base32 characters>.internal.`
229
254
  from server-owned immutable DNS keys. Aliases are lowercase ASCII labels unique
230
255
  per network. Display-name edits do not rename DNS. A sole attachment selects its
@@ -233,7 +258,7 @@ suffix. Fully qualified names remain unambiguous across networks. Network policy
233
258
  permits member-to-member connectivity and explicitly configures or denies external
234
259
  DNS forwarding. Creating an alias does not publish public DNS, ingress or ports.
235
260
 
236
- `data.composeServicePrivateNetworkDnsPolicyV1(membership, networks)` derives the
261
+ `data.composeServicePrivateNetworkDnsPolicy(membership, networks)` derives the
237
262
  service's network suffixes, the single selected search suffix, and the intersection
238
263
  of external forwarding permissions. Supply every attached network exactly once,
239
264
  with active state and matching canonical organization/DNS keys. Missing, extra,
@@ -254,7 +279,7 @@ one second. DNS expiry does not prove packet revocation or permit identity reuse
254
279
  Retirement and attachment responses describe desired or historical metadata;
255
280
  consumers must separately track outstanding node revocations and readiness.
256
281
 
257
- `IRuntimeNetworkProtectedAuthorityV1` declares the complete controller-owned IPv4
282
+ `IRuntimeNetworkProtectedAuthority` declares the complete controller-owned IPv4
258
283
  protection set, disjoint private workload/transit pools, protected resolver and
259
284
  platform endpoints, and every affected egress authority, including offline nodes.
260
285
  Its snapshot and digest helpers bind the exact predecessor and all declaration
@@ -265,7 +290,7 @@ the declaration is not a revocation acknowledgement. Prefix arrays are sorted
265
290
  lexically and nonoverlapping; pools and endpoints are sorted by ID; egress owners
266
291
  are ordered by canonical JSON of `[nodeId, runtimeNamespace]`.
267
292
 
268
- `IRuntimeNetworkProtectionReceiptV1` reports a worker's durable, joined native
293
+ `IRuntimeNetworkProtectionReceipt` reports a worker's durable, joined native
269
294
  protection journal separately from projection admission. It binds a complete
270
295
  protected-authority reference, node/controller scope, authenticated reporter,
271
296
  boot identity and exact native journal reference to a monotonic receipt chain.
@@ -273,10 +298,10 @@ A null `nativeBarrier` explicitly removes allocation eligibility. A boot or
273
298
  protection change requires new native journal evidence. These portable values
274
299
  do not inspect a kernel or authenticate their own producer.
275
300
 
276
- `reportRuntimeNetworkProtectionV1` uses the current authenticated physical peer.
301
+ `reportRuntimeNetworkProtection` uses the current authenticated physical peer.
277
302
  Its request binder accepts a historical outbox only against receiver-owned
278
303
  reporter history and identifies that use as historical. The response must match
279
- the exact sent receipt. `bindRuntimeNetworkAllocationProtectionV1` requires the
304
+ the exact sent receipt. `bindRuntimeNetworkAllocationProtection` requires the
280
305
  current persisted protection receipt and durable session for every declared
281
306
  egress owner, in canonical owner order. Missing, unavailable, stale-protection
282
307
  and superseded-session evidence reject. An offline owner cannot be omitted.
@@ -285,15 +310,15 @@ reserving any handoff or workload address. No generic independent-fence flag is
285
310
  provided; such a mechanism needs its own verified owner. Protection receipts
286
311
  neither prove workload readiness nor release packet/address/name quarantine.
287
312
 
288
- `IRuntimeNetworkHandoffLeaseV1` binds an immutable node/router incarnation to its
313
+ `IRuntimeNetworkHandoffLease` binds an immutable node/router incarnation to its
289
314
  protected-authority reference, transit subnet and peers, protocol-qualified source
290
315
  ports, nonzero conntrack zone and 128-bit label. The label is exactly 32 lowercase
291
316
  hex characters. Port ranges are inclusive, sorted by protocol then first port,
292
- and disjoint within each protocol. `bindRuntimeNetworkHandoffLeaseV1` verifies both
317
+ and disjoint within each protocol. `bindRuntimeNetworkHandoffLease` verifies both
293
318
  digests, scope and transit-pool containment and returns detached values. It does
294
319
  not authenticate a projection or attest to an actual namespace/interface.
295
320
 
296
- `snapshotRuntimeNetworkHandoffLedgerV1` verifies at most 256 retained leases for
321
+ `snapshotRuntimeNetworkHandoffLedger` verifies at most 256 retained leases for
297
322
  one node/controller epoch, including quarantined allocations. It rejects host
298
323
  handoff-port collisions even across router/runtime namespace changes, and zone or
299
324
  label reuse within one router incarnation. The caller must transactionally supply
@@ -304,7 +329,7 @@ permits handoff identity reuse. Signed complete projections, live interface proo
304
329
  and the separate router/host apply journal remain responsibilities of Cloudly and
305
330
  Pallet; neither packet admission nor readiness follows from these helpers.
306
331
 
307
- `IRuntimeNetworkWorkloadLeaseV1` is the immutable material referenced by
332
+ `IRuntimeNetworkWorkloadLease` is the immutable material referenced by
308
333
  `assignment.workload.network`. It binds one execution attempt and router
309
334
  incarnation to a workload pool, dedicated unbridged `linkSubnet`, workload
310
335
  `address`, exact `/32` `sourcePrefix`, and router-side `gateway`. The gateway
@@ -312,11 +337,11 @@ also supplies `dnsServer` on port 53, because CRI DNS settings cannot encode a
312
337
  port. Canonical private point-to-point subnets through `/31` are accepted; the
313
338
  owning CNI implementation must qualify its chosen layout. The source prefix,
314
339
  link subnet, VPN control prefix and host/router transit subnet have different
315
- roles. `bindRuntimeNetworkWorkloadLeaseToAssignmentV1` verifies immutable
340
+ roles. `bindRuntimeNetworkWorkloadLeaseToAssignment` verifies immutable
316
341
  assignment identity without a circular assignment digest. Membership, aliases,
317
342
  readiness and projection revisions never change this address material.
318
343
 
319
- `IRuntimeNetworkProjectionV1` contains complete relevant network definitions,
344
+ `IRuntimeNetworkProjection` contains complete relevant network definitions,
320
345
  service memberships and endpoint leases, including remote ready endpoints and
321
346
  declared services with no replicas. It carries the current protected authority,
322
347
  exact historical allocation authorities, local handoff, a maximum 15-minute DNS
@@ -329,7 +354,7 @@ and 4,096 directed grants. These transport bounds do not assert native capacity;
329
354
  the node must preflight the actual SmartVPN, Smartnftables and DNS limits before
330
355
  effectful application.
331
356
 
332
- `validateRuntimeNetworkProjectionV1` checks complete digest and scope bindings,
357
+ `validateRuntimeNetworkProjection` checks complete digest and scope bindings,
333
358
  pool containment, disjoint link subnets, network/organization keys, aliases,
334
359
  resolver authorization and ready-replica uniqueness. It does not authenticate
335
360
  controller inventory, prove observation provenance or authorize allocation reuse.
@@ -338,7 +363,7 @@ retain all affected offline authorities and complete its allocation/barrier
338
363
  transactions before issuance.
339
364
 
340
365
  An endpoint may publish node ports through the optional
341
- `IRuntimeNetworkEndpointV1.publishedPorts`. Each `IRuntimeNetworkPublishedPortV1`
366
+ `IRuntimeNetworkEndpoint.publishedPorts`. Each `IRuntimeNetworkPublishedPort`
342
367
  binds one `hostPort` (1..65535, unique per node and protocol across the
343
368
  projection) to the workload's `targetPort`, optionally on an explicit uplink
344
369
  `hostIp` (never the wildcard or loopback; binding it to the node's uplink is
@@ -349,23 +374,23 @@ An endpoint that publishes nothing omits the member entirely and keeps its exact
349
374
  canonical bytes and digest; a present member is never an empty list, so
350
375
  "publishes nothing" has exactly one encoding.
351
376
 
352
- Operators author that authorization through `getServicePublishedPortsV1` and
353
- `setServicePublishedPortsV1`, modelled on the private-network membership pair.
354
- `IServicePublishedPortsV1` returns the current authorized list with its policy
355
- `revision`, or null when a service never had one. `ISetServicePublishedPortsV1`
356
- returns the accepted `IServicePublishedPortsV1` document with its new
377
+ Operators author that authorization through `getServicePublishedPorts` and
378
+ `setServicePublishedPorts`, modelled on the private-network membership pair.
379
+ `IServicePublishedPorts` returns the current authorized list with its policy
380
+ `revision`, or null when a service never had one. `ISetServicePublishedPorts`
381
+ returns the accepted `IServicePublishedPorts` document with its new
357
382
  `revision` and a `replayed` flag, and carries `expectedRevision` (null for the first document), a retained
358
- `mutationId` for replay, and `IRuntimeNetworkPublishedPortRequestV1` entries of
359
- exactly `protocol`, `hostPort` and `targetPort`: v1 accepts no `hostIp` (an
360
- omitted address means the node uplink) and no ranges. An empty list is a valid
383
+ `mutationId` for replay, and `IRuntimeNetworkPublishedPortRequest` entries of
384
+ exactly `protocol`, `hostPort` and `targetPort`: the request accepts no `hostIp`
385
+ (an omitted address means the node uplink) and no ranges. An empty list is a valid
361
386
  policy meaning the service publishes nothing. Callers never supply an
362
387
  `authorization` reference: Cloudly derives each projection entry's reference
363
388
  from the accepted policy revision and digest, and node-level host-port placement
364
389
  stays Cloudly's decision.
365
390
 
366
- `getRuntimeNetworkProjectionPacketGrantsV1` derives directed member connectivity
391
+ `getRuntimeNetworkProjectionPacketGrants` derives directed member connectivity
367
392
  and explicit local public/platform egress independently of DNS readiness.
368
- `composeRuntimeNetworkProjectionDnsViewsV1` builds one union view per local
393
+ `composeRuntimeNetworkProjectionDnsViews` builds one union view per local
369
394
  workload source address, with every authorized FQDN and complete ready-replica A
370
395
  set. An empty array declares NODATA; absent/unauthorized names remain NXDOMAIN,
371
396
  and IPv4-only service names have AAAA NODATA. The view carries the existing
@@ -384,16 +409,16 @@ asynchronous validation and output retains projection order. This lets Pallet
384
409
  compose only its currently attached sources without duplicating membership rules;
385
410
  the references themselves do not prove native attachments or DNS authority.
386
411
 
387
- The optional `IRuntimeNetworkProjectionV1.router` member carries explicit
388
- router-origin egress selections and withdrawals. Existing persisted V1
389
- projections omit it, retain their exact canonical bytes/digest/signature and grant
390
- no router-origin flows. New issuers can select `resolverIds` and
412
+ The optional `IRuntimeNetworkProjection.router` member carries explicit
413
+ router-origin egress selections and withdrawals. A projection that omits it
414
+ retains its exact canonical bytes, digest and signature and grants no
415
+ router-origin flows. New issuers can select `resolverIds` and
391
416
  `platformEndpointIds` from the current protected inventory; an empty selection
392
417
  denies all router egress. DNS forwarding policy, workload egress and inventory
393
418
  presence alone supply no router grant. The whole member is signed with the
394
419
  projection; it is not an unsigned runtime option.
395
420
 
396
- `getRuntimeNetworkProjectionRouterPacketGrantsV1` validates the complete projection
421
+ `getRuntimeNetworkProjectionRouterPacketGrants` validates the complete projection
397
422
  and returns up to 96 detached exact grants. Each binds the current local handoff
398
423
  reference and its router transit source address to a selected destination ID,
399
424
  IPv4 address, protocol and port. Resolver selection explicitly authorizes both
@@ -413,7 +438,7 @@ must compose both workload and router grants and their separate withdrawals;
413
438
  they must also fence the actual native source, routing and process lifetimes.
414
439
  DNS expiry or readiness changes do not revoke packet authority.
415
440
 
416
- `admitRuntimeNetworkProjectionV1` checks exact predecessor/replay identity and
441
+ `admitRuntimeNetworkProjection` checks exact predecessor/replay identity and
417
442
  rejects changed same-generation content, regressed membership/readiness evidence,
418
443
  changed immutable address/DNS keys, and omitted packet grants without explicit
419
444
  withdrawal. Pending withdrawals/tombstones must carry forward until the caller
@@ -422,7 +447,7 @@ application receipt. That reference is never taken from an incoming ACK. Keep
422
447
  the accepted history and apply journal until native effects and DNS/allocation
423
448
  quarantine are settled; a complete new snapshot does not erase that history.
424
449
 
425
- `IRuntimeNetworkSigningAuthorityV1` is separate from human JWT signing. Install
450
+ `IRuntimeNetworkSigningAuthority` is separate from human JWT signing. Install
426
451
  its current revision through the existing authenticated physical connection and
427
452
  commit it with a durable CAS fence. Newly enrolled nodes may bootstrap the
428
453
  controller's current key generation; existing trust cannot be reset to do so.
@@ -432,9 +457,12 @@ requires newly signed authority for DNS. It does not deny outstanding packets
432
457
  or permit allocation reuse.
433
458
 
434
459
  The Node-only `@serve.zone/interfaces/runtime` export supplies
435
- `createRuntimeNetworkSigningKeyV1`, `signRuntimeNetworkProjectionV1` and
436
- `verifyRuntimeNetworkProjectionV1`. The signature covers the complete canonical
437
- projection, signing-authority reference and dedicated signature domain. Verification
460
+ `createRuntimeNetworkSigningKey`, `signRuntimeNetworkProjection` and
461
+ `verifyRuntimeNetworkProjection`. Signing produces
462
+ `ISignedRuntimeNetworkProjection { authority, projection, signature }`, and the signature
463
+ covers the canonical envelope `{ domain, authority, projection }` under the dedicated
464
+ `serve.zone/runtime-network-projection-signature` domain, so it binds the complete
465
+ projection and the exact signing-authority revision. Verification
438
466
  requires receiver-owned trusted authority and node scope; an envelope contains
439
467
  no key to trust. Private KeyObjects stay process-local; Cloudly persists their
440
468
  exported material only through its encrypted internal secret store. The dedicated
@@ -442,17 +470,17 @@ exported material only through its encrypted internal secret store. The dedicate
442
470
  peer. Their response acknowledges durable admission only, never application,
443
471
  readiness, independent fencing or pool activation.
444
472
 
445
- The `getRuntimeManagedVpnCredentialV1` method contract in `requests.runtimesession`
446
- uses `IGetRuntimeManagedVpnCredentialRequestV1` and
447
- `IGetRuntimeManagedVpnCredentialResponseV1`. Its request carries the exact current
473
+ The `getRuntimeManagedVpnCredential` method contract in `requests.runtimesession`
474
+ uses `IGetRuntimeManagedVpnCredentialRequest` and
475
+ `IGetRuntimeManagedVpnCredentialResponse`. Its request carries the exact current
448
476
  session and signed projection reference. The secret response repeats those
449
477
  bindings and supplies a QUIC relay tuple, managed authority ID, Noise server
450
478
  public key, client keypair and expiry in Unix milliseconds. Keep the response
451
479
  process-local; never persist, hash, log or embed it in a signed projection.
452
480
 
453
- `snapshotGetRuntimeManagedVpnCredentialRequestV1` and its response counterpart
481
+ `snapshotGetRuntimeManagedVpnCredentialRequest` and its response counterpart
454
482
  capture closed detached JSON shapes and canonical 32-byte key encodings.
455
- `bindGetRuntimeManagedVpnCredentialRequestV1(request, projection, trustedSession)`
483
+ `bindGetRuntimeManagedVpnCredentialRequest(request, projection, trustedSession)`
456
484
  validates the complete projection digest and binds node, controller, namespace,
457
485
  physical-session identity and projection reference. The response binder takes
458
486
  `(response, request, projection, trustedSession, now, renewal = null)` and additionally requires
@@ -557,6 +585,14 @@ and every window against its clock, when it admits it. The run must have:
557
585
  verifies no signature. `bindHoldRuntimeNetworkDnsLeaseRenewalsResponse(response, sentRequest)`
558
586
  requires `held` to equal the number of renewals sent.
559
587
 
588
+ A hold is the one call whose outcome the controller cannot read back from its own records, so the
589
+ refusal is the whole answer. `data.holdRuntimeNetworkDnsLeaseRenewalsRefusals` is the frozen list
590
+ of names a relay leads its refusal with — `escrow-run-invalid`, `escrow-node-not-carried`,
591
+ `escrow-run-foreign-session`, `escrow-run-stale`, `escrow-run-unordered` and `escrow-cloudly-only`
592
+ — with `data.THoldRuntimeNetworkDnsLeaseRenewalsRefusal` as their union. The relay writes
593
+ `<name>: <reason>` and the controller matches the name, so neither side keeps its own copy of the
594
+ vocabulary.
595
+
560
596
  The horizon is also the revocation latency: a detached cluster keeps DNS authority until its last
561
597
  held window ends. So a controller must not release an address or a DNS name before the latest
562
598
  `expiresAt` it has signed for that node. Renewals extend DNS only. They grant no packet authority
@@ -568,7 +604,7 @@ node may use: the admitted renewal's if there is one, else the projection's own.
568
604
  bare `IRuntimeNetworkDnsLeaseRenewal` that admission takes and returns, which is what a node
569
605
  stores; Cloudly passes `signed.renewal`. It checks digests and that the renewal names exactly that
570
606
  projection, but verifies no signature. Controller and node use it for every limit tied to the
571
- window, and `bindGetRuntimeManagedVpnCredentialResponseV1` takes the same bare admitted renewal as
607
+ window, and `bindGetRuntimeManagedVpnCredentialResponse` takes the same bare admitted renewal as
572
608
  an optional last argument, so a newly issued managed-VPN credential may run to the end of the
573
609
  renewed window instead of the projection's original one.
574
610
 
@@ -604,7 +640,7 @@ relay uses the managed-VPN credential's `{ id, address, port }`. `platformEndpoi
604
640
  relay: the composed authority adds it, in id order, as the UDP platform endpoint every node dials.
605
641
 
606
642
  `snapshotRuntimeNetworkAddressPlan` refuses any plan that could describe an authority the authority
607
- contract refuses. It runs `snapshotRuntimeNetworkProtectedAuthorityV1` twice, once over the plan's
643
+ contract refuses. It runs `snapshotRuntimeNetworkProtectedAuthority` twice, once over the plan's
608
644
  own sets and once with the relay added, so overlapping pools, more than 16 resolvers, a resolver or
609
645
  endpoint outside `prefixes` or inside a pool, and a relay that `prefixes` do not cover are all
610
646
  refused by that one validator. On top it requires the VPN `controlPrefix` to be a private prefix no
@@ -652,15 +688,25 @@ producer reserves the router a fresh handoff, and answers with the new incarnati
652
688
 
653
689
  ### Node-Bound Runtime Assignments
654
690
 
655
- The `data` namespace exports `IRuntimeAssignmentV1`, canonical digest helpers,
656
- `evaluateRuntimeAssignmentV1`, and assignment observation validators/evaluators
691
+ The `data` namespace exports `IRuntimeAssignment`, canonical digest helpers,
692
+ `evaluateRuntimeAssignment`, and assignment observation validators/evaluators
657
693
  for Cloudly/Onebox controllers and Pallet nodes. These are shared contracts;
658
694
  they do not install a runtime, schedule containers or expose an RPC endpoint.
659
695
 
660
696
  Each assignment binds a controller incarnation, organization, stable node and
661
697
  replica IDs, one immutable execution attempt, image/configuration and opaque
662
698
  network/storage/secret references. Group and site IDs are nullable topology
663
- provenance. All fields except the digest itself participate in a domain-separated
699
+ provenance. Those workload references are also the authority vocabulary:
700
+ `data.runtimeWorkloadAuthorities` is `['network', 'secrets', 'storage']`, and
701
+ `data.requiredWorkloadAuthorities(workload)` names the ones this workload cannot
702
+ run without — filtered out of that vocabulary and sorted, so the answer is
703
+ duplicate-free and compares byte for byte against the `resolvedAuthorities` a node
704
+ stated when it registered. `data.isResolvedWorkloadAuthorities(value)` is the same
705
+ rule read the other way, for a receiver checking a list it was sent. Canonical is
706
+ strictly increasing in the code-unit order of the names themselves — a rule a peer
707
+ in another language implements from the vocabulary alone, rather than from the
708
+ order this package happens to declare it in — and both directions compare through
709
+ that one rule, so neither can drift from the other. All fields except the digest itself participate in a domain-separated
664
710
  SHA-256 digest. Digests provide integrity and compare-and-swap identity;
665
711
  authentication must separately establish the trusted controller/node scope.
666
712
 
@@ -686,10 +732,10 @@ does not authorize storage relocation or prove fencing.
686
732
 
687
733
  ### Resolved Runtime Configuration
688
734
 
689
- `data.IRuntimeConfigV1` seals the public environment, working directory, resource
735
+ `data.IRuntimeConfig` seals the public environment, working directory, resource
690
736
  and identity policies, target ports, readiness timing and log limits together with
691
- the immutable image invocation. `computeRuntimeConfigV1Digest` hashes that exact
692
- content; `bindRuntimeConfigV1ToAssignment` checks the config reference, service,
737
+ the immutable image invocation. `computeRuntimeConfigDigest` hashes that exact
738
+ content; `bindRuntimeConfigToAssignment` checks the config reference, service,
693
739
  organization, image and selected platform against one assignment. Secret material,
694
740
  registry credentials and host paths remain outside the configuration.
695
741
 
@@ -704,14 +750,14 @@ Existing numeric configurations retain their exact meaning and digest.
704
750
 
705
751
  ### Authenticated Runtime Sessions
706
752
 
707
- `requests.runtimesession` defines `registerPalletRuntimeSessionV1`,
708
- `applyRuntimeAssignmentV1`, `reportRuntimeAssignmentObservationV1`, and
709
- `getRuntimeRegistryCredentialV1`. Pallet initiates WSS through its configured
753
+ `requests.runtimesession` defines `registerPalletRuntimeSession`,
754
+ `applyRuntimeAssignment`, `reportRuntimeAssignmentObservation`, and
755
+ `getRuntimeRegistryCredential`. Pallet initiates WSS through its configured
710
756
  Cloudly HTTPS origin, or, on a relayed cluster, through the relay origin its
711
757
  cluster publishes in [`ICluster.data.relay`](#cluster-relay-block) while Cloudly
712
758
  remains the credential authority. The receiver derives the physical peer, process instance
713
759
  and registration idempotency; callers cannot supply those authorities. A serialized
714
- `IRuntimeSessionBindingV1` provides content to compare with authenticated peer
760
+ `IRuntimeSessionBinding` provides content to compare with authenticated peer
715
761
  state and does not itself authenticate a connection. Registry credentials are
716
762
  ephemeral, bounded and authorized against an exact current `run` assignment.
717
763
 
@@ -726,14 +772,31 @@ classify content; authentication and atomic receipt persistence remain server-ow
726
772
 
727
773
  A registration is identified by its node, its credential generation and
728
774
  `nodeInstanceNonce`: a canonical lowercase UUID that Pallet generates once per
729
- process start and sends on every registration. Version 31.1 adds that optional
730
- field. One physical socket may carry the registrations of many nodes — a cluster
731
- relay forwards a whole cluster — so the transport alone can no longer tell a
732
- node restart from a replay. A controller must require the nonce whenever the
733
- registering peer is such a relay, and may accept its absence only from a
734
- directly connected node, whose own socket names its process. The nonce is not a
735
- secret, is not authentication, and grants nothing on its own: the bearer still
736
- authenticates every registration.
775
+ process start and sends on every registration. From 32.0.0 the field is required,
776
+ so a controller no longer has a nonce-less case to decide. One physical socket may
777
+ carry the registrations of many nodes — a cluster relay forwards a whole cluster —
778
+ so the transport alone can no longer tell a node restart from a replay. The nonce
779
+ is not a secret, is not authentication, and grants nothing on its own: the bearer
780
+ still authenticates every registration.
781
+
782
+ The registration also states `resolvedAuthorities`: which of
783
+ `data.runtimeWorkloadAuthorities` (`network`, `secrets`, `storage`) this node
784
+ build can resolve, sorted and duplicate-free so a replay compares byte for byte.
785
+ It is a capability of the registering build, never a permission: a controller
786
+ refuses an assignment whose `data.requiredWorkloadAuthorities(workload)` the list
787
+ does not cover, instead of a node accepting work it cannot carry out. It is not
788
+ part of the registration identity — a changed build is a new process and
789
+ therefore a new nonce — so a replay that states different authorities is a
790
+ conflict rather than an update.
791
+
792
+ Every registration carries `protocol`, and the controller judges the body in the
793
+ one server order `protocol.readProtocolOffer` states: the bounded body, the offer,
794
+ `protocol.negotiateProtocol` against this session kind's own offer, and only then
795
+ the exact-key validator, the bearer and any mutation. For a node that order is what
796
+ keeps a refused registration from touching its packet access.
797
+ `registerPalletRuntimeSession` answers `{ session, protocol }`, and
798
+ `data.bindRegisterPalletRuntimeSessionResponse` binds that answer to the request
799
+ the node sent and negotiates the two offers from the node's side.
737
800
 
738
801
  A changed nonce, or a changed credential generation, is a new registration: the
739
802
  controller mints a new binding and a new session epoch and fences the node's
@@ -743,15 +806,16 @@ transport reconnect: a node that rotates behind a relay registers again with its
743
806
  unchanged nonce and its new generation on the same live socket, and the other
744
807
  nodes that socket carries are untouched.
745
808
 
746
- `registerCloudlyClientSessionV1` separately authenticates a current human or
747
- machine JWT on a physical TypedSocket so the server can own its identity tag.
748
- Both registration methods reject HTTP transport. Sensitive requests and registry
809
+ `registerCloudlyClientSession` separately authenticates a current human or
810
+ machine JWT on a physical TypedSocket so the server can own its identity tag; it
811
+ carries the same offer, judged in the same order before the JWT, and answers with
812
+ the server's own offer. Both registration methods reject HTTP transport. Sensitive requests and registry
749
813
  responses must be excluded from hooks, logs, diagnostics and durable journals.
750
814
 
751
815
  ### Joined Runtime Terminal Receipts
752
816
 
753
- `data.IRuntimeAssignmentTerminalReceiptV1` and
754
- `requests.runtimesession.reportRuntimeAssignmentTerminalReceiptV1` carry a separate
817
+ `data.IRuntimeAssignmentTerminalReceipt` and
818
+ `requests.runtimesession.reportRuntimeAssignmentTerminalReceipt` carry a separate
755
819
  receipt for the exact Pallet attempt's joined terminal execution. Sequence 1 binds
756
820
  the generation 2 stop assignment; sequence 2 binds generation 3 removal and the
757
821
  exact stop receipt digest. Both retain the original generation 1 run reference.
@@ -769,7 +833,7 @@ terminal operation but cannot skip the stop/remove chain.
769
833
 
770
834
  The synchronous snapshot helpers validate bounded exact data only. Digest,
771
835
  assignment-chain and receipt-chain helpers do not prove native execution or
772
- authenticate a reporter. `bindRuntimeReporterSessionV1` and the terminal report
836
+ authenticate a reporter. `bindRuntimeReporterSession` and the terminal report
773
837
  binder require the current physical session and immutable server-indexed reporter
774
838
  history from their trusted owners. Reconnect recovery preserves every original
775
839
  receipt field and returns `historical-recovery`; it never restores active
@@ -847,26 +911,13 @@ through every slot on a cluster's nodes; `limit` runs from 1 to
847
911
  `requests.service.pushServiceRuntimeChanged { serviceId }` tells admin UIs that a service's spec,
848
912
  status or assignments changed; it carries no state, so a late push never overwrites a newer read.
849
913
 
850
- ### Offline Database Settlement And Packaging
851
-
852
- The Node-only `@serve.zone/interfaces/runtime` export includes isolated V1
853
- metadata and control codecs for describing and validating settlement of one
854
- immutable `smartdb.database.export.v1` file offline. The protocol binds exact
855
- source bytes to source authority A, an
856
- HMAC-authenticated rehearsal, a distinct later production-transform authority
857
- B, an HMAC-authenticated transform authorization, exact transformed-core bytes,
858
- a deterministic HMAC-authenticated terminal journal, exact complete output
859
- bytes, an HMAC-authenticated complete-output receipt, and a bounded result.
860
- The journal ID seed excludes `_id` and `mac`; its HMAC input excludes only
861
- `mac`. Only the external complete-output receipt identifies the final output
862
- bytes. These contracts make no transaction or committed-write claim and leave
863
- the existing transaction settlement contracts unchanged. They neither carry
864
- the export bytes or Base64 content nor execute the settlement.
865
-
866
- The same runtime export provides metadata-only Corestore database-export
867
- package contracts. A package request carries exact input length and SHA-256,
868
- approved source-backup and complete-output-receipt digests, a path-safe attempt
869
- ID, and an explicit `absent` or `exact` allocation expectation. It never carries
914
+ ### Corestore Database Export Packaging
915
+
916
+ The Node-only `@serve.zone/interfaces/runtime` export provides metadata-only
917
+ Corestore database-export package contracts. A package request carries exact
918
+ input length and SHA-256, approved source-backup and complete-output-receipt
919
+ digests, a path-safe attempt ID, and an explicit `absent` or `exact` allocation
920
+ expectation. It never carries
870
921
  the export bytes or Base64 content. Immutable completion binds the normal
871
922
  Corestore database backup receipt and exact closure identity; delivery wrappers
872
923
  may report exact replay without changing completion. Release is permitted only
@@ -874,10 +925,18 @@ against the caller's exact fsynced closure and receipt identities. API-token
874
925
  authentication remains transport authority, and these Corestore contracts do
875
926
  not claim to verify Cloudly authority-B HMACs.
876
927
 
877
- Exact source, transformed-core, and complete-output identities include one
878
- terminating LF and are limited to 96 MiB plus that LF. Journal-induced output
879
- growth is limited to 64 KiB. Package control metadata is limited to 16 KiB and
880
- keeps the existing 256 MiB Corestore closure limit.
928
+ Exact input identities include one terminating LF and are limited to 96 MiB
929
+ plus that LF. Package control metadata is limited to 16 KiB and keeps the
930
+ existing 256 MiB Corestore closure limit.
931
+
932
+ One media type and one backup format are stated here for every process that
933
+ handles a Corestore database backup: `runtime.corestoreDatabaseBackupMediaType`
934
+ (`application/vnd.serve-zone.corestore-database-backup`) is the type an archive
935
+ is served and stored under, and `runtime.corestoreDatabaseBackupFormat`
936
+ (`corestore.database.backup`) is the format a receipt states. Corestore's
937
+ control server, its export packager, Onebox and the cluster relay import them
938
+ instead of writing the bytes again, so the side that writes a transfer and the
939
+ side that reads it cannot disagree about its type.
881
940
 
882
941
  Service creation carries canonical ownership separately from caller-writable
883
942
  service data. `requests.service.IRequest_Any_Cloudly_CreateService` requires a
@@ -886,13 +945,97 @@ server-managed ownership field. Consumers should use
886
945
  `data.validateOrganizationId()` to validate the identifier shape and verify
887
946
  the request's ownership authorization separately before persistence.
888
947
 
948
+ ## Protocol handshake
949
+
950
+ The protocol version is the installed `@serve.zone/interfaces` release. There is no protocol
951
+ number beside it, no per-shape version suffix and no `schemaVersion` field: one release of this
952
+ package is exactly one contract, so naming the release states everything about what a peer sends
953
+ and what it can read.
954
+
955
+ Two peers exchange an offer when a session opens and refuse an incompatibility by name:
956
+
957
+ ```typescript
958
+ import { protocol } from '@serve.zone/interfaces';
959
+ import { TypedResponseError } from '@api.global/typedrequest';
960
+
961
+ // One offer per session kind a deployable serves, built once at start-up. A deployable that
962
+ // depends on nothing newer than its own major accepts that whole major.
963
+ const [installedMajor] = protocol.protocolVersion.split('.');
964
+ const ownOffer = protocol.createProtocolOffer(`${installedMajor}.0.0`);
965
+
966
+ // Server, on every carrier: the bounded body, then the offer, then everything else — the
967
+ // exact-key validator, the bearer and any mutation.
968
+ const peerOffer = protocol.readProtocolOffer(requestBody);
969
+ const negotiation = protocol.negotiateProtocol(ownOffer, peerOffer);
970
+ if (!negotiation.compatible) {
971
+ throw new TypedResponseError(
972
+ protocol.describeProtocolRefusal(negotiation.refusal),
973
+ negotiation.refusal,
974
+ );
975
+ }
976
+
977
+ // Client, on the payload of a failed hop: `TypedResponseError.errorData` on a TypedRequest, the
978
+ // parsed body on the Spark heartbeat's refusal status. Everything else is not a refusal.
979
+ const refusal = protocol.readProtocolRefusal(errorData);
980
+ if (refusal) {
981
+ logger.warn(protocol.describeProtocolRefusal(refusal));
982
+ // A refusal stands until an operator upgrades one of the two sides, so the client waits.
983
+ setTimeout(offerAgain, protocol.refusedOfferRetryIntervalMs);
984
+ }
985
+ ```
986
+
987
+ | Export | Purpose |
988
+ | --- | --- |
989
+ | `protocolVersion` | The installed release, taken from the package's own commit info. |
990
+ | `IProtocolOffer` | `{ interfacesVersion; minimumPeerVersion }` — what a side speaks and the oldest peer it accepts. |
991
+ | `createProtocolOffer(minimumPeerVersion)` | This build's offer. A minimum above the installed release, or in another major, throws here — at start-up, not at the first registration. |
992
+ | `validateProtocolOffer(value, path?)` | Every reason an offer is refused, as an array of messages; empty for a valid offer. |
993
+ | `validateProtocolOfferOn(body, path?)` | The same reasons for the `protocol` key of a body, read as a data property. The naming twin of `readProtocolOffer`, for validators that list reasons instead of throwing. |
994
+ | `readProtocolOffer(body)` | The offer on a body, read from the `protocol` key alone and returned detached. |
995
+ | `negotiateProtocol(local, remote)` | `{ compatible: true }` or `{ compatible: false; refusal }`. Pure; throws when either side's offer names something that is not a release version. |
996
+ | `IProtocolRefusal` | `{ code: 'protocol-incompatible'; reason; refusing; refused }`, carried as `TypedResponseError.errorData` or as a plain HTTP JSON body. |
997
+ | `TProtocolIncompatibility` | `'major-mismatch'`, `'peer-below-minimum'`, `'self-below-peer-minimum'`. |
998
+ | `readProtocolRefusal(value)` | The refusal inside an error payload (`TypedResponseError.errorData`) or a response body, or `null` when the value is not one. |
999
+ | `describeProtocolRefusal(refusal)` | One log line naming both sides, opening with the frozen `protocol-incompatible` code. |
1000
+ | `refusedOfferRetryIntervalMs` | `300_000` — the shared cadence at which a refused client offers again. |
1001
+
1002
+ `negotiateProtocol` checks in a fixed order: different majors are `major-mismatch` even when a
1003
+ minimum would also fail, then a remote below the local minimum is `peer-below-minimum`, then a
1004
+ local below the remote minimum is `self-below-peer-minimum`. The last two are mirror images of one
1005
+ another — one holding on this side means the other holds on the peer — and they can never hold at
1006
+ once, which would require each version to be below the other. An offer that does not name two
1007
+ release versions is a local defect, not an incompatibility between two peers: `negotiateProtocol`
1008
+ throws on it, and a peer-sent body is read with `readProtocolOffer` first, which refuses it by
1009
+ name. Both offers and refusals come back frozen and detached from the values they were read from.
1010
+
1011
+ **Every carrier reads the offer once, as data.** A `protocol` key, or a member of the offer under
1012
+ it, that is an accessor rather than a plain value is refused as "must use its exact schema" and is
1013
+ never invoked — by `readProtocolOffer`, by `validateProtocolOfferOn`, by `validateProtocolOffer`
1014
+ and by `readProtocolRefusal` alike. No snapshot, validator or client on any carrier runs code a
1015
+ peer attached to a body, and none of them can observe one value while a session is served by
1016
+ another. This is stated here and nowhere else: the families that carry an offer go through these
1017
+ functions rather than restating the rule.
1018
+
1019
+ A carrier that negotiates puts the offer in a top-level `protocol` key on both its request and its
1020
+ response, and a server reads it before it validates the rest of the body, authenticates or changes
1021
+ any state. Validators in this package are exact-key, so a body from a later major is unreadable by
1022
+ an older build; reading the offer first is what turns that into a named refusal instead of a
1023
+ generic contract rejection, and it keeps a refused peer's credentials, sessions and packet access
1024
+ untouched.
1025
+
1026
+ Versions are `MAJOR.MINOR.PATCH` and nothing else — no prerelease, no build metadata, no `v`
1027
+ prefix, no leading zeros — and are compared per numeric component, so `32.10.0` is above `32.9.0`.
1028
+ `minimumPeerVersion` is in the same major as `interfacesVersion` and never above it. Each
1029
+ deployable owns its minimums, one per session kind it serves, and raises one only in the commit
1030
+ that starts depending on a later interfaces minor, for that session kind alone.
1031
+
889
1032
  ## Secrets v24 Pre-Cutover Contract
890
1033
 
891
1034
  Version 24 retains the value-free v23 architecture while replacing the remaining
892
1035
  pre-cutover lifecycle and runtime authority gaps. Secret values enter Cloudly
893
1036
  only as strict SmartCrypto X25519 envelopes or an explicit bounded
894
- server-generation request. Coreflow receives a full digest-verified schema-v2
895
- manifest plus one sealed envelope per pinned SecretVersion through the Node-only
1037
+ server-generation request. Coreflow receives a full digest-verified manifest
1038
+ plus one sealed envelope per pinned SecretVersion through the Node-only
896
1039
  runtime export.
897
1040
 
898
1041
  This is a contract release before consumer cutover. It does **not** claim that
@@ -916,7 +1059,7 @@ The current secret contract includes:
916
1059
  `requests.secret.IReq_GetSecretIngressRecipient` contract with method
917
1060
  `getSecretIngressRecipient`, and fixed-order create/rotate/App Store context
918
1061
  builders.
919
- - `ISecretEnvelopeAdmissionBindingV1` under the Node-only runtime export binds
1062
+ - `ISecretEnvelopeAdmissionBinding` under the Node-only runtime export binds
920
1063
  an exact envelope and request context to one recipient generation. The
921
1064
  binding is reproducible and does not itself prove admission. Retiring-key
922
1065
  retries require a trusted Cloudly mutation receipt created atomically while
@@ -928,12 +1071,12 @@ The current secret contract includes:
928
1071
  non-issuable `purge-pending` lifecycle and revisioned
929
1072
  `pending`/`erasing`/`failed`/`succeeded` operation keep external erasure
930
1073
  durable across retries without treating preflight as authorization.
931
- - Schema-v2 `IResolvedSecretManifest` contracts, stable Docker resource naming,
1074
+ - `IResolvedSecretManifest` contracts, stable Docker resource naming,
932
1075
  WorkloadInit map/wrapper helpers, and `ISealedResolvedSecretMaterial` under
933
1076
  `@serve.zone/interfaces/runtime`.
934
1077
  - Two-step Coreflow X25519 recipient enrollment and exact recipient lifecycle
935
1078
  validators.
936
- - Mandatory `IImmutableContainerInvocationV1` evidence on immutable image
1079
+ - Mandatory `IImmutableContainerInvocation` evidence on immutable image
937
1080
  deployment plans.
938
1081
 
939
1082
  ### Secrets v24 Runtime Registration And Reporting
@@ -946,7 +1089,7 @@ Coreflow connection:
946
1089
  empty state or the complete valid recipient set for the cluster derived from
947
1090
  the verified JWT.
948
1091
  - `getCoreflowSecretRuntimeRegistrationExpectation` returns either an
949
- `available` v2 expectation or an explicit unavailable reason. The available
1092
+ `available` expectation or an explicit unavailable reason. The available
950
1093
  expectation binds the live reporter session, active recipient, fresh
951
1094
  generation-fenced target authority, and active WorkloadInit approval through
952
1095
  `expectationDigest`.
@@ -955,17 +1098,19 @@ Coreflow connection:
955
1098
  credential, reconciles manager consensus privately, and publishes one fresh
956
1099
  single-Swarm target authority or a targetless unavailable state. Structural
957
1100
  contract validation does not itself establish manager consensus.
958
- - `TSparkSwarmObservationV2` lets workers report only their local Swarm node ID,
1101
+ - `TSparkSwarmObservation` lets workers report only their local Swarm node ID,
959
1102
  because Docker does not expose the Swarm cluster ID to workers. Cloudly may
960
1103
  associate that node with a cluster only through authenticated node scope and
961
- accepted manager consensus. The v2 transport contract designates
962
- `/spark/v2/nodes/swarm-observation`. A conforming Spark sender must retain one
963
- exact request until it receives a request-bound acceptance receipt. A
964
- conforming Cloudly consumer must authenticate the node before inspecting
965
- replay state, derive cluster scope from that persisted identity, and never
966
- register, translate, or accept v1 observations as an authority fallback.
967
- - `computeSparkSwarmObservationV2Digest` uses strict canonical JSON and the
968
- `serve.zone/spark-swarm-observation-v2` domain. A reporter session starts at
1104
+ accepted manager consensus. The transport contract designates
1105
+ `sparkSwarmNodeContracts.swarmObservation`, whose endpoint is
1106
+ `/spark/swarm-nodes/swarm-observation` and whose `maxRequestBytes` covers the
1107
+ largest snapshot this validator accepts; every body on it carries the sender's
1108
+ `protocol` offer. A conforming Spark sender must retain one exact
1109
+ request until it receives a request-bound acceptance receipt. A conforming
1110
+ Cloudly consumer must authenticate the node before inspecting replay state and
1111
+ derive cluster scope from that persisted identity.
1112
+ - `computeSparkSwarmObservationDigest` uses strict canonical JSON and the
1113
+ `serve.zone/spark-swarm-observation` domain. A reporter session starts at
969
1114
  sequence one. The contract requires a Cloudly consumer to accept only the
970
1115
  exact next sequence with a strictly advancing `observedAt`, while an exact
971
1116
  same-sequence/same-digest retry returns the byte-equivalent persisted receipt
@@ -975,25 +1120,42 @@ Coreflow connection:
975
1120
  sequence-one observation must still advance the permanent timestamp
976
1121
  high-water mark. The consumer must persist acceptance state and its receipt
977
1122
  in one atomic boundary.
978
- - `validateSparkSwarmObservationV2` validates exact structure and attached
979
- snapshots, while `validateSparkSwarmObservationV2Request` and
980
- `validateSparkSwarmObservationV2Response` validate transport and request
1123
+ - `validateSparkSwarmObservation` validates exact structure and attached
1124
+ snapshots, while `validateSparkSwarmObservationRequest` and
1125
+ `validateSparkSwarmObservationResponse` validate transport and request
981
1126
  binding. None of these functions authenticates a node, persists replay state,
982
1127
  establishes manager consensus, or creates runtime authority.
983
1128
  - WorkloadInit approval binds a clean stable release identity, version-tagged
984
1129
  OCI index, exact amd64 and arm64 platform/executable digests, policy
985
1130
  generation, and a Cloudly summary of detached Cosign DSSE/SLSA verification.
986
1131
  Public shape and digest validators do not verify the signature, public-key
987
- trust root, or private Cloudly policy.
1132
+ trust root, or private Cloudly policy. The provenance identifiers a verifier
1133
+ matches are `workloadInitReleaseContract.buildType`
1134
+ (`https://serve.zone/buildtypes/workloadinit-gitzone-tsdocker`) and
1135
+ `builderId` (`https://serve.zone/builders/workloadinit-manual-release`); the
1136
+ in-toto statement type, the SLSA predicate type, the Cosign bundle format and
1137
+ the OCI index media type keep the exact bytes their own specifications define.
1138
+ An approval carries no shape version, and
1139
+ `IWorkloadInitApprovalAuthorityReference` states only the counter
1140
+ `authorityGeneration` beside the approval digest it points at.
988
1141
  - `coreflowSecretRuntimeRegistrationTagId` is the sole dedicated TypedSocket
989
1142
  tag identifier. Its payload is exactly
990
- `ICoreflowSecretRuntimeRegistrationV2`; cluster scope comes from the verified
1143
+ `ICoreflowSecretRuntimeRegistration`; cluster scope comes from the verified
991
1144
  connection identity rather than the tag.
992
- - `ICoreflowSecretRuntimeRegistrationV2` references the exact expectation,
1145
+ - `ICoreflowSecretRuntimeRegistration` references the exact expectation,
993
1146
  target generation/digest, and WorkloadInit authority. It must cover every
994
1147
  Cloudly-node/Swarm-cluster/Swarm-node identity and approved per-platform
995
1148
  manifest and installed-executable digest without self-asserting placement or
996
1149
  approval.
1150
+ - `capabilities` is a sorted, duplicate-free list of `data.TCoreflowRuntimeCapability`
1151
+ names drawn from `data.coreflowRuntimeCapabilities`, judged by
1152
+ `data.isCoreflowRuntimeCapabilityList`. A build states what it can do by name;
1153
+ which shape it speaks is the interfaces version both peers exchanged. Every
1154
+ name in `data.requiredCoreflowRuntimeCapabilities` must be present. That list
1155
+ is the vocabulary minus `data.optionalCoreflowRuntimeCapabilities`, which
1156
+ holds `corestore-inventory` alone: it is the one capability a build may lack,
1157
+ because it depends on the node's Corestore deployment rather than on the
1158
+ contracts the build was compiled against.
997
1159
  - `validateCoreflowSecretRuntimeRegistration` compares the registration with a
998
1160
  trusted expectation built by Cloudly, including the live reporter session.
999
1161
  Missing, extra, duplicate, reordered, or mismatched node evidence fails
@@ -1075,7 +1237,7 @@ const storageConfig: appstore.IAppStoreVersionConfig = {
1075
1237
  delivery: {
1076
1238
  type: 'file',
1077
1239
  targetPath: '/run/secrets/backup-archive.json',
1078
- format: 'servezone-object-storage-v1',
1240
+ format: 'servezone-object-storage',
1079
1241
  uid: 1000,
1080
1242
  gid: 1000,
1081
1243
  mode: 0o400,
@@ -1084,10 +1246,10 @@ const storageConfig: appstore.IAppStoreVersionConfig = {
1084
1246
  },
1085
1247
  ],
1086
1248
  requiresFeatures: [
1087
- appstore.appStoreStorageFeatureIds.bindingsV2,
1088
- appstore.appStoreStorageFeatureIds.filesystemV1,
1089
- appstore.appStoreStorageFeatureIds.objectStorageV2,
1090
- appstore.appStoreStorageFeatureIds.objectStorageFileV2,
1249
+ appstore.appStoreStorageFeatureIds.bindings,
1250
+ appstore.appStoreStorageFeatureIds.filesystem,
1251
+ appstore.appStoreStorageFeatureIds.objectStorage,
1252
+ appstore.appStoreStorageFeatureIds.objectStorageFile,
1091
1253
  ],
1092
1254
  };
1093
1255
  ```
@@ -1311,7 +1473,7 @@ Common data contracts include:
1311
1473
  - `IDomain`, `IDnsEntry`, and traffic contracts for routing and DNS management.
1312
1474
  - Traffic and gateway route contracts including `ICoretrafficPortRouteConfig`, routing `portRoutes`, and `IGatewayClientRoute` client-owned route views. Gateway route intent supports optional match `domains`, `transport`, and `remoteIngress`, plus explicit route `priority` and `managedRouteKind`. Ownership can combine `hostname` with `routeRef` so a normal route and a path-specific managed route for the same hostname reconcile independently.
1313
1475
  - Value-free `ISecretMetadata`, `ISecretVersionMetadata`, and `ISecretSetMetadata`
1314
- contracts for operator views, plus schema-v2 exact-version
1476
+ contracts for operator views, plus exact-version
1315
1477
  `IResolvedSecretManifest` contracts for cluster delivery. Manifest helpers
1316
1478
  bind immutable image rollout and invocation evidence, enforce canonical
1317
1479
  digests and globally unique launcher/file targets, and track per-cluster
@@ -1333,7 +1495,7 @@ Common data contracts include:
1333
1495
 
1334
1496
  ### Cluster Relay Block
1335
1497
 
1336
- `ICluster.data.relay` is the optional `IClusterRelayV1 { schemaVersion: 1; origin: string }`
1498
+ `ICluster.data.relay` is the optional `IClusterRelay { origin: string }`
1337
1499
  that states how cluster-side components reach this cluster's relay. It is absent on a cluster
1338
1500
  whose nodes still target Cloudly directly, so its presence is what marks a cluster as relayed.
1339
1501
 
@@ -1342,7 +1504,7 @@ session registers over, as described in [Authenticated Runtime Sessions](#authen
1342
1504
  It must be an `https:` origin with no path, query, fragment, userinfo or trailing slash, so that
1343
1505
  `new URL(origin).origin === origin`; an explicit port is allowed, and because `URL` drops a
1344
1506
  default port and lowercases the host, `https://relay.example:443` and `https://Relay.example`
1345
- are not canonical origins. `validateClusterRelayV1(value, path?)` returns one named reason per
1507
+ are not canonical origins. `validateClusterRelay(value, path?)` returns one named reason per
1346
1508
  violation and an empty array for a valid block.
1347
1509
 
1348
1510
  The relay's certificate subject is not a second field: every consumer derives it from the origin
@@ -1350,31 +1512,41 @@ with `clusterRelayCertificateDomainName(relay)`, which returns the origin's host
1350
1512
  Cloudly issues the certificate for and owns the A record for. An origin naming an address literal
1351
1513
  therefore has no issuable certificate.
1352
1514
 
1515
+ `data.clusterRelayUndispatchedRefusals` names the three registration refusals that end a relay's
1516
+ custody — `relay-cluster-not-enabled`, `relay-node-foreign` and `relay-sequence-not-advancing` —
1517
+ with `data.TClusterRelayUndispatchedRefusal` as their union. Each is Cloudly reading its own
1518
+ records and stating that it does not dispatch to this relay, so a relay that hears one drops what
1519
+ it holds for its nodes instead of waiting. A refusal that only says Cloudly could not verify the
1520
+ relay states nothing about dispatch and is deliberately not in the list.
1521
+
1353
1522
  ```typescript
1354
- const relay: data.IClusterRelayV1 = {
1355
- schemaVersion: 1,
1523
+ const relay: data.IClusterRelay = {
1356
1524
  origin: 'https://relay.cluster-a.serve.zone:8443',
1357
1525
  };
1358
1526
 
1359
- const reasons = data.validateClusterRelayV1(relay); // []
1527
+ const reasons = data.validateClusterRelay(relay); // []
1360
1528
  const certificateDomainName = data.clusterRelayCertificateDomainName(relay);
1361
1529
  // 'relay.cluster-a.serve.zone'
1362
1530
  ```
1363
1531
 
1364
- Enabling a relay is one operator flip per cluster. `enableClusterRelayV1 { identity, clusterId }`
1365
- answers with the block Cloudly published, and `disableClusterRelayV1` removes it and answers with
1532
+ Enabling a relay is one operator flip per cluster. `enableClusterRelay { identity, clusterId }`
1533
+ answers with the block Cloudly published, and `disableClusterRelay` removes it and answers with
1366
1534
  an explicit `relay: null`. The caller names only the cluster: Cloudly derives the origin itself
1367
1535
  from that cluster and its own public hostname, and refuses the request when no DNS zone it
1368
1536
  manages matches the derived name, so a relay name that nothing can publish is never written.
1369
1537
  Both requests push the cluster configuration to the connected relay afterwards, so a relay learns
1370
1538
  the change without reconnecting.
1371
1539
 
1372
- Once its socket is authenticated with `registerCloudlyClientSessionV1`, the relay sends
1373
- `registerClusterRelayV1` carrying `IClusterRelayRegistrationV1`: the relay build, the cluster node
1374
- it runs on, the exact endpoint it bound for its cluster's Pallet nodes, and a
1540
+ Once its socket is authenticated with `registerCloudlyClientSession`, the relay sends
1541
+ `registerClusterRelay` carrying `IRegisterClusterRelayRequest`: the relay build, the cluster node
1542
+ it runs on, the exact endpoint it bound for its cluster's Pallet nodes, its `protocol` offer, and a
1375
1543
  `registrationSequence` that counts up once per registration within one relay process and restarts
1376
- at `0` when that process restarts. `validateClusterRelayRegistrationV1(value, path?)` returns one
1377
- named reason per violation, like every validator here, and checks shape only. `listenAddress` is
1544
+ at `0` when that process restarts. `validateRegisterClusterRelayRequest(value, path?)` returns one
1545
+ named reason per violation, like every validator here, and checks shape only; it validates the
1546
+ offer through the one grammar `protocol` owns. Cloudly reads and negotiates that offer before it
1547
+ takes the verified cluster connection or decides the sequence, so a refused registration consumes
1548
+ no sequence, and the accepted answer carries Cloudly's own offer beside the origin. `relay.version`
1549
+ stays the relay build — a different fact from `protocol.interfacesVersion`, the contract it speaks. `listenAddress` is
1378
1550
  an IPv4 literal because Cloudly publishes it verbatim as the A record of the relay's certificate
1379
1551
  domain name — a hostname would need a `CNAME` and an IPv6 address an `AAAA` record, so admitting
1380
1552
  either is an additive change once Cloudly publishes it. A loopback or link-local address is
@@ -1405,8 +1577,8 @@ present with an empty one. `validateClusterRelayRegistration(value, path?)` retu
1405
1577
  reason per violation, as every validator here does.
1406
1578
 
1407
1579
  ```typescript
1408
- const registration: data.IClusterRelayRegistrationV1 = {
1409
- schemaVersion: 1,
1580
+ const registration: data.IRegisterClusterRelayRequest = {
1581
+ protocol: protocol.createProtocolOffer('32.0.0'),
1410
1582
  relay: {
1411
1583
  version: '4.0.0',
1412
1584
  nodeId: 'node-a',
@@ -1416,7 +1588,7 @@ const registration: data.IClusterRelayRegistrationV1 = {
1416
1588
  registrationSequence: 0,
1417
1589
  };
1418
1590
 
1419
- const registrationReasons = data.validateClusterRelayRegistrationV1(registration); // []
1591
+ const registrationReasons = data.validateRegisterClusterRelayRequest(registration); // []
1420
1592
  ```
1421
1593
 
1422
1594
  How the relay reaches each node's Corestore is part of the same document: see
@@ -1450,7 +1622,7 @@ credential is excluded.
1450
1622
  | --- | --- | --- |
1451
1623
  | `pushClusterRouteTable` | Cloudly → relay | `{ table }` → `{ appliedRevision }` |
1452
1624
  | `getClusterRouteTable` | relay → Cloudly | `{}` → `{ table \| null }` |
1453
- | `registerClusterIngress` | ingress → relay → Cloudly | `{ bearer, version, nodeId, capabilities? }` → `{ accepted: true, revision \| null }` |
1625
+ | `registerClusterIngress` | ingress → relay → Cloudly | `{ bearer, version, nodeId, capabilities?, protocol }` → `{ accepted: true, revision \| null, protocol }` |
1454
1626
  | `getClusterIngressRouteTable` | ingress → relay | `{ knownRevision \| null }` → `{ table \| null }` |
1455
1627
  | `getClusterTrafficStatistics` | Cloudly → relay → ingress | `{ fromDayUtc, toDayUtc, cursor?, limit }` → `{ statistics, nextCursor? }` |
1456
1628
 
@@ -1472,7 +1644,11 @@ Isolation on that shared listener is therefore by **authorization, not by routin
1472
1644
  connection whose registration Cloudly accepted is the ingress, `getClusterIngressRouteTable` and
1473
1645
  `getClusterTrafficStatistics` are answered for that connection alone, and any other connection —
1474
1646
  including a Pallet node's — is refused by name. `validateClusterIngressRegistration(value, path?)`
1475
- checks shape only: whether the bearer is the one Cloudly minted is Cloudly's to verify.
1647
+ checks shape only: whether the bearer is the one Cloudly minted is Cloudly's to verify. It reads
1648
+ the registration's `protocol` offer through the one grammar `protocol` owns, and both hops judge
1649
+ the body in the one server order `protocol.readProtocolOffer` states — the offer is negotiated
1650
+ before this shape check and before the bearer — so a relay refuses a peer it cannot serve without
1651
+ asking Cloudly, and an accepted registration carries the accepting side's own offer back.
1476
1652
 
1477
1653
  `IClusterTrafficStatistics` is one UTC day of one hostname's traffic on one node —
1478
1654
  `requests`, `bytesIn`, `bytesOut`, `connections` and the backend failures the proxy can observe
@@ -1493,6 +1669,7 @@ const ingressReasons = data.validateClusterIngressRegistration({
1493
1669
  bearer: ingressBearerFromAssignmentEnvironment,
1494
1670
  version: '3.0.0',
1495
1671
  nodeId: 'node-a',
1672
+ protocol: protocol.createProtocolOffer('32.0.0'),
1496
1673
  }); // []
1497
1674
  ```
1498
1675
 
@@ -1500,7 +1677,7 @@ const ingressReasons = data.validateClusterIngressRegistration({
1500
1677
 
1501
1678
  A route to a node's published host ports needs the address that node answers on, and only the
1502
1679
  node can prove it: it holds the uplink observation the address belongs to. It therefore states it
1503
- on the network report it already sends — `IReportRuntimeNetworkProtectionRequestV1` gains the
1680
+ on the network report it already sends — `IReportRuntimeNetworkProtectionRequest` gains the
1504
1681
  optional `uplinkAddress`, a unicast IPv4 literal that is neither loopback nor link-local
1505
1682
  (`runtimeNodeUplinkAddress`). Absence stays absent: a node that states no address is told apart
1506
1683
  from one that states a value, and nothing downstream defaults or infers one. Cloudly stores what
@@ -1729,7 +1906,7 @@ metadata, `data.normalizeCoreMailControlBootstrap` for startup authority, and
1729
1906
  normalizers reject unknown fields, noncanonical mailboxes,
1730
1907
  ambiguous active recipient ownership, malformed SHA-256 values, and
1731
1908
  credential-lifecycle inconsistencies. CoreMail credential verifiers use the
1732
- versioned `argon2id-v1` format and the exact policy exported as
1909
+ `argon2id` format and the exact policy exported as
1733
1910
  `data.coreMailCredentialVerifierPolicy`.
1734
1911
 
1735
1912
  `data.ICoreMailDesiredState.smtp` is the optional CoreMail SMTP submission
@@ -1757,7 +1934,7 @@ canonical environment keys for the verifier-only bootstrap payload and the
1757
1934
  separate control and gateway secret values; no consumer may derive or embed
1758
1935
  plaintext material in desired state.
1759
1936
 
1760
- CoreMail schema v2 keeps durable authority at the stable tenant, service, and
1937
+ The CoreMail contracts keep durable authority at the stable tenant, service, and
1761
1938
  binding identity while revisions, config epochs, and composite
1762
1939
  `credentialId`/`version` values fence sessions and new actions. Credential
1763
1940
  versions are authority-wide monotonic and unique even when rotation changes the
@@ -1792,8 +1969,8 @@ consumed only by the first durable insertion of an idempotency identity, while
1792
1969
  replays consume none. Pending inbound includes every state except
1793
1970
  `acknowledged`. These semantics are exported as `data.coreMailQuotaPolicy`.
1794
1971
  Every binding carries finite `messagesPerMinute`, `messagesPerDay`, and
1795
- `maxPendingInbound` values; omitted or unlimited quotas are not valid schema-v2
1796
- desired state.
1972
+ `maxPendingInbound` values; omitted or unlimited quotas are not valid desired
1973
+ state.
1797
1974
  `data.coreMailRetentionPolicy` retains terminal outbound,
1798
1975
  acknowledged inbound, and idempotency receipts for 30 days and expired
1799
1976
  capabilities for 24 hours. Pending inbound is never age-purged.
@@ -1815,7 +1992,7 @@ kind-specific byte budget reports `PAYLOAD_LIMIT_EXCEEDED`.
1815
1992
 
1816
1993
  Control and gateway credential rotation is ordered: provision the candidate
1817
1994
  plaintext through resolved runtime secrets, publish and activate the matching
1818
- `argon2id-v1` verifier, roll or reconnect every affected replica, observe
1995
+ `argon2id` verifier, roll or reconnect every affected replica, observe
1819
1996
  per-task authentication and readiness, then mark the previous verifier
1820
1997
  `retiring` with `acceptUntil`. Remove the previous verifier and secret only
1821
1998
  after its acceptance window has elapsed and no session uses that composite
@@ -1846,7 +2023,7 @@ and a `coremail:<identifier>` management scope, validated by the existing
1846
2023
  `data.validateSecretManagementScope` and secret-metadata validators.
1847
2024
 
1848
2025
  `IServiceMailConfig.coreMail` publishes the service's CoreMail binding as public
1849
- metadata only: binding and composite credential identity, the `argon2id-v1`
2026
+ metadata only: binding and composite credential identity, the `argon2id`
1850
2027
  verification hash, the binding state, and any `retiring` predecessor with its
1851
2028
  `acceptUntil`. Plaintext never appears there, and the field is excluded from
1852
2029
  `data.TServiceWritableData`, so only the control plane writes it while the rest
@@ -1929,9 +2106,9 @@ async function acceptMaterial(
1929
2106
  }
1930
2107
  ```
1931
2108
 
1932
- Runtime material is shaped as `{ schemaVersion: 1, manifest, entries }`, where
2109
+ Runtime material is shaped as `{ manifest, entries }`, where
1933
2110
  each entry contains only `secretVersionId` and a strict X25519 envelope. The
1934
- helper verifies the canonical schema-v2 manifest digest, envelope context
2111
+ helper verifies the canonical manifest digest, envelope context
1935
2112
  digests, active recipient key, sorted exact one-to-one version coverage, every
1936
2113
  request fence, and the trusted local organization/cluster expectation. It never
1937
2114
  decrypts. Organization and cluster are derived from the verified cluster JWT
@@ -1948,16 +2125,13 @@ canonical token boundary, while
1948
2125
  plaintext directly from validated UTF-8 bytes without first constructing a
1949
2126
  JavaScript token string. Publication grants carry the service, binding,
1950
2127
  reconciliation generation, binding-request digest, target Secrets revision, and
1951
- bounded validity window. `computeCorestoreCredentialBindingRequestSha256()`
1952
- produces that digest from the exact value-free provider, scope, environment, and
1953
- optional retention request for persisted schema-v1 records. New issuers use
1954
- `TCorestoreCredentialBindingRequestV2` and
1955
- `validateCorestoreCredentialBindingRequestV2()`,
1956
- `createCorestoreCredentialBindingRequestDigestInputV2()`, and
1957
- `computeCorestoreCredentialBindingRequestSha256V2()`. The corresponding
1958
- unsuffixed APIs remain the schema-v1 boundary. Schema v2 binds every object-storage
1959
- request to the exact durable `bucketName`; schema-v1 records stay readable but
1960
- do not provide that authority. Object-storage plaintext keeps the logical
2128
+ bounded validity window. `TCorestoreCredentialBindingRequest` with
2129
+ `validateCorestoreCredentialBindingRequest()`,
2130
+ `createCorestoreCredentialBindingRequestDigestInput()`, and
2131
+ `computeCorestoreCredentialBindingRequestSha256()` produces that digest from
2132
+ the exact value-free provider, scope, environment, bucket, and optional
2133
+ retention request. Every object-storage request binds the exact durable
2134
+ `bucketName`. Object-storage plaintext keeps the logical
1961
2135
  `accessKeyId` and `secretAccessKey` fields while
1962
2136
  `createCorestoreCredentialSecretValues()` expands them to the six canonical
1963
2137
  Corestore `S3_*` and `AWS_*` Secret aliases. Receipts bind that exact key
@@ -2019,7 +2193,11 @@ answer is refused with one: the two states can never be read as each other.
2019
2193
  | `getClusterPlatformDesiredState` | relay → Cloudly | `{}` → `{ capabilities, providerConfigs, bindings }` |
2020
2194
  | `backupServiceDatabaseClosure` | relay → Cloudly | `{ backupId, serviceId, nodeId, descriptor, closure }` → `{ accepted }` |
2021
2195
  | `restoreServiceDatabaseClosure` | relay → Cloudly | `{ backupId, serviceId, nodeId }` → `{ descriptor, closure }` |
2022
- | `stageIsolatedRestoreArchive` | Cloudly → relay | `{ restoreGrant, clusterId, targetNodeName, restoreId, expectedRevision, object, archive }` → `{ state }` |
2196
+ | `prepareIsolatedRestoreOnNode` | Cloudly → relay | `{ nodeId, control }` → `{ progress }` |
2197
+ | `stageIsolatedRestoreArchive` | Cloudly → relay | `{ nodeId, control }` → `{ progress }` |
2198
+ | `getIsolatedRestoreProgressOnNode` | Cloudly → relay | `{ nodeId, control }` → `{ progress }` |
2199
+ | `executeIsolatedRestoreOnNode` | Cloudly → relay | `{ nodeId, control }` → `{ progress }` |
2200
+ | `cleanupIsolatedRestoreOnNode` | Cloudly → relay | `{ nodeId, control }` → `{ progress }` |
2023
2201
 
2024
2202
  **Placement is a field too.** `getClusterPlatformDesiredState` answers with
2025
2203
  `IClusterPlatformBindingPlacement { nodeId, binding }` rather than a bare binding list: a database
@@ -2051,14 +2229,53 @@ closure its own receipt contradicts.
2051
2229
 
2052
2230
  **Isolated restore carries its grant.** Corestore accepts exactly one authority on
2053
2231
  `/isolated-restore/*`: a compact RS256 grant that names one operation, expires within minutes and
2054
- binds the cluster, the node, the canonical resource mappings and the archive manifest. So
2055
- `coreflowPrepareIsolatedRestore`, `coreflowExecuteIsolatedRestore`,
2056
- `coreflowGetIsolatedRestoreStatus`, `coreflowCleanupIsolatedRestore` and
2057
- `stageIsolatedRestoreArchive` each carry `restoreGrant`, which the relay forwards untouched and can
2058
- neither widen nor mint. Each is sensitive ephemeral input: exclude the complete request from hooks,
2059
- logs and journals on every hop. Staging is one archive object per call — the manifest entry is the
2060
- descriptor — because Corestore writes staged objects one bounded chunk at a time under a
2061
- `write-object` grant, and turning a stream into those chunks is the relay's work.
2232
+ binds the cluster, the node name, the canonical resource mappings and the archive manifest. The
2233
+ five methods above are therefore one shape — `{ nodeId, control }` → `{ progress }` — where
2234
+ `control` is the body Corestore itself reads (`data.IIsolatedRestoreControlPrepareRequest`,
2235
+ `…WriteRequest`, `…StatusRequest`, `…ExecuteRequest`, `…CleanupRequest`) with the grant inside it.
2236
+ The relay forwards the control untouched and can neither widen it nor mint one; it adds no
2237
+ identity and no protocol offer either, because the cluster is the one the verified machine identity
2238
+ on the session `registerCloudlyClientSession` opened names, and the protocol offer was exchanged by
2239
+ that handshake and again by `registerClusterRelay`'s own `protocol` member. Every one of them is
2240
+ sensitive ephemeral input: the grant is a bearer credential, declared once on
2241
+ `data.IIsolatedRestoreControlAuthority`, so exclude the complete request from hooks, logs,
2242
+ diagnostics and durable journals on every hop.
2243
+
2244
+ **The order an operator's restore runs in.** Cloudly calls `createIsolatedRestore` with the node
2245
+ it dispatches to, then, on that node: `prepareIsolatedRestoreOnNode` with `expectedRevision: 0`,
2246
+ `stageIsolatedRestoreArchive` once per bounded chunk of every manifest object,
2247
+ `executeIsolatedRestoreOnNode`, and `cleanupIsolatedRestoreOnNode` when the rehearsal is over;
2248
+ `getIsolatedRestoreProgressOnNode` reads the same state without changing it. Staging is bounded by
2249
+ `data.isolatedRestoreContractLimits`: at most 512 KiB decoded per chunk, 4,096 chunks per object
2250
+ and 64 MiB per object, each chunk authenticated by its own `chunkSha256` against the immutable
2251
+ object descriptor. Every mutation carries the `revision` the node last stated as its
2252
+ `expectedRevision`, so a replay settles instead of racing.
2253
+
2254
+ **The node id routes, the node name is authorized.** `createIsolatedRestore` takes `targetNodeId`
2255
+ — the cluster document's node id, which is what a relay routes on — while the signed grant keeps
2256
+ `targetNodeName`, which is what the operator authorized and what Corestore verifies. Cloudly is
2257
+ the only party holding both, so `IIsolatedRestoreRecord` states both and every control call is
2258
+ dispatched by the id. `data.bindIsolatedRestoreProgressToNode(progress, { nodeId, restoreId })`
2259
+ is where an answer is tied back: the control body names neither the node nor the restore, so
2260
+ without it a relay could answer with another node's — or another restore's — perfectly canonical
2261
+ progress.
2262
+
2263
+ **What a node can state, in Corestore's own words.** `data.IIsolatedRestoreProgress` is Corestore's
2264
+ staging summary plus the node the control was carried to: `{ nodeId, restoreId, stagingArchiveId,
2265
+ status, revision, lastFence, expectedObjects, receivedObjects, completedMappings, totalMappings,
2266
+ preparedAt, updatedAt, executedAt?, stagedObject? }`, with `status` one of
2267
+ `data.isolatedRestoreProgressStatuses` — `prepared`, `executing`, `failed`, `executed`, `cleaned`.
2268
+ `stagedObject` is present only on a staged chunk and says where the next chunk continues; it is
2269
+ named apart from the `object` receipt Corestore's own write answer carries, which is a different
2270
+ shape and is not projected. `data.normalizeIsolatedRestoreProgress(value)` reads it exactly,
2271
+ refusing an unknown status, a count larger than the plan it belongs to, a `revision` or `lastFence`
2272
+ below 1 — a committed state carries both — an `executing` or `executed` answer that does not hold
2273
+ the whole archive, and an `executed` answer that left a mapping behind or does not say when it
2274
+ finished. A node states nothing of the control plane's record — no source backup, no requester, no
2275
+ verification — so Cloudly composes its record from this and its own knowledge, exactly as it does
2276
+ with the Corestore inventory. A relay that cannot reach the node answers one of
2277
+ `data.isolatedRestoreNodeControlRefusals` instead: `restore-node-not-carried`,
2278
+ `restore-node-unreachable`, or `restore-node-refused` followed by Corestore's own reason.
2062
2279
 
2063
2280
  `IIsolatedRestoreDatabaseMapping.target.databaseAllocation` carries Corestore's safe allocation
2064
2281
  reference, present exactly when the target database is allocation-managed. It lives inside the
@@ -2095,88 +2312,22 @@ recipient the relay enrolled with `beginSecretRecipientEnrollment` and
2095
2312
  `completeSecretRecipientEnrollment` — see [Corestore Runtime Credentials](#corestore-runtime-credentials).
2096
2313
  No credential travels in the cluster document, and none is read from the relay's environment.
2097
2314
 
2098
- ### Legacy Deployment Settlement
2099
-
2100
- The Node-only `/runtime` export also provides strict, exact-schema codecs for
2101
- Cloudly legacy deployment settlement plans, production requests, check/apply
2102
- summaries, stable outcomes, and scratch/production receipts. Authenticated
2103
- artifacts use domain-separated HMAC inputs and references derived from the full
2104
- authority-metadata SHA-256; authority-aware normalizers enforce metadata fences
2105
- and artifact-time validity windows. General authority-aware APIs require a
2106
- consumer-owned, constant-time HMAC verifier; private stdio authority-aware APIs
2107
- derive verification from their exact plaintext authority handoffs. They never
2108
- read the current wall clock.
2109
- Structural normalizers explicitly do not authenticate MACs. Literal portable
2110
- golden vectors contain only safe
2111
- metadata, summaries, digests, and MACs. The separately named
2112
- `ICloudlyLegacyDeploymentSettlementAuthorityHandoffPlaintextV1` parser/encoder
2113
- exists only for private maintenance-container interoperability and is never part
2114
- of a safe result, receipt, or golden summary.
2115
-
2116
- Fenced settlement contracts add deterministic execution-attempt identities and
2117
- prepared, committed, or aborted observations. Resolution bundles distinguish
2118
- `committed-schema1`, `committed-schema2`, `aborted-prepared`,
2119
- `observed-aborted`, `absent-tombstone`, and `legacy-no-op`; only the scratch-only
2120
- legacy no-op has no durable attempt observation. Source and execution authority
2121
- status are bound to the resolution's `observedAt`. Scratch attempts use the same
2122
- authority for both roles and require matching statuses. Historical verification
2123
- remains possible after expiry because status is derived only from the
2124
- authenticated observation time. Production recovery always uses receipt v2 and
2125
- a distinct execution authority B whose metadata names source authority A and
2126
- binds the authenticated plan, request bundle, and scratch receipt digests.
2127
- B is issued strictly after scratch completion; B's `requestedAt` and
2128
- `approvedAt` are independent of A's request timestamp and must remain within
2129
- B's own validity interval.
2130
-
2131
- Consumers make trust decisions through
2132
- `normalizeAuthenticatedCloudlyLegacyDeploymentSettlementResolutionForAuthorities()`,
2133
- `normalizeCloudlyLegacyDeploymentSettlementScratchExecutionBundleForAuthority()`,
2134
- `normalizeCloudlyLegacyDeploymentSettlementProductionExecutionBundleV2ForAuthorities()`,
2135
- `normalizeCloudlyLegacyDeploymentSettlementStdioRequestForAuthorities()`, and
2136
- `normalizeCloudlyLegacyDeploymentSettlementStdioResponseForRequestAuthorities()`.
2137
- Every resolution outcome, including an unapplied outcome, is carried in an
2138
- `IAuthenticatedCloudlyLegacyDeploymentSettlementResolutionV1` recovery
2139
- attestation under the dedicated resolution HMAC domain and signed by the
2140
- execution authority. The raw
2141
- `normalizeCloudlyLegacyDeploymentSettlementResolutionBundleForAuthorities()`
2142
- helper verifies referenced artifacts and status only; its result is not an
2143
- authenticated recovery claim. The non-stdio authority-aware APIs use the
2144
- caller-supplied HMAC verifier and never receive plaintext keys. The private
2145
- stdio variants accept no external verifier: they bind each HMAC to the
2146
- authority reference and key in the corresponding request handoff. Interfaces
2147
- never executes a settlement.
2148
-
2149
- The stdio contract exposes nine commands from `settlement-plan` through
2150
- `settlement-production-resolve`. Requests and responses are exact canonical
2151
- JSON followed by one LF, bounded by
2152
- `cloudlyLegacyDeploymentSettlementStdioLimits`, and responses use a
2153
- command-discriminated success payload or a finite secret-safe error. Private
2154
- authority handoffs and MongoDB descriptors belong only on protected request
2155
- transport. Production stdio requests reject equal decoded A and B HMAC keys and
2156
- wipe temporary comparison buffers. The handoff-derived verifier uses HMAC-SHA256
2157
- with constant-time MAC comparison, rejects wrong or swapped handoff keys, and
2158
- wipes temporary decoded key and MAC buffers. Handoffs and derived verifiers are
2159
- private in-process capabilities that must never be persisted or logged and must
2160
- be discarded when the stdio exchange ends. Verifier-only integrations do not
2161
- expose key material to Interfaces, so their callers must enforce the same key
2162
- separation.
2163
- Authority-aware response validation also binds settlement-request
2164
- `requestedAt`, production-authorization `requestedAt` and `approvedAt`, and
2165
- resolution `observedAt` to the corresponding request. Production check and
2166
- apply results must be issued by B no earlier than both A's request and B's
2167
- `approvedAt`. Callers remain responsible for process isolation, key
2168
- destruction, logging discipline, database access, and the actual settlement
2169
- transaction.
2315
+ ### Launcher Environment Delivery
2170
2316
 
2171
2317
  Launcher environment delivery uses stable `szsv-<base32-sha256>` Docker
2172
2318
  resource names and `/run/serve.zone/secrets/<resource>` source paths. The
2173
- nonsecret map is written to `/run/serve.zone/workloadinit-map-v1.json` with mode
2174
- 0444. Runtime assets under `/opt/serve.zone/runtime-assets` are read-only, and
2175
- the wrapper executes
2176
- `workloadinit run --map /run/serve.zone/workloadinit-map-v1.json -- <argv...>`
2177
- without a shell, symlink, or aggregate value file. Resources and maps remain
2178
- retained while any desired, applied, or previous-accepted manifest references
2179
- them and are deleted only after Docker confirms rollout or service removal.
2319
+ nonsecret map is written to `/run/serve.zone/workloadinit-map.json` with mode
2320
+ 0444. Runtime assets under `/opt/serve.zone/runtime-assets` are read-only, the
2321
+ wrapper itself lives at
2322
+ `/opt/serve.zone/runtime-assets/workloadinit/workloadinit`, and it executes
2323
+ `workloadinit run --map /run/serve.zone/workloadinit-map.json -- <argv...>`
2324
+ without a shell, symlink, or aggregate value file. A resource name is the
2325
+ lowercase base32 of a SHA-256 over `serve.zone/docker-secret-resource`, a NUL
2326
+ and the SecretVersion id, so every launcher secret of a service is republished
2327
+ under a new Docker resource name when a cluster moves to 32.0.0. Resources and
2328
+ maps remain retained while any desired, applied, or previous-accepted manifest
2329
+ references them and are deleted only after Docker confirms rollout or service
2330
+ removal.
2180
2331
 
2181
2332
  ## Platform Contracts
2182
2333