@serve.zone/interfaces 31.9.0 → 32.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/changelog.md +476 -0
  2. package/dist_ts/00_commitinfo_data.js +3 -3
  3. package/dist_ts/appstore/types.d.ts +4 -6
  4. package/dist_ts/appstore/types.js +5 -5
  5. package/dist_ts/data/backup.d.ts +3 -26
  6. package/dist_ts/data/cluster.d.ts +36 -11
  7. package/dist_ts/data/cluster.js +33 -22
  8. package/dist_ts/data/clusteringress.d.ts +8 -0
  9. package/dist_ts/data/clusteringress.js +13 -8
  10. package/dist_ts/data/clusternode.d.ts +121 -74
  11. package/dist_ts/data/clusternode.js +109 -132
  12. package/dist_ts/data/coremail.d.ts +3 -7
  13. package/dist_ts/data/coremail.js +2 -2
  14. package/dist_ts/data/coremail.runtime.d.ts +0 -1
  15. package/dist_ts/data/coremail.runtime.js +5 -22
  16. package/dist_ts/data/deploymentadoption.d.ts +0 -2
  17. package/dist_ts/data/deploymentadoption.js +40 -6
  18. package/dist_ts/data/deploymentoperation.js +16 -3
  19. package/dist_ts/data/deploymentpreflight.d.ts +0 -2
  20. package/dist_ts/data/deploymentpreflight.js +1 -1
  21. package/dist_ts/data/immutableimage.d.ts +38 -21
  22. package/dist_ts/data/immutableimage.js +45 -5
  23. package/dist_ts/data/index.d.ts +1 -1
  24. package/dist_ts/data/isolatedrestore.d.ts +139 -21
  25. package/dist_ts/data/isolatedrestore.golden.d.ts +0 -1
  26. package/dist_ts/data/isolatedrestore.golden.js +6 -9
  27. package/dist_ts/data/isolatedrestore.js +176 -40
  28. package/dist_ts/data/mail.d.ts +1 -1
  29. package/dist_ts/data/networkaddressplan.d.ts +8 -8
  30. package/dist_ts/data/networkaddressplan.js +4 -4
  31. package/dist_ts/data/networkauthority.d.ts +20 -22
  32. package/dist_ts/data/networkauthority.js +26 -28
  33. package/dist_ts/data/networkcredential.d.ts +10 -10
  34. package/dist_ts/data/networkcredential.js +16 -16
  35. package/dist_ts/data/networkdelivery.d.ts +18 -18
  36. package/dist_ts/data/networkdelivery.js +16 -16
  37. package/dist_ts/data/networklease.d.ts +18 -7
  38. package/dist_ts/data/networklease.js +29 -7
  39. package/dist_ts/data/networknode.d.ts +2 -2
  40. package/dist_ts/data/networkprojection.d.ts +59 -60
  41. package/dist_ts/data/networkprojection.js +53 -57
  42. package/dist_ts/data/networkprotection.d.ts +27 -28
  43. package/dist_ts/data/networkprotection.js +36 -36
  44. package/dist_ts/data/networkrouter.d.ts +11 -11
  45. package/dist_ts/data/networkrouter.js +1 -1
  46. package/dist_ts/data/networksigning.d.ts +16 -18
  47. package/dist_ts/data/networksigning.js +20 -21
  48. package/dist_ts/data/nodecredential.d.ts +4 -6
  49. package/dist_ts/data/nodeenrollment.d.ts +35 -40
  50. package/dist_ts/data/nodeenrollment.js +57 -72
  51. package/dist_ts/data/privatenetwork.d.ts +21 -24
  52. package/dist_ts/data/privatenetwork.js +39 -54
  53. package/dist_ts/data/runtimeassignment.d.ts +50 -34
  54. package/dist_ts/data/runtimeassignment.js +79 -45
  55. package/dist_ts/data/runtimeconfig.d.ts +21 -22
  56. package/dist_ts/data/runtimeconfig.js +24 -24
  57. package/dist_ts/data/runtimesession.d.ts +100 -62
  58. package/dist_ts/data/runtimesession.js +98 -79
  59. package/dist_ts/data/runtimeterminalreceipt.d.ts +28 -29
  60. package/dist_ts/data/runtimeterminalreceipt.js +42 -42
  61. package/dist_ts/data/secret.d.ts +18 -25
  62. package/dist_ts/data/secret.js +12 -52
  63. package/dist_ts/data/servicecredential.d.ts +14 -16
  64. package/dist_ts/data/servicecredential.js +17 -28
  65. package/dist_ts/data/serviceruntime.d.ts +7 -7
  66. package/dist_ts/data/serviceruntime.js +4 -4
  67. package/dist_ts/data/sparknodereport.d.ts +27 -14
  68. package/dist_ts/data/sparknodereport.js +35 -28
  69. package/dist_ts/data/webpush.d.ts +0 -1
  70. package/dist_ts/index.d.ts +2 -1
  71. package/dist_ts/index.js +3 -2
  72. package/dist_ts/platform/objectstorageretention.d.ts +21 -24
  73. package/dist_ts/platform/objectstorageretention.js +10 -17
  74. package/dist_ts/platform/storage.d.ts +1 -3
  75. package/dist_ts/platform/storagemigration.d.ts +0 -7
  76. package/dist_ts/platform/storagemigration.golden.d.ts +0 -1
  77. package/dist_ts/platform/storagemigration.golden.js +8 -12
  78. package/dist_ts/platform/storagemigration.js +6 -51
  79. package/dist_ts/platform/types.d.ts +2 -2
  80. package/dist_ts/private/canonicaljson.d.ts +9 -1
  81. package/dist_ts/private/canonicaljson.js +14 -2
  82. package/dist_ts/private/dataproperties.d.ts +62 -0
  83. package/dist_ts/private/dataproperties.js +117 -0
  84. package/dist_ts/private/networkcontract.d.ts +10 -10
  85. package/dist_ts/private/networkcontract.js +1 -1
  86. package/dist_ts/private/releaseversion.d.ts +37 -0
  87. package/dist_ts/private/releaseversion.js +48 -0
  88. package/dist_ts/private/runtimepolicy.d.ts +2 -2
  89. package/dist_ts/private/runtimepolicy.js +1 -1
  90. package/dist_ts/private/tokenlist.d.ts +9 -0
  91. package/dist_ts/private/tokenlist.js +23 -0
  92. package/dist_ts/protocol.d.ts +137 -0
  93. package/dist_ts/protocol.js +255 -0
  94. package/dist_ts/requests/admin.d.ts +8 -8
  95. package/dist_ts/requests/backup.d.ts +102 -112
  96. package/dist_ts/requests/cluster.d.ts +10 -10
  97. package/dist_ts/requests/clusteringress.d.ts +9 -0
  98. package/dist_ts/requests/clusteringress.js +1 -1
  99. package/dist_ts/requests/config.d.ts +3 -3
  100. package/dist_ts/requests/corestore.d.ts +0 -3
  101. package/dist_ts/requests/corestore.js +5 -9
  102. package/dist_ts/requests/network.d.ts +30 -30
  103. package/dist_ts/requests/node.d.ts +15 -28
  104. package/dist_ts/requests/pallet.d.ts +13 -13
  105. package/dist_ts/requests/runtimesession.d.ts +53 -41
  106. package/dist_ts/requests/secret.d.ts +4 -5
  107. package/dist_ts/requests/secret.js +1 -5
  108. package/dist_ts/runtime.corestore.d.ts +56 -74
  109. package/dist_ts/runtime.corestore.databaseexportpackage.d.ts +30 -36
  110. package/dist_ts/runtime.corestore.databaseexportpackage.golden.d.ts +11 -12
  111. package/dist_ts/runtime.corestore.databaseexportpackage.golden.js +54 -71
  112. package/dist_ts/runtime.corestore.databaseexportpackage.js +9 -25
  113. package/dist_ts/runtime.corestore.js +35 -135
  114. package/dist_ts/runtime.corestore.relay.js +2 -7
  115. package/dist_ts/runtime.d.ts +38 -48
  116. package/dist_ts/runtime.js +20 -77
  117. package/dist_ts/runtime.networkprojection.d.ts +4 -4
  118. package/dist_ts/runtime.networkprojection.js +18 -18
  119. package/dist_ts/runtime.workloadinit.d.ts +23 -24
  120. package/dist_ts/runtime.workloadinit.js +6 -13
  121. package/package.json +2 -2
  122. package/readme.md +421 -270
  123. package/ts/00_commitinfo_data.ts +3 -3
  124. package/ts/appstore/types.ts +4 -6
  125. package/ts/data/backup.ts +3 -38
  126. package/ts/data/cluster.ts +51 -34
  127. package/ts/data/clusteringress.ts +12 -12
  128. package/ts/data/clusternode.ts +204 -232
  129. package/ts/data/coremail.runtime.ts +4 -21
  130. package/ts/data/coremail.ts +3 -7
  131. package/ts/data/deploymentadoption.ts +46 -4
  132. package/ts/data/deploymentoperation.ts +16 -1
  133. package/ts/data/deploymentpreflight.ts +0 -2
  134. package/ts/data/immutableimage.ts +71 -24
  135. package/ts/data/index.ts +1 -1
  136. package/ts/data/isolatedrestore.golden.ts +5 -9
  137. package/ts/data/isolatedrestore.ts +339 -54
  138. package/ts/data/mail.ts +1 -1
  139. package/ts/data/networkaddressplan.ts +13 -13
  140. package/ts/data/networkauthority.ts +42 -46
  141. package/ts/data/networkcredential.ts +24 -24
  142. package/ts/data/networkdelivery.ts +27 -27
  143. package/ts/data/networklease.ts +40 -13
  144. package/ts/data/networknode.ts +2 -2
  145. package/ts/data/networkprojection.ts +119 -124
  146. package/ts/data/networkprotection.ts +54 -55
  147. package/ts/data/networkrouter.ts +14 -14
  148. package/ts/data/networksigning.ts +29 -32
  149. package/ts/data/nodecredential.ts +4 -6
  150. package/ts/data/nodeenrollment.ts +96 -114
  151. package/ts/data/privatenetwork.ts +58 -74
  152. package/ts/data/runtimeassignment.ts +143 -94
  153. package/ts/data/runtimeconfig.ts +45 -46
  154. package/ts/data/runtimesession.ts +185 -138
  155. package/ts/data/runtimeterminalreceipt.ts +72 -73
  156. package/ts/data/secret.ts +28 -76
  157. package/ts/data/servicecredential.ts +33 -43
  158. package/ts/data/serviceruntime.ts +11 -11
  159. package/ts/data/sparknodereport.ts +48 -36
  160. package/ts/data/webpush.ts +0 -1
  161. package/ts/index.ts +2 -0
  162. package/ts/platform/objectstorageretention.ts +40 -54
  163. package/ts/platform/storage.ts +1 -3
  164. package/ts/platform/storagemigration.golden.ts +7 -11
  165. package/ts/platform/storagemigration.ts +5 -55
  166. package/ts/platform/types.ts +2 -2
  167. package/ts/private/canonicaljson.ts +13 -1
  168. package/ts/private/dataproperties.ts +139 -0
  169. package/ts/private/networkcontract.ts +9 -9
  170. package/ts/private/releaseversion.ts +70 -0
  171. package/ts/private/runtimepolicy.ts +2 -2
  172. package/ts/private/tokenlist.ts +30 -0
  173. package/ts/protocol.ts +364 -0
  174. package/ts/requests/admin.ts +11 -11
  175. package/ts/requests/backup.ts +112 -117
  176. package/ts/requests/cluster.ts +13 -13
  177. package/ts/requests/clusteringress.ts +9 -0
  178. package/ts/requests/config.ts +3 -3
  179. package/ts/requests/corestore.ts +4 -16
  180. package/ts/requests/network.ts +40 -40
  181. package/ts/requests/node.ts +21 -38
  182. package/ts/requests/pallet.ts +17 -17
  183. package/ts/requests/runtimesession.ts +63 -51
  184. package/ts/requests/secret.ts +5 -10
  185. package/ts/runtime.corestore.databaseexportpackage.golden.ts +77 -95
  186. package/ts/runtime.corestore.databaseexportpackage.ts +51 -73
  187. package/ts/runtime.corestore.relay.ts +1 -10
  188. package/ts/runtime.corestore.ts +118 -268
  189. package/ts/runtime.networkprojection.ts +30 -30
  190. package/ts/runtime.ts +83 -158
  191. package/ts/runtime.workloadinit.ts +36 -48
  192. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.d.ts +0 -902
  193. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.d.ts +0 -35
  194. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.js +0 -315
  195. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.js +0 -3315
  196. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.d.ts +0 -291
  197. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.d.ts +0 -30
  198. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.js +0 -408
  199. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.js +0 -1126
  200. package/ts/runtime.cloudlylegacydeploymentsettlement.golden.ts +0 -421
  201. package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.ts +0 -510
  202. package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.ts +0 -2470
  203. package/ts/runtime.cloudlylegacydeploymentsettlement.ts +0 -6630
@@ -1,6 +1,9 @@
1
+ import { isReleaseVersion } from '../private/releaseversion.js';
2
+ import { readExactDataRecord } from '../private/dataproperties.js';
3
+ import { protocolRefusalHttpStatus, readProtocolOffer, type IProtocolOffer } from '../protocol.js';
4
+
1
5
  /** Host observations only. Container and workload readiness belong to Pallet. */
2
- export interface ISparkNodeReportV2 {
3
- schemaVersion: 2;
6
+ export interface ISparkNodeReport {
4
7
  observedAt: number;
5
8
  metrics: {
6
9
  cpuUsagePercent: number;
@@ -21,51 +24,55 @@ export interface ISparkNodeReportV2 {
21
24
  }
22
25
 
23
26
  /** HTTPS POST body. Never log, persist, or return nodeToken. */
24
- export interface ISparkNodeHeartbeatRequestV2 {
27
+ export interface ISparkNodeHeartbeatRequest {
25
28
  nodeId: string;
26
29
  nodeToken: string;
27
- report: ISparkNodeReportV2;
30
+ /** Read and negotiated before the node is authenticated; see `sparkNodeHeartbeatContract`. */
31
+ protocol: IProtocolOffer;
32
+ report: ISparkNodeReport;
28
33
  }
29
34
 
30
35
  /** Historical acceptance evidence, not current authority or workload readiness. */
31
- export interface ISparkNodeHeartbeatReceiptV2 {
32
- schemaVersion: 2;
36
+ export interface ISparkNodeHeartbeatReceipt {
33
37
  accepted: true;
34
38
  nodeId: string;
35
39
  credentialGeneration: number;
36
40
  sessionEpoch: number;
37
41
  acceptedAt: number;
42
+ /** The controller's offer, so an accepted node judges the session it just reported into. */
43
+ protocol: IProtocolOffer;
38
44
  }
39
45
 
40
46
  /** Persist only after the server's current Spark credential transaction commits. */
41
- export interface ISparkNodeReportRecordV2 {
42
- report: ISparkNodeReportV2;
43
- receipt: ISparkNodeHeartbeatReceiptV2;
47
+ export interface ISparkNodeReportRecord {
48
+ report: ISparkNodeReport;
49
+ receipt: ISparkNodeHeartbeatReceipt;
44
50
  }
45
51
 
46
- export const sparkNodeHeartbeatV2Contract = Object.freeze({
47
- endpoint: '/spark/v2/nodes/heartbeat',
52
+ /**
53
+ * The node runtime's own heartbeat route.
54
+ *
55
+ * Judged in the one server order `protocol.readProtocolOffer` states, against this server's
56
+ * `sparkNode` offer and within `maxRequestBytes`. The route carries no typed error, so a refusal
57
+ * is answered with `protocolRefusalStatus` and an `IProtocolRefusal` body — before the report is
58
+ * snapshotted and before the node is authenticated, because a route that authenticated first would
59
+ * answer a peer of another major with a credential verdict it cannot act on.
60
+ */
61
+ export const sparkNodeHeartbeatContract = Object.freeze({
62
+ endpoint: '/spark/nodes/heartbeat',
48
63
  maxRequestBytes: 8192,
49
64
  maxResponseBytes: 2048,
65
+ protocolRefusalStatus: protocolRefusalHttpStatus,
50
66
  } as const);
51
67
 
52
68
  const fail = (): never => { throw new Error('Spark node heartbeat contract rejected.'); };
53
69
  function requireValue(valueArg: unknown): asserts valueArg {
54
70
  if (!valueArg) fail();
55
71
  }
72
+ /** Exactly these keys, carrying data: the shared reader refuses an accessor instead of running it. */
56
73
  const record = (valueArg: unknown, keysArg: readonly string[]): Record<string, unknown> => {
57
- requireValue(valueArg !== null && typeof valueArg === 'object' && !Array.isArray(valueArg));
58
- const prototype = Object.getPrototypeOf(valueArg);
59
- requireValue(prototype === Object.prototype || prototype === null);
60
- const keys = Reflect.ownKeys(valueArg);
61
- requireValue(keys.length === keysArg.length);
62
- const result: Record<string, unknown> = Object.create(null);
63
- for (const key of keys) {
64
- requireValue(typeof key === 'string' && keysArg.includes(key));
65
- const descriptor = Object.getOwnPropertyDescriptor(valueArg, key);
66
- requireValue(descriptor?.enumerable && Object.hasOwn(descriptor, 'value'));
67
- result[key] = descriptor.value;
68
- }
74
+ const result = readExactDataRecord(valueArg, keysArg);
75
+ requireValue(result !== null);
69
76
  return result;
70
77
  };
71
78
  const text = (valueArg: unknown, patternArg: RegExp, maxLengthArg: number): string => {
@@ -73,6 +80,11 @@ const text = (valueArg: unknown, patternArg: RegExp, maxLengthArg: number): stri
73
80
  && valueArg.match(patternArg)?.[0] === valueArg);
74
81
  return valueArg;
75
82
  };
83
+ // The shared grammar, not a copy of it: a stored bundle version has to be one the release
84
+ // comparison can order, and a second rule here would be a second answer to that question.
85
+ const releaseVersion = (valueArg: unknown): string => (
86
+ isReleaseVersion(valueArg) ? valueArg : fail()
87
+ );
76
88
  const number = (valueArg: unknown, maxArg = Number.MAX_SAFE_INTEGER): number => {
77
89
  requireValue(typeof valueArg === 'number' && Number.isFinite(valueArg)
78
90
  && !Object.is(valueArg, -0) && valueArg >= 0 && valueArg <= maxArg);
@@ -85,9 +97,8 @@ const integer = (valueArg: unknown, minimumArg: number): number => {
85
97
  };
86
98
 
87
99
  /** Exact inert JSON records; detaches and freezes before any consumer awaits. */
88
- export const snapshotSparkNodeReportV2 = (valueArg: unknown): ISparkNodeReportV2 => {
89
- const value = record(valueArg, ['schemaVersion', 'observedAt', 'metrics', 'runtime']);
90
- requireValue(value.schemaVersion === 2);
100
+ export const snapshotSparkNodeReport = (valueArg: unknown): ISparkNodeReport => {
101
+ const value = record(valueArg, ['observedAt', 'metrics', 'runtime']);
91
102
  const metrics = record(value.metrics, ['cpuUsagePercent', 'memoryUsedMB', 'memoryAvailableMB',
92
103
  'diskUsedGB', 'diskAvailableGB']);
93
104
  const runtime = record(value.runtime, ['hostname', 'platform', 'arch', 'osRelease', 'bundle',
@@ -98,7 +109,6 @@ export const snapshotSparkNodeReportV2 = (valueArg: unknown): ISparkNodeReportV2
98
109
  requireValue(hostname.split('.').every((label) => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label)));
99
110
  const bundle = record(runtime.bundle, ['version', 'sourceCommit', 'manifestSha256']);
100
111
  return Object.freeze({
101
- schemaVersion: 2,
102
112
  observedAt: integer(value.observedAt, 1),
103
113
  metrics: Object.freeze({
104
114
  cpuUsagePercent: number(metrics.cpuUsagePercent, 100),
@@ -109,7 +119,7 @@ export const snapshotSparkNodeReportV2 = (valueArg: unknown): ISparkNodeReportV2
109
119
  hostname, platform: 'linux', arch: runtime.arch as 'x86_64' | 'aarch64',
110
120
  osRelease: text(runtime.osRelease, /^[\x20-\x7e]+$/, 256),
111
121
  bundle: Object.freeze({
112
- version: text(bundle.version, /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/, 64),
122
+ version: releaseVersion(bundle.version),
113
123
  sourceCommit: text(bundle.sourceCommit, /^[a-f0-9]{40}$/, 40),
114
124
  manifestSha256: text(bundle.manifestSha256, /^[a-f0-9]{64}$/, 64),
115
125
  }),
@@ -118,23 +128,25 @@ export const snapshotSparkNodeReportV2 = (valueArg: unknown): ISparkNodeReportV2
118
128
  });
119
129
  };
120
130
 
121
- export const snapshotSparkNodeHeartbeatRequestV2 = (valueArg: unknown): ISparkNodeHeartbeatRequestV2 => {
122
- const value = record(valueArg, ['nodeId', 'nodeToken', 'report']);
131
+ export const snapshotSparkNodeHeartbeatRequest = (valueArg: unknown): ISparkNodeHeartbeatRequest => {
132
+ const value = record(valueArg, ['nodeId', 'nodeToken', 'protocol', 'report']);
123
133
  return Object.freeze({
124
134
  nodeId: text(value.nodeId, /^[A-Za-z0-9_-]+$/, 128),
125
135
  // Both freshly enrolled and explicitly adopted credentials use this bearer format.
126
136
  nodeToken: text(value.nodeToken, /^secureToken[a-f0-9]{64}$/, 75),
127
- report: snapshotSparkNodeReportV2(value.report),
137
+ protocol: readProtocolOffer(value),
138
+ report: snapshotSparkNodeReport(value.report),
128
139
  });
129
140
  };
130
141
 
131
- export const snapshotSparkNodeHeartbeatReceiptV2 = (valueArg: unknown): ISparkNodeHeartbeatReceiptV2 => {
132
- const value = record(valueArg, ['schemaVersion', 'accepted', 'nodeId', 'credentialGeneration',
133
- 'sessionEpoch', 'acceptedAt']);
134
- requireValue(value.schemaVersion === 2 && value.accepted === true);
142
+ export const snapshotSparkNodeHeartbeatReceipt = (valueArg: unknown): ISparkNodeHeartbeatReceipt => {
143
+ const value = record(valueArg, ['accepted', 'nodeId', 'credentialGeneration',
144
+ 'sessionEpoch', 'acceptedAt', 'protocol']);
145
+ requireValue(value.accepted === true);
135
146
  return Object.freeze({
136
- schemaVersion: 2, accepted: true, nodeId: text(value.nodeId, /^[A-Za-z0-9_-]+$/, 128),
147
+ accepted: true, nodeId: text(value.nodeId, /^[A-Za-z0-9_-]+$/, 128),
137
148
  credentialGeneration: integer(value.credentialGeneration, 1),
138
149
  sessionEpoch: integer(value.sessionEpoch, 0), acceptedAt: integer(value.acceptedAt, 1),
150
+ protocol: readProtocolOffer(value),
139
151
  });
140
152
  };
@@ -101,7 +101,6 @@ export interface IWebPushBinding {
101
101
  * from their authenticated API after receiving this event.
102
102
  */
103
103
  export interface IWebPushNotificationPayload {
104
- schemaVersion: 1;
105
104
  event: 'notificationAvailable';
106
105
  eventId: string;
107
106
  /** Same-origin application route beginning with a single slash. */
package/ts/index.ts CHANGED
@@ -2,6 +2,7 @@ import * as data from './data/index.js';
2
2
  import * as appstore from './appstore/index.js';
3
3
  import * as platform from './platform/index.js';
4
4
  import * as platformservice from './platformservice/index.js';
5
+ import * as protocol from './protocol.js';
5
6
  import * as requests from './requests/index.js';
6
7
 
7
8
  export {
@@ -9,5 +10,6 @@ export {
9
10
  data,
10
11
  platform,
11
12
  platformservice,
13
+ protocol,
12
14
  requests
13
15
  }
@@ -1,19 +1,19 @@
1
1
  import { createCanonicalJsonSha256Hex } from '../private/canonicaljson.js';
2
2
  import { validatePlatformObjectStorageBucketName } from './objectstorage.js';
3
+ import { hasExactKeys } from '../private/dataproperties.js';
3
4
 
4
5
  export const objectStorageRetentionLimits = Object.freeze({
5
6
  maximumPolicyIdLength: 96,
6
7
  maximumRetentionDurationSeconds: 100 * 366 * 24 * 60 * 60,
7
8
  });
8
9
 
9
- export interface IObjectStorageRetentionIntentV1 {
10
+ export interface IObjectStorageRetentionIntent {
10
11
  mode: 'compliance';
11
12
  policyId: string;
12
13
  retentionDurationSeconds: number;
13
14
  }
14
15
 
15
- export interface IObjectStorageRetentionCapabilityEvidenceV1 {
16
- version: 1;
16
+ export interface IObjectStorageRetentionCapabilityEvidence {
17
17
  supported: true;
18
18
  backend: 'standalone' | 'clustered';
19
19
  modes: ['compliance'];
@@ -21,16 +21,15 @@ export interface IObjectStorageRetentionCapabilityEvidenceV1 {
21
21
  retainedMultipartSupported: false;
22
22
  }
23
23
 
24
- export interface IObjectStorageRetentionAuthorityV1 {
24
+ export interface IObjectStorageRetentionAuthority {
25
25
  serviceId: string;
26
26
  bindingId: string;
27
27
  reconciliationGeneration: number;
28
28
  bindingRequestDigest: string;
29
29
  }
30
30
 
31
- export interface IObjectStorageRetentionSentinelEvidenceV1
32
- extends IObjectStorageRetentionIntentV1 {
33
- version: 1;
31
+ export interface IObjectStorageRetentionSentinelEvidence
32
+ extends IObjectStorageRetentionIntent {
34
33
  createdAt: number;
35
34
  retainUntil: number;
36
35
  payloadSha256: string;
@@ -38,33 +37,32 @@ extends IObjectStorageRetentionIntentV1 {
38
37
  etag: string;
39
38
  }
40
39
 
41
- export interface IObjectStorageRetentionReceiptV1
42
- extends IObjectStorageRetentionIntentV1 {
40
+ export interface IObjectStorageRetentionReceipt
41
+ extends IObjectStorageRetentionIntent {
43
42
  bucketName: string;
44
43
  configuredAt: number;
45
44
  sentinelKey: string;
46
- sentinel: IObjectStorageRetentionSentinelEvidenceV1;
45
+ sentinel: IObjectStorageRetentionSentinelEvidence;
47
46
  }
48
47
 
49
- export interface IObjectStorageRetentionEvidenceV1 {
50
- intent: IObjectStorageRetentionIntentV1;
48
+ export interface IObjectStorageRetentionEvidence {
49
+ intent: IObjectStorageRetentionIntent;
51
50
  intentSha256: string;
52
- authority: IObjectStorageRetentionAuthorityV1;
53
- capability: IObjectStorageRetentionCapabilityEvidenceV1;
54
- receipt: IObjectStorageRetentionReceiptV1;
51
+ authority: IObjectStorageRetentionAuthority;
52
+ capability: IObjectStorageRetentionCapabilityEvidence;
53
+ receipt: IObjectStorageRetentionReceipt;
55
54
  verifiedAt: number;
56
55
  }
57
56
 
58
57
  /** Value-free desired intent with optional exact provider evidence. */
59
- export interface IObjectStorageRetentionBindingV1 {
60
- schemaVersion: 1;
61
- intent: IObjectStorageRetentionIntentV1;
62
- evidence?: IObjectStorageRetentionEvidenceV1;
58
+ export interface IObjectStorageRetentionBinding {
59
+ intent: IObjectStorageRetentionIntent;
60
+ evidence?: IObjectStorageRetentionEvidence;
63
61
  }
64
62
 
65
- export interface IObjectStorageRetentionEvidenceExpectationV1
66
- extends IObjectStorageRetentionAuthorityV1 {
67
- intent: IObjectStorageRetentionIntentV1;
63
+ export interface IObjectStorageRetentionEvidenceExpectation
64
+ extends IObjectStorageRetentionAuthority {
65
+ intent: IObjectStorageRetentionIntent;
68
66
  bucketName: string;
69
67
  }
70
68
 
@@ -72,6 +70,11 @@ const policyIdPattern = /^[A-Za-z0-9._-]{1,96}$/;
72
70
  const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/;
73
71
  const sha256Pattern = /^[a-f0-9]{64}$/;
74
72
  const etagPattern = /^[a-f0-9]{32}$/;
73
+ /**
74
+ * ObjectStorage's own on-disk sentinel key, pinned verbatim: `@lossless.org/objectstorage` writes
75
+ * `.smartstorage-retention/v1/sentinel-<sha256>.json`, so the `v1` segment belongs to that
76
+ * package's object layout and is not a serve.zone name (naming rule N10 — foreign bytes stay).
77
+ */
75
78
  const sentinelKeyPattern = /^\.smartstorage-retention\/v1\/sentinel-[a-f0-9]{64}\.json$/;
76
79
 
77
80
  const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => (
@@ -82,16 +85,6 @@ const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => (
82
85
  || Object.getPrototypeOf(valueArg) === null)
83
86
  );
84
87
 
85
- const hasExactKeys = (
86
- valueArg: Record<string, unknown>,
87
- keysArg: readonly string[],
88
- ): boolean => {
89
- const actualKeys = Object.keys(valueArg).sort();
90
- const expectedKeys = [...keysArg].sort();
91
- return actualKeys.length === expectedKeys.length
92
- && actualKeys.every((keyArg, indexArg) => keyArg === expectedKeys[indexArg]);
93
- };
94
-
95
88
  const isPositiveSafeInteger = (valueArg: unknown): valueArg is number => (
96
89
  Number.isSafeInteger(valueArg) && (valueArg as number) > 0
97
90
  );
@@ -135,7 +128,7 @@ export const createObjectStorageRetentionRetainUntil = (
135
128
  };
136
129
 
137
130
  export const createObjectStorageRetentionIntentDigestInput = (
138
- intentArg: IObjectStorageRetentionIntentV1,
131
+ intentArg: IObjectStorageRetentionIntent,
139
132
  ): string => {
140
133
  const errors = validateObjectStorageRetentionIntent(intentArg);
141
134
  if (errors.length > 0) throw new Error(errors[0]);
@@ -146,12 +139,11 @@ export const createObjectStorageRetentionIntentDigestInput = (
146
139
  policyId: intentArg.policyId,
147
140
  retentionDurationSeconds: intentArg.retentionDurationSeconds,
148
141
  },
149
- version: 1,
150
142
  });
151
143
  };
152
144
 
153
145
  export const computeObjectStorageRetentionIntentSha256 = async (
154
- intentArg: IObjectStorageRetentionIntentV1,
146
+ intentArg: IObjectStorageRetentionIntent,
155
147
  ): Promise<string> => createCanonicalJsonSha256Hex(
156
148
  createObjectStorageRetentionIntentDigestInput(intentArg),
157
149
  (reasonArg) => { throw new Error(reasonArg); },
@@ -162,12 +154,12 @@ export const objectStorageRetentionIntentsEqual = (
162
154
  rightArg: unknown,
163
155
  ): boolean => validateObjectStorageRetentionIntent(leftArg).length === 0
164
156
  && validateObjectStorageRetentionIntent(rightArg).length === 0
165
- && (leftArg as IObjectStorageRetentionIntentV1).mode
166
- === (rightArg as IObjectStorageRetentionIntentV1).mode
167
- && (leftArg as IObjectStorageRetentionIntentV1).policyId
168
- === (rightArg as IObjectStorageRetentionIntentV1).policyId
169
- && (leftArg as IObjectStorageRetentionIntentV1).retentionDurationSeconds
170
- === (rightArg as IObjectStorageRetentionIntentV1).retentionDurationSeconds;
157
+ && (leftArg as IObjectStorageRetentionIntent).mode
158
+ === (rightArg as IObjectStorageRetentionIntent).mode
159
+ && (leftArg as IObjectStorageRetentionIntent).policyId
160
+ === (rightArg as IObjectStorageRetentionIntent).policyId
161
+ && (leftArg as IObjectStorageRetentionIntent).retentionDurationSeconds
162
+ === (rightArg as IObjectStorageRetentionIntent).retentionDurationSeconds;
171
163
 
172
164
  const retentionIntentFieldsEqual = (
173
165
  leftArg: unknown,
@@ -183,7 +175,7 @@ const retentionIntentFieldsEqual = (
183
175
 
184
176
  export const validateObjectStorageRetentionEvidence = async (
185
177
  evidenceArg: unknown,
186
- expectedArg: IObjectStorageRetentionEvidenceExpectationV1,
178
+ expectedArg: IObjectStorageRetentionEvidenceExpectation,
187
179
  ): Promise<string[]> => {
188
180
  if (!isRecord(evidenceArg)
189
181
  || !hasExactKeys(evidenceArg, [
@@ -197,7 +189,7 @@ export const validateObjectStorageRetentionEvidence = async (
197
189
  return ['objectstorage retention evidence must use its exact schema'];
198
190
  }
199
191
  const errors = validateObjectStorageRetentionIntent(evidenceArg.intent);
200
- const intent = evidenceArg.intent as IObjectStorageRetentionIntentV1;
192
+ const intent = evidenceArg.intent as IObjectStorageRetentionIntent;
201
193
  if (!retentionIntentFieldsEqual(intent, expectedArg.intent)) {
202
194
  errors.push('objectstorage retention evidence intent must equal the expected intent');
203
195
  }
@@ -233,14 +225,12 @@ export const validateObjectStorageRetentionEvidence = async (
233
225
  const capability = evidenceArg.capability;
234
226
  if (!isRecord(capability)
235
227
  || !hasExactKeys(capability, [
236
- 'version',
237
228
  'supported',
238
229
  'backend',
239
230
  'modes',
240
231
  'atomicCreateOnly',
241
232
  'retainedMultipartSupported',
242
233
  ])
243
- || capability.version !== 1
244
234
  || capability.supported !== true
245
235
  || (capability.backend !== 'standalone' && capability.backend !== 'clustered')
246
236
  || !Array.isArray(capability.modes)
@@ -283,7 +273,6 @@ export const validateObjectStorageRetentionEvidence = async (
283
273
  const sentinel = receipt.sentinel;
284
274
  if (!isRecord(sentinel)
285
275
  || !hasExactKeys(sentinel, [
286
- 'version',
287
276
  'mode',
288
277
  'policyId',
289
278
  'retentionDurationSeconds',
@@ -296,7 +285,7 @@ export const validateObjectStorageRetentionEvidence = async (
296
285
  errors.push('objectstorage retention sentinel evidence must use its exact schema');
297
286
  return errors;
298
287
  }
299
- if (sentinel.version !== 1 || !retentionIntentFieldsEqual(sentinel, intent)) {
288
+ if (!retentionIntentFieldsEqual(sentinel, intent)) {
300
289
  errors.push('objectstorage retention sentinel intent must equal its evidence intent');
301
290
  }
302
291
  if (!isPositiveSafeInteger(sentinel.createdAt)
@@ -331,17 +320,14 @@ export const validateObjectStorageRetentionEvidence = async (
331
320
 
332
321
  export const validateObjectStorageRetentionBinding = async (
333
322
  bindingArg: unknown,
334
- expectedArg: IObjectStorageRetentionEvidenceExpectationV1,
323
+ expectedArg: IObjectStorageRetentionEvidenceExpectation,
335
324
  ): Promise<string[]> => {
336
325
  if (!isRecord(bindingArg)
337
- || !Object.hasOwn(bindingArg, 'schemaVersion')
338
326
  || !Object.hasOwn(bindingArg, 'intent')
339
- || Object.keys(bindingArg).some((keyArg) => !['schemaVersion', 'intent', 'evidence'].includes(keyArg))) {
327
+ || Object.keys(bindingArg).some((keyArg) => !['intent', 'evidence'].includes(keyArg))) {
340
328
  return ['objectstorage retention binding must use its exact schema'];
341
329
  }
342
- const errors = bindingArg.schemaVersion === 1
343
- ? validateObjectStorageRetentionIntent(bindingArg.intent)
344
- : ['objectstorage retention binding schemaVersion must be 1'];
330
+ const errors = validateObjectStorageRetentionIntent(bindingArg.intent);
345
331
  if (!retentionIntentFieldsEqual(bindingArg.intent, expectedArg.intent)) {
346
332
  errors.push('objectstorage retention binding intent must equal the trusted expected intent');
347
333
  }
@@ -356,7 +342,7 @@ export const validateObjectStorageRetentionBinding = async (
356
342
 
357
343
  export const validatePlatformBindingObjectStorageRetention = async (
358
344
  bindingArg: unknown,
359
- expectedArg: IObjectStorageRetentionEvidenceExpectationV1,
345
+ expectedArg: IObjectStorageRetentionEvidenceExpectation,
360
346
  ): Promise<string[]> => {
361
347
  if (!isRecord(bindingArg)) {
362
348
  return ['platform objectstorage retention binding must be an object'];
@@ -102,7 +102,6 @@ export type TStorageClassCapabilities =
102
102
  * addresses, mount options, principals, and credential values are private.
103
103
  */
104
104
  export interface IStorageCapabilityAdvertisement {
105
- schemaVersion: 2;
106
105
  featureIds: string[];
107
106
  classes: TStorageClassCapabilities[];
108
107
  }
@@ -131,7 +130,7 @@ export interface IObjectStorageFileDelivery {
131
130
  * JSON object with endpoint, bucket, region, accessKeyId,
132
131
  * secretAccessKey, and optional sessionToken fields.
133
132
  */
134
- format: 'servezone-object-storage-v1';
133
+ format: 'servezone-object-storage';
135
134
  uid: number;
136
135
  gid: number;
137
136
  mode: 0o400 | 0o440;
@@ -181,7 +180,6 @@ export interface IResolvedStorageCapacity {
181
180
  }
182
181
 
183
182
  export interface IResolvedStorageBindingBase {
184
- schemaVersion: 2;
185
183
  /** Stable binding/allocation identity. */
186
184
  id: string;
187
185
  serviceId: string;
@@ -37,7 +37,6 @@ const delivery = {
37
37
  const credentialManagementScope = 'platform:object-storage-binding';
38
38
 
39
39
  const sourceBinding: TActiveObjectStorageBinding = {
40
- schemaVersion: 2,
41
40
  id: 'binding-object',
42
41
  serviceId: 'service-app',
43
42
  requestId: 'object-data',
@@ -97,7 +96,6 @@ const target = {
97
96
 
98
97
  const preparationSnapshotPayload:
99
98
  TUnfencedObjectStorageBindingControlSnapshotDigestPayload = {
100
- schemaVersion: 2,
101
99
  binding: sourceBinding,
102
100
  recordRevision: 12,
103
101
  capturedAt: 1_700_000_000_010,
@@ -106,11 +104,10 @@ TUnfencedObjectStorageBindingControlSnapshotDigestPayload = {
106
104
  const preparationSnapshot: IUnfencedObjectStorageBindingControlSnapshot = {
107
105
  ...preparationSnapshotPayload,
108
106
  snapshotDigest:
109
- 'ac8166a9888144d11dc7419f60be055eeb6f392ec41a254a9f9b2a7b3df1fba7',
107
+ '59e518dd446cde4ff8899d628f017e9410674e3d52ce6be5f7965c1ec04c80a3',
110
108
  };
111
109
 
112
110
  const prepareRequest: IObjectStorageMigrationPrepareRequest = {
113
- schemaVersion: 2,
114
111
  migrationId: 'migration-object-capacity',
115
112
  sourceControlSnapshot: preparationSnapshot,
116
113
  workloadGeneration: 8,
@@ -140,7 +137,6 @@ const candidateBinding: TStagedObjectStorageBinding = {
140
137
  };
141
138
 
142
139
  export const storageMigrationCanonicalDigestGoldenVectors = deepFreezeValue({
143
- version: 2 as const,
144
140
  targetRequest: {
145
141
  input: target.request,
146
142
  canonicalJson: '{"accessMode":"readWrite","capacity":{"limit":"1TiB","request":"500GiB"},"delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"id":"object-data","kind":"objectStorage","reclaimPolicy":"retain","storageClass":"backupCapacity"}',
@@ -148,17 +144,17 @@ export const storageMigrationCanonicalDigestGoldenVectors = deepFreezeValue({
148
144
  },
149
145
  preparationSnapshot: {
150
146
  input: preparationSnapshotPayload,
151
- canonicalJson: '{"binding":{"accessMode":"readWrite","capabilities":{"backup":true,"durability":"persistent","encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},"connection":{"bucket":"source-bucket","endpoint":"https://source.example.test","region":"local"},"createdAt":1700000000000,"credentialManagementScope":"platform:object-storage-binding","delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"generation":3,"id":"binding-object","kind":"objectStorage","observedGeneration":3,"policy":{"classId":"source-object","revision":"1"},"requestDigest":"1111111111111111111111111111111111111111111111111111111111111111","requestId":"object-data","resourceRef":"source-resource","schemaVersion":2,"serviceId":"service-app","status":"ready","updatedAt":1700000000001,"versioning":false},"capturedAt":1700000000010,"recordRevision":12,"schemaVersion":2}',
152
- sha256: 'ac8166a9888144d11dc7419f60be055eeb6f392ec41a254a9f9b2a7b3df1fba7',
147
+ canonicalJson: '{"binding":{"accessMode":"readWrite","capabilities":{"backup":true,"durability":"persistent","encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},"connection":{"bucket":"source-bucket","endpoint":"https://source.example.test","region":"local"},"createdAt":1700000000000,"credentialManagementScope":"platform:object-storage-binding","delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"generation":3,"id":"binding-object","kind":"objectStorage","observedGeneration":3,"policy":{"classId":"source-object","revision":"1"},"requestDigest":"1111111111111111111111111111111111111111111111111111111111111111","requestId":"object-data","resourceRef":"source-resource","serviceId":"service-app","status":"ready","updatedAt":1700000000001,"versioning":false},"capturedAt":1700000000010,"recordRevision":12}',
148
+ sha256: '59e518dd446cde4ff8899d628f017e9410674e3d52ce6be5f7965c1ec04c80a3',
153
149
  },
154
150
  prepareRequest: {
155
151
  input: prepareRequest,
156
- canonicalJson: '{"migrationId":"migration-object-capacity","schemaVersion":2,"sourceControlSnapshot":{"binding":{"accessMode":"readWrite","capabilities":{"backup":true,"durability":"persistent","encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},"connection":{"bucket":"source-bucket","endpoint":"https://source.example.test","region":"local"},"createdAt":1700000000000,"credentialManagementScope":"platform:object-storage-binding","delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"generation":3,"id":"binding-object","kind":"objectStorage","observedGeneration":3,"policy":{"classId":"source-object","revision":"1"},"requestDigest":"1111111111111111111111111111111111111111111111111111111111111111","requestId":"object-data","resourceRef":"source-resource","schemaVersion":2,"serviceId":"service-app","status":"ready","updatedAt":1700000000001,"versioning":false},"capturedAt":1700000000010,"recordRevision":12,"schemaVersion":2,"snapshotDigest":"ac8166a9888144d11dc7419f60be055eeb6f392ec41a254a9f9b2a7b3df1fba7"},"target":{"credentialManagementScope":"platform:object-storage-binding","policy":{"classId":"capacity-object","revision":"4"},"request":{"accessMode":"readWrite","capacity":{"limit":"1TiB","request":"500GiB"},"delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"id":"object-data","kind":"objectStorage","reclaimPolicy":"retain","storageClass":"backupCapacity"},"storageClass":{"kind":"objectStorage","purpose":"backup","required":{"backup":true,"durability":"persistent","performanceTier":"capacity"}}},"workloadGeneration":8}',
157
- sha256: '7682ed6f8c8b64d4c677ea91c04e4261bfe5326a3fb905d5da093f51e4b2dbbe',
152
+ canonicalJson: '{"migrationId":"migration-object-capacity","sourceControlSnapshot":{"binding":{"accessMode":"readWrite","capabilities":{"backup":true,"durability":"persistent","encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},"connection":{"bucket":"source-bucket","endpoint":"https://source.example.test","region":"local"},"createdAt":1700000000000,"credentialManagementScope":"platform:object-storage-binding","delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"generation":3,"id":"binding-object","kind":"objectStorage","observedGeneration":3,"policy":{"classId":"source-object","revision":"1"},"requestDigest":"1111111111111111111111111111111111111111111111111111111111111111","requestId":"object-data","resourceRef":"source-resource","serviceId":"service-app","status":"ready","updatedAt":1700000000001,"versioning":false},"capturedAt":1700000000010,"recordRevision":12,"snapshotDigest":"59e518dd446cde4ff8899d628f017e9410674e3d52ce6be5f7965c1ec04c80a3"},"target":{"credentialManagementScope":"platform:object-storage-binding","policy":{"classId":"capacity-object","revision":"4"},"request":{"accessMode":"readWrite","capacity":{"limit":"1TiB","request":"500GiB"},"delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"id":"object-data","kind":"objectStorage","reclaimPolicy":"retain","storageClass":"backupCapacity"},"storageClass":{"kind":"objectStorage","purpose":"backup","required":{"backup":true,"durability":"persistent","performanceTier":"capacity"}}},"workloadGeneration":8}',
153
+ sha256: '549cc9ec83ec3ce1b17c9379448a869d43eba911d540973e207672e4f2d82644',
158
154
  },
159
155
  candidateBinding: {
160
156
  input: candidateBinding,
161
- canonicalJson: '{"accessMode":"readWrite","capabilities":{"backup":true,"durability":"persistent","encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},"capacity":{"granted":"500GiB","limit":"1TiB","requested":"500GiB"},"connection":{"bucket":"destination-bucket","endpoint":"https://destination.example.test","region":"local"},"createdAt":1700000000000,"credentialManagementScope":"platform:object-storage-binding","delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"generation":4,"id":"binding-object","kind":"objectStorage","observedGeneration":3,"policy":{"classId":"capacity-object","revision":"4"},"requestDigest":"3f864862041002e25b790b3bc7afbe37c399a006f385e1def9f679a93d4fa173","requestId":"object-data","resourceRef":"destination-resource","schemaVersion":2,"serviceId":"service-app","status":"provisioning","updatedAt":1700000000020,"versioning":false}',
162
- sha256: '6e5c954466b600f81d1f19dcdd7ef426be99dc12ae33103e4d0c238e0070de71',
157
+ canonicalJson: '{"accessMode":"readWrite","capabilities":{"backup":true,"durability":"persistent","encryptedInTransit":true,"hardQuota":true,"performanceTier":"capacity","snapshots":"none","topology":"singleNode"},"capacity":{"granted":"500GiB","limit":"1TiB","requested":"500GiB"},"connection":{"bucket":"destination-bucket","endpoint":"https://destination.example.test","region":"local"},"createdAt":1700000000000,"credentialManagementScope":"platform:object-storage-binding","delivery":{"keys":{"accessKeyId":"APP_S3_ACCESS_KEY","bucket":"APP_S3_BUCKET","endpoint":"APP_S3_ENDPOINT","region":"APP_S3_REGION","secretAccessKey":"APP_S3_SECRET_KEY"},"type":"launcher-environment"},"generation":4,"id":"binding-object","kind":"objectStorage","observedGeneration":3,"policy":{"classId":"capacity-object","revision":"4"},"requestDigest":"3f864862041002e25b790b3bc7afbe37c399a006f385e1def9f679a93d4fa173","requestId":"object-data","resourceRef":"destination-resource","serviceId":"service-app","status":"provisioning","updatedAt":1700000000020,"versioning":false}',
158
+ sha256: '30ccf8221dea7cf7d26b062edf64b6cba29ef809cc1a213fd6428ee95ff92179',
163
159
  },
164
160
  });