@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.
- package/changelog.md +476 -0
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/appstore/types.d.ts +4 -6
- package/dist_ts/appstore/types.js +5 -5
- package/dist_ts/data/backup.d.ts +3 -26
- package/dist_ts/data/cluster.d.ts +36 -11
- package/dist_ts/data/cluster.js +33 -22
- package/dist_ts/data/clusteringress.d.ts +8 -0
- package/dist_ts/data/clusteringress.js +13 -8
- package/dist_ts/data/clusternode.d.ts +121 -74
- package/dist_ts/data/clusternode.js +109 -132
- package/dist_ts/data/coremail.d.ts +3 -7
- package/dist_ts/data/coremail.js +2 -2
- package/dist_ts/data/coremail.runtime.d.ts +0 -1
- package/dist_ts/data/coremail.runtime.js +5 -22
- package/dist_ts/data/deploymentadoption.d.ts +0 -2
- package/dist_ts/data/deploymentadoption.js +40 -6
- package/dist_ts/data/deploymentoperation.js +16 -3
- package/dist_ts/data/deploymentpreflight.d.ts +0 -2
- package/dist_ts/data/deploymentpreflight.js +1 -1
- package/dist_ts/data/immutableimage.d.ts +38 -21
- package/dist_ts/data/immutableimage.js +45 -5
- package/dist_ts/data/index.d.ts +1 -1
- package/dist_ts/data/isolatedrestore.d.ts +139 -21
- package/dist_ts/data/isolatedrestore.golden.d.ts +0 -1
- package/dist_ts/data/isolatedrestore.golden.js +6 -9
- package/dist_ts/data/isolatedrestore.js +176 -40
- package/dist_ts/data/mail.d.ts +1 -1
- package/dist_ts/data/networkaddressplan.d.ts +8 -8
- package/dist_ts/data/networkaddressplan.js +4 -4
- package/dist_ts/data/networkauthority.d.ts +20 -22
- package/dist_ts/data/networkauthority.js +26 -28
- package/dist_ts/data/networkcredential.d.ts +10 -10
- package/dist_ts/data/networkcredential.js +16 -16
- package/dist_ts/data/networkdelivery.d.ts +18 -18
- package/dist_ts/data/networkdelivery.js +16 -16
- package/dist_ts/data/networklease.d.ts +18 -7
- package/dist_ts/data/networklease.js +29 -7
- package/dist_ts/data/networknode.d.ts +2 -2
- package/dist_ts/data/networkprojection.d.ts +59 -60
- package/dist_ts/data/networkprojection.js +53 -57
- package/dist_ts/data/networkprotection.d.ts +27 -28
- package/dist_ts/data/networkprotection.js +36 -36
- package/dist_ts/data/networkrouter.d.ts +11 -11
- package/dist_ts/data/networkrouter.js +1 -1
- package/dist_ts/data/networksigning.d.ts +16 -18
- package/dist_ts/data/networksigning.js +20 -21
- package/dist_ts/data/nodecredential.d.ts +4 -6
- package/dist_ts/data/nodeenrollment.d.ts +35 -40
- package/dist_ts/data/nodeenrollment.js +57 -72
- package/dist_ts/data/privatenetwork.d.ts +21 -24
- package/dist_ts/data/privatenetwork.js +39 -54
- package/dist_ts/data/runtimeassignment.d.ts +50 -34
- package/dist_ts/data/runtimeassignment.js +79 -45
- package/dist_ts/data/runtimeconfig.d.ts +21 -22
- package/dist_ts/data/runtimeconfig.js +24 -24
- package/dist_ts/data/runtimesession.d.ts +100 -62
- package/dist_ts/data/runtimesession.js +98 -79
- package/dist_ts/data/runtimeterminalreceipt.d.ts +28 -29
- package/dist_ts/data/runtimeterminalreceipt.js +42 -42
- package/dist_ts/data/secret.d.ts +18 -25
- package/dist_ts/data/secret.js +12 -52
- package/dist_ts/data/servicecredential.d.ts +14 -16
- package/dist_ts/data/servicecredential.js +17 -28
- package/dist_ts/data/serviceruntime.d.ts +7 -7
- package/dist_ts/data/serviceruntime.js +4 -4
- package/dist_ts/data/sparknodereport.d.ts +27 -14
- package/dist_ts/data/sparknodereport.js +35 -28
- package/dist_ts/data/webpush.d.ts +0 -1
- package/dist_ts/index.d.ts +2 -1
- package/dist_ts/index.js +3 -2
- package/dist_ts/platform/objectstorageretention.d.ts +21 -24
- package/dist_ts/platform/objectstorageretention.js +10 -17
- package/dist_ts/platform/storage.d.ts +1 -3
- package/dist_ts/platform/storagemigration.d.ts +0 -7
- package/dist_ts/platform/storagemigration.golden.d.ts +0 -1
- package/dist_ts/platform/storagemigration.golden.js +8 -12
- package/dist_ts/platform/storagemigration.js +6 -51
- package/dist_ts/platform/types.d.ts +2 -2
- package/dist_ts/private/canonicaljson.d.ts +9 -1
- package/dist_ts/private/canonicaljson.js +14 -2
- package/dist_ts/private/dataproperties.d.ts +62 -0
- package/dist_ts/private/dataproperties.js +117 -0
- package/dist_ts/private/networkcontract.d.ts +10 -10
- package/dist_ts/private/networkcontract.js +1 -1
- package/dist_ts/private/releaseversion.d.ts +37 -0
- package/dist_ts/private/releaseversion.js +48 -0
- package/dist_ts/private/runtimepolicy.d.ts +2 -2
- package/dist_ts/private/runtimepolicy.js +1 -1
- package/dist_ts/private/tokenlist.d.ts +9 -0
- package/dist_ts/private/tokenlist.js +23 -0
- package/dist_ts/protocol.d.ts +137 -0
- package/dist_ts/protocol.js +255 -0
- package/dist_ts/requests/admin.d.ts +8 -8
- package/dist_ts/requests/backup.d.ts +102 -112
- package/dist_ts/requests/cluster.d.ts +10 -10
- package/dist_ts/requests/clusteringress.d.ts +9 -0
- package/dist_ts/requests/clusteringress.js +1 -1
- package/dist_ts/requests/config.d.ts +3 -3
- package/dist_ts/requests/corestore.d.ts +0 -3
- package/dist_ts/requests/corestore.js +5 -9
- package/dist_ts/requests/network.d.ts +30 -30
- package/dist_ts/requests/node.d.ts +15 -28
- package/dist_ts/requests/pallet.d.ts +13 -13
- package/dist_ts/requests/runtimesession.d.ts +53 -41
- package/dist_ts/requests/secret.d.ts +4 -5
- package/dist_ts/requests/secret.js +1 -5
- package/dist_ts/runtime.corestore.d.ts +56 -74
- package/dist_ts/runtime.corestore.databaseexportpackage.d.ts +30 -36
- package/dist_ts/runtime.corestore.databaseexportpackage.golden.d.ts +11 -12
- package/dist_ts/runtime.corestore.databaseexportpackage.golden.js +54 -71
- package/dist_ts/runtime.corestore.databaseexportpackage.js +9 -25
- package/dist_ts/runtime.corestore.js +35 -135
- package/dist_ts/runtime.corestore.relay.js +2 -7
- package/dist_ts/runtime.d.ts +38 -48
- package/dist_ts/runtime.js +20 -77
- package/dist_ts/runtime.networkprojection.d.ts +4 -4
- package/dist_ts/runtime.networkprojection.js +18 -18
- package/dist_ts/runtime.workloadinit.d.ts +23 -24
- package/dist_ts/runtime.workloadinit.js +6 -13
- package/package.json +2 -2
- package/readme.md +421 -270
- package/ts/00_commitinfo_data.ts +3 -3
- package/ts/appstore/types.ts +4 -6
- package/ts/data/backup.ts +3 -38
- package/ts/data/cluster.ts +51 -34
- package/ts/data/clusteringress.ts +12 -12
- package/ts/data/clusternode.ts +204 -232
- package/ts/data/coremail.runtime.ts +4 -21
- package/ts/data/coremail.ts +3 -7
- package/ts/data/deploymentadoption.ts +46 -4
- package/ts/data/deploymentoperation.ts +16 -1
- package/ts/data/deploymentpreflight.ts +0 -2
- package/ts/data/immutableimage.ts +71 -24
- package/ts/data/index.ts +1 -1
- package/ts/data/isolatedrestore.golden.ts +5 -9
- package/ts/data/isolatedrestore.ts +339 -54
- package/ts/data/mail.ts +1 -1
- package/ts/data/networkaddressplan.ts +13 -13
- package/ts/data/networkauthority.ts +42 -46
- package/ts/data/networkcredential.ts +24 -24
- package/ts/data/networkdelivery.ts +27 -27
- package/ts/data/networklease.ts +40 -13
- package/ts/data/networknode.ts +2 -2
- package/ts/data/networkprojection.ts +119 -124
- package/ts/data/networkprotection.ts +54 -55
- package/ts/data/networkrouter.ts +14 -14
- package/ts/data/networksigning.ts +29 -32
- package/ts/data/nodecredential.ts +4 -6
- package/ts/data/nodeenrollment.ts +96 -114
- package/ts/data/privatenetwork.ts +58 -74
- package/ts/data/runtimeassignment.ts +143 -94
- package/ts/data/runtimeconfig.ts +45 -46
- package/ts/data/runtimesession.ts +185 -138
- package/ts/data/runtimeterminalreceipt.ts +72 -73
- package/ts/data/secret.ts +28 -76
- package/ts/data/servicecredential.ts +33 -43
- package/ts/data/serviceruntime.ts +11 -11
- package/ts/data/sparknodereport.ts +48 -36
- package/ts/data/webpush.ts +0 -1
- package/ts/index.ts +2 -0
- package/ts/platform/objectstorageretention.ts +40 -54
- package/ts/platform/storage.ts +1 -3
- package/ts/platform/storagemigration.golden.ts +7 -11
- package/ts/platform/storagemigration.ts +5 -55
- package/ts/platform/types.ts +2 -2
- package/ts/private/canonicaljson.ts +13 -1
- package/ts/private/dataproperties.ts +139 -0
- package/ts/private/networkcontract.ts +9 -9
- package/ts/private/releaseversion.ts +70 -0
- package/ts/private/runtimepolicy.ts +2 -2
- package/ts/private/tokenlist.ts +30 -0
- package/ts/protocol.ts +364 -0
- package/ts/requests/admin.ts +11 -11
- package/ts/requests/backup.ts +112 -117
- package/ts/requests/cluster.ts +13 -13
- package/ts/requests/clusteringress.ts +9 -0
- package/ts/requests/config.ts +3 -3
- package/ts/requests/corestore.ts +4 -16
- package/ts/requests/network.ts +40 -40
- package/ts/requests/node.ts +21 -38
- package/ts/requests/pallet.ts +17 -17
- package/ts/requests/runtimesession.ts +63 -51
- package/ts/requests/secret.ts +5 -10
- package/ts/runtime.corestore.databaseexportpackage.golden.ts +77 -95
- package/ts/runtime.corestore.databaseexportpackage.ts +51 -73
- package/ts/runtime.corestore.relay.ts +1 -10
- package/ts/runtime.corestore.ts +118 -268
- package/ts/runtime.networkprojection.ts +30 -30
- package/ts/runtime.ts +83 -158
- package/ts/runtime.workloadinit.ts +36 -48
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.d.ts +0 -902
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.d.ts +0 -35
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.js +0 -315
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.js +0 -3315
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.d.ts +0 -291
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.d.ts +0 -30
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.js +0 -408
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.js +0 -1126
- package/ts/runtime.cloudlylegacydeploymentsettlement.golden.ts +0 -421
- package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.ts +0 -510
- package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.ts +0 -2470
- 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
|
|
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
|
|
48
|
-
|
|
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.
|
|
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.
|
|
119
|
-
`/spark/
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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. `
|
|
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 `
|
|
142
|
-
adoption preconditions and `
|
|
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. `
|
|
150
|
-
frozen snapshot; `
|
|
151
|
-
|
|
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. `
|
|
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
|
-
`
|
|
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 `
|
|
172
|
-
`
|
|
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
|
-
`
|
|
177
|
-
`
|
|
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. `
|
|
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
|
-
`
|
|
207
|
+
`snapshotPalletNodeEnrollmentActivation` captures the exact activation request,
|
|
183
208
|
rejecting extra wrapper fields as well as malformed nested data.
|
|
184
|
-
`
|
|
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
|
-
`
|
|
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.
|
|
203
|
-
`
|
|
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
|
-
`
|
|
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.
|
|
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.
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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. `
|
|
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
|
-
`
|
|
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. `
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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. `
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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 `
|
|
353
|
-
`
|
|
354
|
-
`
|
|
355
|
-
`revision`, or null when a service never had one. `
|
|
356
|
-
returns the accepted `
|
|
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 `
|
|
359
|
-
exactly `protocol`, `hostPort` and `targetPort`:
|
|
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
|
-
`
|
|
391
|
+
`getRuntimeNetworkProjectionPacketGrants` derives directed member connectivity
|
|
367
392
|
and explicit local public/platform egress independently of DNS readiness.
|
|
368
|
-
`
|
|
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 `
|
|
388
|
-
router-origin egress selections and withdrawals.
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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
|
-
`
|
|
436
|
-
`
|
|
437
|
-
projection,
|
|
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 `
|
|
446
|
-
uses `
|
|
447
|
-
`
|
|
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
|
-
`
|
|
481
|
+
`snapshotGetRuntimeManagedVpnCredentialRequest` and its response counterpart
|
|
454
482
|
capture closed detached JSON shapes and canonical 32-byte key encodings.
|
|
455
|
-
`
|
|
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 `
|
|
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 `
|
|
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 `
|
|
656
|
-
`
|
|
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.
|
|
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.
|
|
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. `
|
|
692
|
-
content; `
|
|
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 `
|
|
708
|
-
`
|
|
709
|
-
`
|
|
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
|
-
`
|
|
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.
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
node restart from a replay.
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
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
|
-
`
|
|
747
|
-
machine JWT on a physical TypedSocket so the server can own its identity tag
|
|
748
|
-
|
|
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.
|
|
754
|
-
`requests.runtimesession.
|
|
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. `
|
|
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
|
-
###
|
|
851
|
-
|
|
852
|
-
The Node-only `@serve.zone/interfaces/runtime` export
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
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
|
|
895
|
-
|
|
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
|
-
- `
|
|
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
|
-
-
|
|
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 `
|
|
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`
|
|
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
|
-
- `
|
|
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
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
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
|
-
- `
|
|
979
|
-
snapshots, while `
|
|
980
|
-
`
|
|
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
|
-
`
|
|
1143
|
+
`ICoreflowSecretRuntimeRegistration`; cluster scope comes from the verified
|
|
991
1144
|
connection identity rather than the tag.
|
|
992
|
-
- `
|
|
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
|
|
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.
|
|
1088
|
-
appstore.appStoreStorageFeatureIds.
|
|
1089
|
-
appstore.appStoreStorageFeatureIds.
|
|
1090
|
-
appstore.appStoreStorageFeatureIds.
|
|
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
|
|
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 `
|
|
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. `
|
|
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.
|
|
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.
|
|
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. `
|
|
1365
|
-
answers with the block Cloudly published, and `
|
|
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 `
|
|
1373
|
-
`
|
|
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. `
|
|
1377
|
-
named reason per violation, like every validator here, and checks shape only
|
|
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.
|
|
1409
|
-
|
|
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.
|
|
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
|
|
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 — `
|
|
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
|
-
|
|
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
|
|
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
|
|
1796
|
-
|
|
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
|
|
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
|
|
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 `{
|
|
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
|
|
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. `
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
`
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
`
|
|
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
|
-
| `
|
|
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.
|
|
2055
|
-
`
|
|
2056
|
-
`
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
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
|
-
###
|
|
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
|
|
2174
|
-
0444. Runtime assets under `/opt/serve.zone/runtime-assets` are read-only,
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
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
|
|