reploid 0.0.0-stage → 0.1.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 (233) hide show
  1. package/apps/poolday/config/chat-files.json +27 -0
  2. package/apps/poolday/config/partition-generation.js +21 -0
  3. package/apps/poolday/config/partition-policy.json +74 -0
  4. package/apps/poolday/config/partition-releases.json +3 -0
  5. package/apps/poolday/config/work-profile.json +122 -0
  6. package/apps/poolday/host/chat-view.js +47 -0
  7. package/apps/poolday/host/earning/client.js +199 -0
  8. package/apps/poolday/host/earning/contribution.js +168 -0
  9. package/apps/poolday/host/partition-release.js +33 -0
  10. package/apps/poolday/host/work-partition-artifact-source.js +37 -0
  11. package/apps/poolday/host/work-partition-capsule.js +39 -0
  12. package/apps/poolday/host/work-partition-plan.js +167 -0
  13. package/apps/poolday/host/work-partition-tokenizer-guard.js +21 -0
  14. package/apps/poolday/infrastructure/model-storage-lock.js +22 -0
  15. package/apps/poolday/infrastructure/pack-release-storage.js +52 -0
  16. package/apps/poolday/pool/pack-operation.js +161 -0
  17. package/apps/poolday/pool/pack-release-policy.js +44 -0
  18. package/apps/poolday/pool/peer-pack-data-channel.js +217 -0
  19. package/apps/poolday/providers/work-adapter.js +67 -0
  20. package/apps/poolday/providers/work-device.js +33 -0
  21. package/apps/poolday/providers/work-generation-completion.js +55 -0
  22. package/apps/poolday/providers/work-provider.js +209 -0
  23. package/apps/poolday/providers/work-resident-provider.js +113 -0
  24. package/deploy/artifacts/doppler-gpu-0.7.1.tgz +0 -0
  25. package/docs/fleet-service.md +214 -0
  26. package/docs/native-participant.md +542 -0
  27. package/native-delivery.json +1174 -0
  28. package/package.json +29 -4
  29. package/packages/reploid/src/artifacts/binary.js +25 -0
  30. package/packages/reploid/src/artifacts/canonical-json.js +23 -0
  31. package/packages/reploid/src/artifacts/custody/exchange.js +402 -0
  32. package/packages/reploid/src/artifacts/custody/piece-acquisition.js +70 -0
  33. package/packages/reploid/src/artifacts/custody/runtime.js +404 -0
  34. package/packages/reploid/src/artifacts/custody/supply-budget.js +19 -0
  35. package/packages/reploid/src/artifacts/custody-certificate.js +123 -0
  36. package/packages/reploid/src/artifacts/delivery-attestation.js +64 -0
  37. package/packages/reploid/src/artifacts/generation-terminal.js +36 -0
  38. package/packages/reploid/src/artifacts/identity.js +91 -0
  39. package/packages/reploid/src/artifacts/participant-proofs.js +159 -0
  40. package/packages/reploid/src/artifacts/receipt.js +131 -0
  41. package/packages/reploid/src/artifacts/signatures.js +94 -0
  42. package/packages/reploid/src/artifacts/whole-model-receipt.js +42 -0
  43. package/packages/reploid/src/cancellation.js +44 -0
  44. package/packages/reploid/src/config/defaults.json +110 -0
  45. package/packages/reploid/src/config/index.js +209 -0
  46. package/packages/reploid/src/config/request-overrides.json +9 -0
  47. package/packages/reploid/src/config/transport-defaults.json +7 -0
  48. package/packages/reploid/src/hashing.js +30 -0
  49. package/packages/reploid/src/measurement/capacity-utilization.js +321 -0
  50. package/packages/reploid/src/measurement/index.js +5 -0
  51. package/packages/reploid/src/measurement/readiness-records.js +310 -0
  52. package/packages/reploid/src/mesh/computer-identity.js +50 -0
  53. package/packages/reploid/src/mesh/computer-identity.policy.json +8 -0
  54. package/packages/reploid/src/mesh/contribution.js +141 -0
  55. package/packages/reploid/src/mesh/generation-recovery.js +58 -0
  56. package/packages/reploid/src/mesh/gpu-signature-shader.js +27 -0
  57. package/packages/reploid/src/mesh/gpu-signature.js +136 -0
  58. package/packages/reploid/src/mesh/gpu-signature.policy.json +11 -0
  59. package/packages/reploid/src/mesh/index.js +66 -0
  60. package/packages/reploid/src/mesh/partitions/automatic-ordered-partitions.js +540 -0
  61. package/packages/reploid/src/mesh/partitions/automatic-partitions.js +298 -0
  62. package/packages/reploid/src/mesh/partitions/partition-attempt-evidence.js +282 -0
  63. package/packages/reploid/src/mesh/partitions/partition-attempt-queue-policy.json +6 -0
  64. package/packages/reploid/src/mesh/partitions/partition-attempt-queue.js +112 -0
  65. package/packages/reploid/src/mesh/partitions/partition-canonical-contract.js +167 -0
  66. package/packages/reploid/src/mesh/partitions/partition-canonical-execution.js +168 -0
  67. package/packages/reploid/src/mesh/partitions/partition-chat-canonical.js +49 -0
  68. package/packages/reploid/src/mesh/partitions/partition-chat.js +125 -0
  69. package/packages/reploid/src/mesh/partitions/partition-contract.js +192 -0
  70. package/packages/reploid/src/mesh/partitions/partition-device-admission.js +96 -0
  71. package/packages/reploid/src/mesh/partitions/partition-discovery.js +96 -0
  72. package/packages/reploid/src/mesh/partitions/partition-entry-publication.js +183 -0
  73. package/packages/reploid/src/mesh/partitions/partition-entry.js +434 -0
  74. package/packages/reploid/src/mesh/partitions/partition-generation-request.js +33 -0
  75. package/packages/reploid/src/mesh/partitions/partition-grants.js +277 -0
  76. package/packages/reploid/src/mesh/partitions/partition-link.js +103 -0
  77. package/packages/reploid/src/mesh/partitions/partition-network.js +109 -0
  78. package/packages/reploid/src/mesh/partitions/partition-ordered-chat.js +290 -0
  79. package/packages/reploid/src/mesh/partitions/partition-ordered-peer.js +415 -0
  80. package/packages/reploid/src/mesh/partitions/partition-ordered-publication.js +100 -0
  81. package/packages/reploid/src/mesh/partitions/partition-peer.js +314 -0
  82. package/packages/reploid/src/mesh/partitions/partition-placement.js +143 -0
  83. package/packages/reploid/src/mesh/partitions/partition-preparation-progress.js +25 -0
  84. package/packages/reploid/src/mesh/partitions/partition-receiver-policy.json +7 -0
  85. package/packages/reploid/src/mesh/partitions/partition-reservations.js +75 -0
  86. package/packages/reploid/src/mesh/partitions/partition-route-envelope-policy.json +16 -0
  87. package/packages/reploid/src/mesh/partitions/partition-route-envelope.js +74 -0
  88. package/packages/reploid/src/mesh/partitions/partition-route-execution.js +409 -0
  89. package/packages/reploid/src/mesh/partitions/partition-route-network.js +179 -0
  90. package/packages/reploid/src/mesh/partitions/partition-route-ranking.js +268 -0
  91. package/packages/reploid/src/mesh/partitions/partition-runner.js +319 -0
  92. package/packages/reploid/src/mesh/partitions/partition-stage-assignment.js +109 -0
  93. package/packages/reploid/src/mesh/partitions/partition-stage-commit.js +103 -0
  94. package/packages/reploid/src/mesh/partitions/partition-step-receiver.js +291 -0
  95. package/packages/reploid/src/mesh/partitions/partition-summary.js +122 -0
  96. package/packages/reploid/src/mesh/partitions/preparation-owner.js +40 -0
  97. package/packages/reploid/src/mesh/partitions/resident-error-codes.json +10 -0
  98. package/packages/reploid/src/mesh/partitions/resident-partition.js +333 -0
  99. package/packages/reploid/src/mesh/partitions/resident-route.js +46 -0
  100. package/packages/reploid/src/mesh/partitions/signed-resident-factory.js +59 -0
  101. package/packages/reploid/src/mesh/peer-generation.js +896 -0
  102. package/packages/reploid/src/mesh/peer-identity.js +87 -0
  103. package/packages/reploid/src/mesh/placement-beliefs.js +107 -0
  104. package/packages/reploid/src/mesh/preparation-progress.js +46 -0
  105. package/packages/reploid/src/mesh/provider-hardware.js +11 -0
  106. package/packages/reploid/src/mesh/remote-generation-requests.js +53 -0
  107. package/packages/reploid/src/mesh/swarm-coordination.js +140 -0
  108. package/packages/reploid/src/mesh/web-access-network.js +112 -0
  109. package/packages/reploid/src/mesh/web-access-policy.json +17 -0
  110. package/packages/reploid/src/retrieval/citations.js +156 -0
  111. package/packages/reploid/src/retrieval/claim-support.js +113 -0
  112. package/packages/reploid/src/retrieval/costs.js +101 -0
  113. package/packages/reploid/src/retrieval/fixed-provider.js +34 -0
  114. package/packages/reploid/src/retrieval/index.js +20 -0
  115. package/packages/reploid/src/retrieval/lexical-support.js +27 -0
  116. package/packages/reploid/src/retrieval/model-reviewer.js +67 -0
  117. package/packages/reploid/src/retrieval/page-fetch.json +10 -0
  118. package/packages/reploid/src/retrieval/page-text.js +182 -0
  119. package/packages/reploid/src/retrieval/passage.js +37 -0
  120. package/packages/reploid/src/retrieval/policy.json +14 -0
  121. package/packages/reploid/src/retrieval/retrieval.js +23 -0
  122. package/packages/reploid/src/retrieval/review-queue.js +76 -0
  123. package/packages/reploid/src/retrieval/sources.js +263 -0
  124. package/packages/reploid/src/retrieval/support-review.js +155 -0
  125. package/packages/reploid/src/retrieval/text.js +21 -0
  126. package/packages/reploid/src/rules/contribution.rules.json +8 -0
  127. package/packages/reploid/src/rules/routing.rules.json +24 -0
  128. package/packages/reploid/src/transport/assignment.js +571 -0
  129. package/packages/reploid/src/transport/bounded-channel-writer.js +46 -0
  130. package/packages/reploid/src/transport/index.js +11 -0
  131. package/packages/reploid/src/transport/introduction-admission.js +24 -0
  132. package/packages/reploid/src/transport/introduction-bootstrap.js +112 -0
  133. package/packages/reploid/src/transport/introduction-policy.json +12 -0
  134. package/packages/reploid/src/transport/introduction-records.js +90 -0
  135. package/packages/reploid/src/transport/introductions.js +209 -0
  136. package/packages/reploid/src/transport/partition-data-channel.js +421 -0
  137. package/packages/reploid/src/transport/partition-errors.json +16 -0
  138. package/packages/reploid/src/transport/retry-policy.js +36 -0
  139. package/packages/reploid/src/transport/room.js +502 -0
  140. package/packages/reploid/src/transport/sdp-fingerprint.js +5 -0
  141. package/packages/reploid/src/transport/signaling.js +571 -0
  142. package/packages/reploid/src/transport/swarm.js +1247 -0
  143. package/packages/reploid/src/transport/tool-offer-channel.js +137 -0
  144. package/server/browser-roots.js +42 -0
  145. package/server/browser-web-worker.js +94 -0
  146. package/server/chat-completions.js +45 -0
  147. package/server/fleet/browser-bridge.js +145 -0
  148. package/server/fleet/contract.js +42 -0
  149. package/server/fleet/credit-evidence.js +103 -0
  150. package/server/fleet/credit-recovery.js +175 -0
  151. package/server/fleet/credits.js +178 -0
  152. package/server/fleet/execution-admission.js +32 -0
  153. package/server/fleet/native-host.js +817 -0
  154. package/server/fleet/placement.js +132 -0
  155. package/server/fleet/requester.js +346 -0
  156. package/server/fleet/service.js +311 -0
  157. package/server/fleet/terminal-policy.js +83 -0
  158. package/server/fleet/text-api.js +94 -0
  159. package/server/hashing.js +35 -0
  160. package/server/http-request-owner.js +49 -0
  161. package/server/messages.js +57 -0
  162. package/server/native-canonical-epochs.js +90 -0
  163. package/server/native-change-log.js +40 -0
  164. package/server/native-earning.js +203 -0
  165. package/server/native-generation-journal.js +46 -0
  166. package/server/native-model-config.js +57 -0
  167. package/server/native-model-files.js +382 -0
  168. package/server/native-model-origin.js +54 -0
  169. package/server/native-model-origins.json +12 -0
  170. package/server/native-node.js +85 -0
  171. package/server/native-owner/controls.js +162 -0
  172. package/server/native-owner/monitor.js +251 -0
  173. package/server/native-owner/policy.js +273 -0
  174. package/server/native-owner/rate.js +92 -0
  175. package/server/native-participant.js +866 -0
  176. package/server/native-readiness-log.js +30 -0
  177. package/server/native-release-storage.js +64 -0
  178. package/server/native-shader-cache.js +369 -0
  179. package/server/native-supply-budget.js +66 -0
  180. package/server/native-whole-earning.js +67 -0
  181. package/server/native-whole-model-fill.js +182 -0
  182. package/server/native-whole-model.js +130 -0
  183. package/server/public-web-worker.js +66 -0
  184. package/server/responses.js +92 -0
  185. package/server/search/errors.js +47 -0
  186. package/server/search/page-fetch.js +185 -0
  187. package/server/text-api.js +83 -0
  188. package/server/text-recovery.js +45 -0
  189. package/server/text-request-options.js +23 -0
  190. package/server/text-requester.js +231 -0
  191. package/server/token-auth.js +9 -0
  192. package/server/whole-model-requester.js +99 -0
  193. package/shared/config/chat-models.json +4685 -0
  194. package/shared/config/doppler-execution-contracts.js +32 -0
  195. package/shared/config/doppler-execution-contracts.json +43 -0
  196. package/shared/config/doppler-generation-contract.js +223 -0
  197. package/shared/config/doppler-local-models.js +79 -0
  198. package/shared/config/doppler-package.json +7 -0
  199. package/shared/config/model-manifests/a0dc69e917133a82780823d17fc771c0f6b882d7c86c571deeaef45be2ff108b.json +6434 -0
  200. package/shared/config/model-manifests/e985e3cdad60d0cb57d84b436161e66ab5b3f6589a168de6f00b4610e5f1fb59.json +16534 -0
  201. package/shared/config/model-manifests/fa6eb7bbc3ba63571f8cede780b877d9272bde948697af93dd3d33837cb46137.json +7175 -0
  202. package/shared/config/model-pieces/qwen-0-8b-pieces.json +1506 -0
  203. package/shared/config/model-pieces/qwen-2b-pieces.json +4800 -0
  204. package/shared/config/model-pieces/qwen35b-q4_0-pieces.json +28880 -0
  205. package/shared/config/reploid-library.json +49 -0
  206. package/shared/config/swarm-bootstrap.json +21 -0
  207. package/shared/infrastructure/doppler-runtime-service.js +261 -0
  208. package/shared/pool/executable-pack.js +87 -0
  209. package/shared/pool/pack-operation-adapters.js +124 -0
  210. package/shared/pool/pack-operation-policy.js +75 -0
  211. package/shared/pool/pool-config.json +2280 -0
  212. package/shared/vendor/reploid/adapters/doppler-session.js +372 -0
  213. package/shared/vendor/reploid/artifacts/binary.js +25 -0
  214. package/shared/vendor/reploid/artifacts/canonical-json.js +23 -0
  215. package/shared/vendor/reploid/artifacts/delivery-attestation.js +64 -0
  216. package/shared/vendor/reploid/artifacts/identity.js +91 -0
  217. package/shared/vendor/reploid/artifacts/manifest-identity.js +17 -0
  218. package/shared/vendor/reploid/artifacts/participant-proofs.js +159 -0
  219. package/shared/vendor/reploid/artifacts/signatures.js +94 -0
  220. package/shared/vendor/reploid/config/defaults.json +110 -0
  221. package/shared/vendor/reploid/config/index.js +209 -0
  222. package/shared/vendor/reploid/config/request-overrides.json +9 -0
  223. package/shared/vendor/reploid/hashing.js +30 -0
  224. package/shared/vendor/reploid/mesh/generation-context-cache.js +97 -0
  225. package/shared/vendor/reploid/mesh/partitions/partition-attempt-evidence.js +282 -0
  226. package/shared/vendor/reploid/mesh/partitions/partition-contract.js +192 -0
  227. package/shared/vendor/reploid/mesh/partitions/partition-receiver-policy.json +7 -0
  228. package/shared/vendor/reploid/mesh/partitions/partition-route-ranking.js +268 -0
  229. package/shared/vendor/reploid/mesh/preparation-progress.js +46 -0
  230. package/shared/vendor/reploid/mesh/provider-hardware.js +11 -0
  231. package/shared/vendor/reploid/rules/routing.rules.json +24 -0
  232. package/shared/vendor/reploid/transport/partition-errors.json +16 -0
  233. package/README.md +0 -3
@@ -0,0 +1,97 @@
1
+ /** Application-owned, bounded retention of opaque runtime contexts. No tensor math. */
2
+ export function createGenerationContextCache({ policy, create, now = Date.now }) {
3
+ const { maxEntries, maxBytes, maxEntryBytes, idleMs } = policy;
4
+ if (![maxEntries, maxBytes, maxEntryBytes, idleMs].every(n => Number.isSafeInteger(n) && n > 0)
5
+ || maxEntryBytes > maxBytes) throw new Error('Invalid generation context retention policy');
6
+ const entries = new Map();
7
+ let closed = false;
8
+ let cleanupError = null;
9
+ let hits = 0, misses = 0, evictions = 0;
10
+ const remove = (key, { evicted = false } = {}) => {
11
+ const entry = entries.get(key);
12
+ if (!entry || entry.active) return false;
13
+ entry.context.close();
14
+ clearTimeout(entry.timer);
15
+ entries.delete(key);
16
+ if (evicted) evictions++;
17
+ return true;
18
+ };
19
+ const sweep = () => {
20
+ for (const [key, entry] of entries) if (!entry.active && now() - entry.used >= idleMs) remove(key, { evicted: true });
21
+ };
22
+ const chargedBytes = entry => {
23
+ const bytes = entry.context.getStats?.().allocatedBytes;
24
+ return entry.active || !Number.isSafeInteger(bytes) || bytes < 0 ? maxEntryBytes : bytes;
25
+ };
26
+ return Object.freeze({
27
+ acquire(key) {
28
+ if (closed) throw new Error('Generation context cache is closed');
29
+ if (cleanupError) throw new Error('Generation context expiry failed; close and replace the provider', { cause: cleanupError });
30
+ if (!key) return null;
31
+ sweep();
32
+ let entry = entries.get(key);
33
+ if (entry?.active) throw new Error('Generation context already leased');
34
+ const otherBytes = () => [...entries].reduce((sum, [id, item]) => sum + (id === key ? 0 : chargedBytes(item)), 0);
35
+ while ((!entry && entries.size >= maxEntries) || otherBytes() + maxEntryBytes > maxBytes) {
36
+ const victim = [...entries].find(([id, candidate]) => id !== key && !candidate.active);
37
+ if (!victim) return null;
38
+ remove(victim[0], { evicted: true });
39
+ }
40
+ if (entry) {
41
+ hits++;
42
+ } else {
43
+ misses++;
44
+ // Reserve the full per-entry allowance before the runtime allocates it.
45
+ // Active requests cannot be evicted to make room for another request.
46
+ entry = { context: create({ maxBytes: maxEntryBytes }), used: now(), active: false, timer: null };
47
+ entries.set(key, entry);
48
+ }
49
+ clearTimeout(entry.timer);
50
+ entry.active = true;
51
+ let released = false;
52
+ return Object.freeze({
53
+ context: entry.context,
54
+ release({ discard = false } = {}) {
55
+ if (released) return;
56
+ released = true;
57
+ entry.active = false;
58
+ entry.used = now();
59
+ if (discard) { remove(key); return; }
60
+ entries.delete(key);
61
+ entries.set(key, entry);
62
+ entry.timer = setTimeout(() => {
63
+ try { remove(key, { evicted: true }); } catch (error) { cleanupError = error; }
64
+ }, idleMs);
65
+ entry.timer.unref?.();
66
+ },
67
+ });
68
+ },
69
+ getStats() {
70
+ let activeEntries = 0, allocatedBytes = 0, cachedTokens = 0;
71
+ for (const entry of entries.values()) {
72
+ if (entry.active) activeEntries++;
73
+ const stats = entry.context.getStats?.();
74
+ if (Number.isSafeInteger(stats?.allocatedBytes) && stats.allocatedBytes > 0) allocatedBytes += stats.allocatedBytes;
75
+ if (Number.isSafeInteger(stats?.cachedTokens) && stats.cachedTokens > 0) cachedTokens += stats.cachedTokens;
76
+ }
77
+ return Object.freeze({
78
+ entries: entries.size,
79
+ activeEntries,
80
+ allocatedBytes,
81
+ cachedTokens,
82
+ maxEntries,
83
+ maxBytes,
84
+ maxEntryBytes,
85
+ hits,
86
+ misses,
87
+ evictions,
88
+ });
89
+ },
90
+ close() {
91
+ if ([...entries.values()].some(entry => entry.active)) throw new Error('Context execution must settle before cache close');
92
+ for (const key of entries.keys()) remove(key);
93
+ cleanupError = null;
94
+ closed = true;
95
+ },
96
+ });
97
+ }
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Signed execution evidence for one charged ordered-route attempt.
3
+ *
4
+ * The requester binds the attempt to its reservation with a signed
5
+ * acknowledgment of zero outputs (the charge binding) before dispatch. Every
6
+ * stage that admits the attempt signs cumulative stage receipts with its
7
+ * persistent participant identity: reservation and route binding, steps
8
+ * executed, tokens processed, timing, and its closing status. The final stage
9
+ * also commits to its outputs as a hash chain over every step's delta. The
10
+ * requester acknowledges each output it received by signing the cumulative
11
+ * chain; it acknowledges only a receipt whose chain matches what it received.
12
+ * Under the receiver policy's bound K, no stage computes step s unless it holds
13
+ * the requester's verified acknowledgment of at least s - K outputs, so a
14
+ * requester that stops acknowledging leaves at most K + 1 outputs of work
15
+ * unacknowledged, whatever the coordinator does.
16
+ * Signatures establish who asserted what; they do not prove honest or correct
17
+ * computation, and verification against registered devices is the service's.
18
+ * WebCrypto only, so browser and native hosts sign with the code the service
19
+ * verifies with.
20
+ */
21
+ import { canonicalPartitionJson, canonicalRouteAttemptBinding } from './partition-contract.js';
22
+ import { sha256Hex as digestText } from '../../artifacts/signatures.js';
23
+ import { normalizeParticipantPublicKey, participantIdFor, signAsParticipant, verifyParticipantSignature } from '../../artifacts/participant-proofs.js';
24
+ import receiverPolicy from './partition-receiver-policy.json' with { type: 'json' };
25
+
26
+ export const STAGE_RECEIPT_SCHEMA = 'reploid.stage-receipt/v1';
27
+ export const OUTPUT_ACKNOWLEDGMENT_SCHEMA = 'reploid.output-acknowledgment/v1';
28
+ export const OUTPUT_CHAIN_SCHEMA = 'reploid.output-chain/v1';
29
+ export const ATTEMPT_EVIDENCE_SCHEMA = 'reploid.attempt-evidence/v1';
30
+ export const STAGE_RECEIPT_STATUSES = Object.freeze(['executing', 'settled', 'failed']);
31
+ export const ACKNOWLEDGMENT_OVERDUE = 'ACKNOWLEDGMENT_OVERDUE';
32
+
33
+ /** K: the most outputs a charged attempt may run ahead of the requester's verified acknowledgments. */
34
+ export const MAX_UNACKNOWLEDGED_OUTPUTS = receiverPolicy.maxUnacknowledgedOutputs;
35
+ if (!Number.isSafeInteger(MAX_UNACKNOWLEDGED_OUTPUTS) || MAX_UNACKNOWLEDGED_OUTPUTS < 0) {
36
+ throw new Error('partition-receiver-policy.json must declare maxUnacknowledgedOutputs as a non-negative integer');
37
+ }
38
+ /** A step refused or abandoned because the requester's acknowledgments fell more than K outputs behind. */
39
+ export const acknowledgmentOverdue = (message = 'The requester\'s output acknowledgments are overdue') => Object.assign(new Error(message), { code: ACKNOWLEDGMENT_OVERDUE });
40
+
41
+ const RECEIPT_KEYS = Object.freeze(['schema', 'reservationId', 'attemptId', 'routeHash', 'partitionIndex', 'participantId',
42
+ 'sequence', 'status', 'steps', 'tokens', 'timing', 'output', 'signedAt']);
43
+ const ACKNOWLEDGMENT_KEYS = Object.freeze(['schema', 'reservationId', 'attemptId', 'routeHash', 'requesterId', 'count', 'chain', 'signedAt']);
44
+ const TIMING_KEYS = Object.freeze(['reservedAt', 'firstStepAt', 'lastStepAt', 'closedAt']);
45
+ const OUTPUT_KEYS = Object.freeze(['count', 'chain', 'done', 'stopReason']);
46
+ const BUNDLE_KEYS = Object.freeze(['schema', 'reservationId', 'attemptId', 'identity', 'receipts', 'acknowledged']);
47
+ const DIGEST = /^sha256:[a-f0-9]{64}$/;
48
+
49
+ const assert = (ok, message) => { if (!ok) throw new Error(message); };
50
+ const exact = (value, keys) => Boolean(value) && typeof value === 'object' && !Array.isArray(value)
51
+ && Object.keys(value).length === keys.length && keys.every(key => Object.hasOwn(value, key));
52
+ const text = (value, max) => typeof value === 'string' && value.length > 0 && value.length <= max;
53
+ const count = value => Number.isSafeInteger(value) && value >= 0;
54
+ const isoOrNull = value => value === null || typeof value === 'string' && value.length <= 40 && Number.isFinite(Date.parse(value));
55
+ const iso = ms => new Date(ms).toISOString();
56
+ const sameJson = (a, b) => canonicalPartitionJson(a) === canonicalPartitionJson(b);
57
+
58
+ /** The ledger's route hash: sha256 over the canonical route attempt binding. */
59
+ export const attemptRouteHash = identity => digestText(canonicalPartitionJson(canonicalRouteAttemptBinding(identity)));
60
+ /** Chain value before any output, bound to the reservation and route. */
61
+ export const outputChainGenesis = ({ reservationId, routeHash }) => digestText(canonicalPartitionJson({ schema: OUTPUT_CHAIN_SCHEMA, reservationId, routeHash }));
62
+ /** One step's output appended to the chain; `delta` is that step's text, empty when it published none. */
63
+ export const extendOutputChain = (previous, { step, delta }) => digestText(canonicalPartitionJson({ previous, step, delta }));
64
+
65
+ /** Signing port over a host's persistent participant identity and its clock; the host keeps the identity. */
66
+ export function createAttemptEvidenceSigner({ identity, now = Date.now }) {
67
+ const publicJwk = normalizeParticipantPublicKey(identity?.publicJwk);
68
+ if (!publicJwk || !identity.privateJwk || typeof identity.peerId !== 'string') throw new TypeError('A persistent participant signing identity is required');
69
+ const bundle = structuredClone(identity);
70
+ let checked = null;
71
+ if (typeof now !== 'function') throw new TypeError('The signing host clock must be a function');
72
+ return Object.freeze({
73
+ participantId: bundle.peerId,
74
+ now,
75
+ async sign(domain, body) {
76
+ checked ??= participantIdFor(publicJwk).then(id => assert(id === bundle.peerId, 'Signing identity does not derive its participant id'));
77
+ await checked;
78
+ return { ...structuredClone(body), publicJwk: structuredClone(publicJwk), signature: await signAsParticipant(bundle, domain, body) };
79
+ }
80
+ });
81
+ }
82
+
83
+ function receiptProblem(body) {
84
+ if (!exact(body, RECEIPT_KEYS) || body.schema !== STAGE_RECEIPT_SCHEMA || !text(body.reservationId, 160) || !text(body.attemptId, 256)
85
+ || !DIGEST.test(body.routeHash) || !count(body.partitionIndex) || !text(body.participantId, 256)
86
+ || !Number.isSafeInteger(body.sequence) || body.sequence < 1 || !STAGE_RECEIPT_STATUSES.includes(body.status) || !count(body.steps)
87
+ || !exact(body.tokens, ['prompt', 'processed']) || !(body.tokens.prompt === null || count(body.tokens.prompt)) || !count(body.tokens.processed)
88
+ || !exact(body.timing, TIMING_KEYS) || !TIMING_KEYS.every(key => isoOrNull(body.timing[key]))
89
+ || !isoOrNull(body.signedAt) || body.signedAt === null) return 'receipt_malformed';
90
+ if (body.output !== null && !(exact(body.output, OUTPUT_KEYS) && count(body.output.count) && body.output.count === body.steps
91
+ && DIGEST.test(body.output.chain) && typeof body.output.done === 'boolean'
92
+ && (body.output.stopReason === null || text(body.output.stopReason, 64)))) return 'receipt_malformed';
93
+ return null;
94
+ }
95
+
96
+ function acknowledgmentProblem(body) {
97
+ return exact(body, ACKNOWLEDGMENT_KEYS) && body.schema === OUTPUT_ACKNOWLEDGMENT_SCHEMA && text(body.reservationId, 160)
98
+ && text(body.attemptId, 256) && DIGEST.test(body.routeHash) && text(body.requesterId, 256) && count(body.count)
99
+ && DIGEST.test(body.chain) && isoOrNull(body.signedAt) && body.signedAt !== null ? null : 'acknowledgment_malformed';
100
+ }
101
+
102
+ async function verifySigned(record, { domain, problemOf, signerOf, publicJwk }) {
103
+ if (!record || typeof record !== 'object' || Array.isArray(record)) return { ok: false, problem: problemOf(null) };
104
+ const { publicJwk: carried, signature, ...body } = record;
105
+ const problem = problemOf(body);
106
+ if (problem) return { ok: false, problem };
107
+ const key = normalizeParticipantPublicKey(carried);
108
+ if (!key || !exact(carried, Object.keys(key)) || typeof signature !== 'string') return { ok: false, problem: problemOf(null) };
109
+ if (publicJwk && !sameJson(normalizeParticipantPublicKey(publicJwk), key)) return { ok: false, problem: 'signer_key_mismatch' };
110
+ if (await participantIdFor(key) !== signerOf(body)) return { ok: false, problem: 'signer_key_mismatch' };
111
+ if (!await verifyParticipantSignature(key, domain, body, signature)) return { ok: false, problem: 'signature_invalid' };
112
+ return { ok: true, body };
113
+ }
114
+
115
+ /** Form, key-to-participant binding and signature of a stage receipt; `publicJwk`, when given, must be the signing key. */
116
+ export const verifyStageReceipt = (receipt, { publicJwk = null } = {}) => verifySigned(receipt, {
117
+ domain: STAGE_RECEIPT_SCHEMA, problemOf: body => (body ? receiptProblem(body) : 'receipt_malformed'), signerOf: body => body.participantId, publicJwk });
118
+
119
+ /** Form, key-to-requester binding and signature of an output acknowledgment. */
120
+ export const verifyOutputAcknowledgment = (acknowledgment, { publicJwk = null } = {}) => verifySigned(acknowledgment, {
121
+ domain: OUTPUT_ACKNOWLEDGMENT_SCHEMA, problemOf: body => (body ? acknowledgmentProblem(body) : 'acknowledgment_malformed'),
122
+ signerOf: body => body.requesterId, publicJwk });
123
+
124
+ /**
125
+ * Form of a charge binding carried with an ordered input: the requester's
126
+ * acknowledgment of zero outputs of this attempt. Stages verify the signature.
127
+ */
128
+ export function isChargeBindingShape(charge, { attemptId, requesterId }) {
129
+ if (!exact(charge, [...ACKNOWLEDGMENT_KEYS, 'publicJwk', 'signature']) || typeof charge.signature !== 'string' || charge.signature.length > 256) return false;
130
+ const { publicJwk, signature: _signature, ...body } = charge;
131
+ return acknowledgmentProblem(body) === null && body.count === 0 && body.attemptId === attemptId && body.requesterId === requesterId
132
+ && normalizeParticipantPublicKey(publicJwk) !== null;
133
+ }
134
+
135
+ /** Check the bundle's form; signatures and registrations are the verifier's. */
136
+ export function checkAttemptEvidenceBundle(bundle) {
137
+ if (!exact(bundle, BUNDLE_KEYS) || bundle.schema !== ATTEMPT_EVIDENCE_SCHEMA || !text(bundle.reservationId, 160)
138
+ || !text(bundle.attemptId, 256) || !Array.isArray(bundle.receipts)
139
+ || !(bundle.acknowledged === null || exact(bundle.acknowledged, ['acknowledgment', 'receipt']))) return 'evidence_malformed';
140
+ try { if (canonicalRouteAttemptBinding(bundle.identity).attemptId !== bundle.attemptId) return 'evidence_malformed'; }
141
+ catch { return 'evidence_malformed'; }
142
+ return null;
143
+ }
144
+
145
+ /** The charge binding a requester signs after binding its attempt and before any input leaves the device. */
146
+ async function checkChargeBinding(charge, binding, routeHash) {
147
+ const checked = await verifyOutputAcknowledgment(charge);
148
+ const body = checked.body;
149
+ assert(checked.ok && body.count === 0 && body.attemptId === binding.attemptId && body.routeHash === routeHash
150
+ && body.requesterId === binding.requesterId && body.chain === await outputChainGenesis({ reservationId: body.reservationId, routeHash }),
151
+ 'Attempt charge binding is not the requester\'s signed binding of this route');
152
+ return { reservationId: body.reservationId, requesterKey: normalizeParticipantPublicKey(charge.publicJwk) };
153
+ }
154
+
155
+ /**
156
+ * Stage side: cumulative signed receipts of one charged attempt at one stage.
157
+ * A repeated step never advances the tally twice; a closed tally signs nothing new.
158
+ */
159
+ export async function openStageReceipts({ signer, identity, partitionIndex, charge, now = signer?.now ?? Date.now }) {
160
+ const binding = canonicalRouteAttemptBinding(identity);
161
+ const stage = binding.route.stages[partitionIndex];
162
+ assert(stage && typeof signer?.sign === 'function' && signer.participantId === stage.participantId, 'Stage receipts need this stage\'s own signing identity');
163
+ const routeHash = await attemptRouteHash(binding);
164
+ const { reservationId, requesterKey } = await checkChargeBinding(charge, binding, routeHash);
165
+ const finalStage = binding.route.stages.at(-1);
166
+ const final = partitionIndex === finalStage.index;
167
+ const state = { sequence: 0, steps: 0, prompt: null, processed: 0, timing: { reservedAt: iso(now()), firstStepAt: null, lastStepAt: null, closedAt: null },
168
+ output: final ? { count: 0, chain: await outputChainGenesis({ reservationId, routeHash }), done: false, stopReason: null } : null,
169
+ receipt: null, acknowledged: null, closed: false };
170
+ let queue = Promise.resolve();
171
+ const serial = task => { const run = queue.then(task); queue = run.catch(() => {}); return run; };
172
+ const sign = async status => {
173
+ state.sequence += 1;
174
+ state.receipt = await signer.sign(STAGE_RECEIPT_SCHEMA, { schema: STAGE_RECEIPT_SCHEMA, reservationId, attemptId: binding.attemptId, routeHash,
175
+ partitionIndex, participantId: stage.participantId, sequence: state.sequence, status, steps: state.steps,
176
+ tokens: { prompt: state.prompt, processed: state.processed }, timing: structuredClone(state.timing),
177
+ output: state.output && structuredClone(state.output), signedAt: iso(now()) });
178
+ return structuredClone(state.receipt);
179
+ };
180
+ return Object.freeze({
181
+ reservationId, routeHash,
182
+ /** Record one executed step and sign the cumulative receipt. */
183
+ step: ({ step, inputTokenCount, delta = '', done = false, stopReason = null }) => serial(async () => {
184
+ if (state.closed || step !== state.steps || !count(inputTokenCount)) return state.receipt && structuredClone(state.receipt);
185
+ const at = iso(now());
186
+ if (step === 0) { state.prompt = inputTokenCount; state.timing.firstStepAt = at; }
187
+ state.steps += 1; state.processed += inputTokenCount; state.timing.lastStepAt = at;
188
+ if (state.output) {
189
+ assert(typeof delta === 'string', 'Final stage output delta required');
190
+ state.output = { count: state.steps, chain: await extendOutputChain(state.output.chain, { step, delta }), done: done === true,
191
+ stopReason: done === true && typeof stopReason === 'string' ? stopReason.slice(0, 64) : null };
192
+ }
193
+ return sign('executing');
194
+ }),
195
+ /** Keep the latest requester acknowledgment and the final-stage receipt it acknowledged, as forwarded by the coordinator. */
196
+ /**
197
+ * Keep a newer acknowledgment only when the requester's key from the charge
198
+ * binding signed it and the final stage signed the receipt it pairs with.
199
+ */
200
+ async acknowledge(pair) {
201
+ const ack = pair?.acknowledgment, receipt = pair?.receipt;
202
+ if (!ack || ack.count <= (state.acknowledged?.acknowledgment.count ?? 0)) return false;
203
+ const [signed, committed] = await Promise.all([verifyOutputAcknowledgment(ack, { publicJwk: requesterKey }), verifyStageReceipt(receipt)]);
204
+ if (!signed.ok || !committed.ok || ack.reservationId !== reservationId || ack.attemptId !== binding.attemptId || ack.routeHash !== routeHash
205
+ || ack.requesterId !== binding.requesterId || ack.count < 1 || committed.body.reservationId !== reservationId
206
+ || committed.body.attemptId !== binding.attemptId || committed.body.routeHash !== routeHash
207
+ || committed.body.partitionIndex !== finalStage.index || committed.body.participantId !== finalStage.participantId
208
+ || committed.body.output?.count !== ack.count || committed.body.output.chain !== ack.chain) return false;
209
+ if (ack.count > (state.acknowledged?.acknowledgment.count ?? 0)) state.acknowledged = structuredClone({ acknowledgment: ack, receipt });
210
+ return true;
211
+ },
212
+ /** Refuse step `step` unless the verified acknowledgments cover at least step - K outputs. */
213
+ async authorizeStep({ step, acknowledged = null }) {
214
+ if (acknowledged) await this.acknowledge(acknowledged);
215
+ if ((state.acknowledged?.acknowledgment.count ?? 0) < step - MAX_UNACKNOWLEDGED_OUTPUTS) {
216
+ throw acknowledgmentOverdue(`Step ${step} needs the requester's acknowledgment of ${step - MAX_UNACKNOWLEDGED_OUTPUTS} outputs`);
217
+ }
218
+ },
219
+ /** Sign the closing receipt once: settled, or failed when the stage recorded a failure. */
220
+ close: ({ failed = false } = {}) => serial(async () => {
221
+ if (state.closed) return state.receipt && structuredClone(state.receipt);
222
+ state.closed = true; state.timing.closedAt = iso(now());
223
+ return sign(failed ? 'failed' : 'settled');
224
+ }),
225
+ /** This stage's evidence for its own submission: its latest receipt and the latest acknowledgment it holds. */
226
+ evidence: () => ({ schema: ATTEMPT_EVIDENCE_SCHEMA, reservationId, attemptId: binding.attemptId, identity: structuredClone(binding),
227
+ receipts: state.receipt ? [structuredClone(state.receipt)] : [], acknowledged: state.acknowledged && structuredClone(state.acknowledged) })
228
+ });
229
+ }
230
+
231
+ /**
232
+ * Requester side: the charge binding, then one signed acknowledgment per
233
+ * received output whose final-stage receipt commits to exactly the chain of
234
+ * deltas this device received. A receipt that does not match is not
235
+ * acknowledged, and nothing after it can be.
236
+ */
237
+ export async function openOutputAcknowledgments({ signer, identity, reservationId, now = signer?.now ?? Date.now, restore = null }) {
238
+ const binding = canonicalRouteAttemptBinding(identity);
239
+ assert(typeof signer?.sign === 'function' && signer.participantId === binding.requesterId && text(reservationId, 160),
240
+ 'Acknowledgments need the requester\'s own signing identity and reservation');
241
+ const routeHash = await attemptRouteHash(binding);
242
+ const finalStage = binding.route.stages.at(-1);
243
+ const sign = (outputs, chain) => signer.sign(OUTPUT_ACKNOWLEDGMENT_SCHEMA, { schema: OUTPUT_ACKNOWLEDGMENT_SCHEMA, reservationId,
244
+ attemptId: binding.attemptId, routeHash, requesterId: binding.requesterId, count: outputs, chain, signedAt: iso(now()) });
245
+ const state = restore && restore.reservationId === reservationId && restore.attemptId === binding.attemptId ? structuredClone(restore)
246
+ : { reservationId, attemptId: binding.attemptId, count: 0, chain: await outputChainGenesis({ reservationId, routeHash }),
247
+ charge: null, acknowledged: null, latestReceipt: null, broken: false };
248
+ state.charge ??= await sign(0, state.chain);
249
+ let queue = Promise.resolve();
250
+ const serial = task => { const run = queue.then(task); queue = run.catch(() => {}); return run; };
251
+ return Object.freeze({
252
+ routeHash,
253
+ charge: structuredClone(state.charge),
254
+ /** Acknowledge one step's output: `{ step, delta, receipt }` from the route. Returns the pair, or null when not acknowledged. */
255
+ observe: ({ step, delta, receipt }) => serial(async () => {
256
+ if (!state.broken && step === state.count - 1 && state.lastObserved
257
+ && sameJson(state.lastObserved, { step, delta, receipt })) return structuredClone(state.acknowledged);
258
+ if (state.broken || step !== state.count || typeof delta !== 'string') { state.broken = true; return null; }
259
+ const checked = await verifyStageReceipt(receipt);
260
+ const body = checked.body;
261
+ const chain = await extendOutputChain(state.chain, { step, delta });
262
+ if (!checked.ok || body.reservationId !== reservationId || body.attemptId !== binding.attemptId || body.routeHash !== routeHash
263
+ || body.partitionIndex !== finalStage.index || body.participantId !== finalStage.participantId
264
+ || body.output?.count !== step + 1 || body.output.chain !== chain) { state.broken = true; return null; }
265
+ const acknowledgment = await sign(step + 1, chain);
266
+ Object.assign(state, { count: step + 1, chain, acknowledged: { acknowledgment, receipt: structuredClone(receipt) }, latestReceipt: structuredClone(receipt),
267
+ lastObserved: structuredClone({ step, delta, receipt }) });
268
+ return structuredClone(state.acknowledged);
269
+ }),
270
+ /** Durable state for a restarted host. */
271
+ state: () => structuredClone(state),
272
+ /** The requester's evidence for its report: the latest final-stage receipt and its acknowledgment. */
273
+ evidence: () => ({ schema: ATTEMPT_EVIDENCE_SCHEMA, reservationId, attemptId: binding.attemptId, identity: structuredClone(binding),
274
+ receipts: state.latestReceipt ? [structuredClone(state.latestReceipt)] : [], acknowledged: state.acknowledged && structuredClone(state.acknowledged) })
275
+ });
276
+ }
277
+
278
+ export default {
279
+ STAGE_RECEIPT_SCHEMA, OUTPUT_ACKNOWLEDGMENT_SCHEMA, OUTPUT_CHAIN_SCHEMA, ATTEMPT_EVIDENCE_SCHEMA,
280
+ attemptRouteHash, outputChainGenesis, extendOutputChain, createAttemptEvidenceSigner,
281
+ verifyStageReceipt, verifyOutputAcknowledgment, checkAttemptEvidenceBundle, openStageReceipts, openOutputAcknowledgments
282
+ };
@@ -0,0 +1,192 @@
1
+ import failureMessages from '../../transport/partition-errors.json' with { type: 'json' };
2
+ import { canonicalize } from '../../artifacts/canonical-json.js';
3
+ import { sha256Hex } from '../../artifacts/signatures.js';
4
+
5
+ /** Shared orchestration bindings. Tensor semantics remain supplied by Doppler. */
6
+ export const partitionIdentityKeys = Object.freeze(['modelId', 'modelIdentity', 'planId', 'threadId', 'attemptId', 'participantA', 'participantB']);
7
+ export const partitionActions = Object.freeze(['mesh.execute_partition_a', 'mesh.execute_partition_b',
8
+ 'mesh.transfer_intermediate_activation', 'mesh.transfer_token_context', 'mesh.transfer_partition_output']);
9
+ export const assertPartition = (value, message) => { if (!value) throw new Error(message); };
10
+ /** A declared failure whose code and message cross every channel hop together; an uncoded error
11
+ * becomes EXECUTION_FAILED at the next hop. `detail` adds non-message fields, such as a bound. */
12
+ export function partitionFailure(code, detail = {}) {
13
+ assertPartition(Object.hasOwn(failureMessages, code), 'Undeclared partition failure code');
14
+ return Object.assign(new Error(failureMessages[code]), detail, { code });
15
+ }
16
+ /** Throw CAPACITY_UNAVAILABLE unless `value` holds: an exhausted or closed admission slot, never a defect. */
17
+ export const assertCapacity = value => { if (!value) throw partitionFailure('CAPACITY_UNAVAILABLE'); };
18
+ export const samePartitionIdentity = (actual, expected) => actual && [...partitionIdentityKeys, 'requesterId', 'placementGeneration'].every(key => actual[key] === expected[key]);
19
+ /** Ask the injected provider for tensor layout; retain the accepted v1 adapter. */
20
+ export function partitionActivationContract(runtime, plan) {
21
+ if (runtime.getPartitionActivationContract) {
22
+ assertPartition(typeof runtime.validateActivationFrameForPlan === 'function', 'Doppler activation validator required');
23
+ return Object.freeze({ ...runtime.getPartitionActivationContract(plan) });
24
+ }
25
+ assertPartition(plan?.schema === runtime.LAYER_PARTITION_SCHEMA
26
+ && plan.activationPrecision === undefined && plan.activationComponents === undefined
27
+ && plan.activationElementStrideBytes === undefined, 'Unsupported Doppler partition activation contract');
28
+ return Object.freeze({ schema: runtime.ACTIVATION_TENSOR_SCHEMA, dtype: plan.activationDtype,
29
+ hiddenSize: plan.hiddenSize, precision: plan.activationDtype, components: 1,
30
+ elementStrideBytes: plan.activationDtype === 'f16' ? 2 : 4 });
31
+ }
32
+ export function matchingPartitionActivation(value, contract, frame = false) {
33
+ if (frame && contract.components === 1) {
34
+ return value?.dtype === contract.dtype && ['precision', 'components', 'elementStrideBytes']
35
+ .every(key => value[key] === undefined);
36
+ }
37
+ return value?.dtype === contract.dtype && ['precision', 'components', 'elementStrideBytes']
38
+ .every(key => value[key] === contract[key] || contract.components === 1 && value[key] === undefined);
39
+ }
40
+ export function validatePartitionIdentity(identity) {
41
+ assertPartition(identity && typeof identity === 'object' && !Array.isArray(identity) && partitionIdentityKeys.every(key => typeof identity[key] === 'string'
42
+ && identity[key].length > 0 && identity[key].length <= 256)
43
+ && /^sha256:[a-f0-9]{64}$/.test(identity.modelIdentity)
44
+ && identity.participantA !== identity.participantB, 'Invalid partition identity');
45
+ if (identity.requesterId !== undefined || identity.placementGeneration !== undefined) {
46
+ assertPartition(typeof identity.requesterId === 'string' && identity.requesterId.length > 0 && identity.requesterId.length <= 256
47
+ && Number.isSafeInteger(identity.placementGeneration) && identity.placementGeneration >= 0, 'Invalid requester placement binding');
48
+ }
49
+ }
50
+ /** Snapshot the complete validated identity; optional placement fields travel as a pair. */
51
+ export function canonicalPartitionBinding(identity) {
52
+ validatePartitionIdentity(identity);
53
+ const keys = identity.requesterId === undefined ? partitionIdentityKeys
54
+ : [...partitionIdentityKeys, 'requesterId', 'placementGeneration'];
55
+ return Object.freeze(Object.fromEntries(keys.map(key => [key, identity[key]])));
56
+ }
57
+ const assertFiniteJson = value => {
58
+ if (Array.isArray(value)) return value.forEach(item => assertFiniteJson(item));
59
+ if (value && typeof value === 'object') return Object.keys(value).forEach(key => assertFiniteJson(value[key]));
60
+ assertPartition(value === null || ['string', 'boolean'].includes(typeof value)
61
+ || typeof value === 'number' && Number.isFinite(value), 'Partition metadata must be finite JSON');
62
+ };
63
+ /** The library's canonical JSON (artifacts/canonical-json.js) of finite JSON metadata. */
64
+ export function canonicalPartitionJson(value) {
65
+ assertFiniteJson(value);
66
+ return canonicalize(value);
67
+ }
68
+ export async function partitionFingerprint(metadata, bytes = new Uint8Array()) {
69
+ const header = new TextEncoder().encode(canonicalPartitionJson(metadata));
70
+ const input = new Uint8Array(4 + header.length + bytes.length);
71
+ new DataView(input.buffer).setUint32(0, header.length);
72
+ input.set(header, 4); input.set(bytes, 4 + header.length);
73
+ return sha256Hex(input);
74
+ }
75
+
76
+ export const PARTITION_ROUTE_SCHEMA = 'reploid.partition-route/v1';
77
+ export const ROUTE_ATTEMPT_SCHEMA = 'reploid.partition-attempt/v2';
78
+ const identifier = value => typeof value === 'string' && value.length > 0 && value.length <= 256;
79
+ const exactFields = (value, keys) => value && typeof value === 'object' && !Array.isArray(value)
80
+ && Object.keys(value).length === keys.length && keys.every(key => Object.hasOwn(value, key));
81
+ const freezeRouteJson = value => {
82
+ if (value && typeof value === 'object') { Object.values(value).forEach(freezeRouteJson); Object.freeze(value); }
83
+ return value;
84
+ };
85
+ /** Structural checks also apply when comparing a received identity without a runtime. */
86
+ export function canonicalPartitionRoute(route) {
87
+ assertPartition(exactFields(route, ['schema', 'modelId', 'modelIdentity', 'planId', 'stages', 'activation'])
88
+ && route.schema === PARTITION_ROUTE_SCHEMA && identifier(route.modelId) && /^sha256:[a-f0-9]{64}$/.test(route.planId)
89
+ && /^sha256:[a-f0-9]{64}$/.test(route.modelIdentity)
90
+ && Array.isArray(route.stages) && route.stages.length >= 2, 'Invalid partition route');
91
+ const participants = new Set(), transports = new Set();
92
+ route.stages.forEach((stage, index) => {
93
+ assertPartition(exactFields(stage, ['index', 'participantId', 'transportId', 'residentId', 'executionId'])
94
+ && stage.index === index && ['participantId', 'transportId', 'residentId', 'executionId'].every(key => identifier(stage[key]))
95
+ && !participants.has(stage.participantId) && !transports.has(stage.transportId), 'Invalid or duplicate route stage');
96
+ participants.add(stage.participantId); transports.add(stage.transportId);
97
+ });
98
+ const layout = route.activation;
99
+ assertPartition(exactFields(layout, ['schema', 'dtype', 'hiddenSize', 'precision', 'components', 'elementStrideBytes'])
100
+ && layout.schema === (layout.precision === 'two-f32' ? 'doppler.activation-tensor/v2' : 'doppler.activation-tensor/v1')
101
+ && Number.isSafeInteger(layout.hiddenSize) && layout.hiddenSize > 0
102
+ && (layout.precision === 'two-f32' ? layout.dtype === 'f32' && layout.components === 2 && layout.elementStrideBytes === 8
103
+ : ['f32', 'f16'].includes(layout.dtype) && layout.precision === layout.dtype && layout.components === 1
104
+ && layout.elementStrideBytes === (layout.dtype === 'f16' ? 2 : 4)), 'Invalid route activation layout');
105
+ return freezeRouteJson(structuredClone(route));
106
+ }
107
+ /** Provider hashing validates its plan geometry; host admission separately binds the selected manifest. */
108
+ export async function validatePartitionRoute(runtime, plan, route) {
109
+ const snapshot = canonicalPartitionRoute(route), planSnapshot = structuredClone(plan);
110
+ assertPartition(typeof runtime?.hashLayerPartitionPlan === 'function'
111
+ && typeof runtime.getPartitionActivationContract === 'function', 'Ordered Doppler contracts required');
112
+ assertPartition(planSnapshot?.schema === 'doppler.layer-partition-contract/v3'
113
+ && planSnapshot.modelId === snapshot.modelId && planSnapshot.stageCount === snapshot.stages.length
114
+ && Array.isArray(planSnapshot.partitions) && planSnapshot.partitions.length === snapshot.stages.length
115
+ && planSnapshot.partitions.every((part, index) => part.index === index), 'Route plan stage mismatch');
116
+ const layout = partitionActivationContract(runtime, planSnapshot);
117
+ assertPartition(canonicalPartitionJson(layout) === canonicalPartitionJson(snapshot.activation), 'Route activation mismatch');
118
+ assertPartition(await runtime.hashLayerPartitionPlan(planSnapshot) === snapshot.planId, 'Route plan identity mismatch');
119
+ return snapshot;
120
+ }
121
+ export function canonicalRouteAttemptBinding(identity) {
122
+ assertPartition(exactFields(identity, ['schema', 'threadId', 'attemptId', 'requesterId', 'placementGeneration', 'route'])
123
+ && identity.schema === ROUTE_ATTEMPT_SCHEMA && ['threadId', 'attemptId', 'requesterId'].every(key => identifier(identity[key]))
124
+ && Number.isSafeInteger(identity.placementGeneration) && identity.placementGeneration >= 0, 'Invalid route attempt identity');
125
+ return Object.freeze({ schema: identity.schema, threadId: identity.threadId, attemptId: identity.attemptId,
126
+ requesterId: identity.requesterId, placementGeneration: identity.placementGeneration, route: canonicalPartitionRoute(identity.route) });
127
+ }
128
+ export function sameRouteAttemptIdentity(actual, expected) {
129
+ try { return canonicalPartitionJson(canonicalRouteAttemptBinding(actual)) === canonicalPartitionJson(canonicalRouteAttemptBinding(expected)); }
130
+ catch { return false; }
131
+ }
132
+
133
+ /** A ready stage is inventory, not a complete chat path. This checks advertised
134
+ * binding only; entry refresh, grants and reservations remain authoritative. */
135
+ export function isOrderedPartitionEntryModel(model, peer) {
136
+ try {
137
+ const selection = model?.partition;
138
+ if (peer.description?.schema !== 'reploid.partition-capability/v2' || model.index !== 0
139
+ || selection?.schema !== 'reploid.partition-selection/v2') return false;
140
+ const route = canonicalPartitionRoute(selection.route), first = route.stages[0];
141
+ return model.id === route.modelId && model.identity === route.modelIdentity
142
+ && model.planId === route.planId && selection.planId === route.planId
143
+ && selection.coordinatorId === first.participantId && first.participantId === peer.participantId
144
+ && first.transportId === peer.transportId && first.residentId === model.residentId
145
+ && first.executionId === model.executionId
146
+ && canonicalPartitionJson(route.activation) === canonicalPartitionJson(model.activation);
147
+ } catch { return false; }
148
+ }
149
+
150
+ /** One observed inventory version, bounded by host policy. These hints never authorize execution. */
151
+ export function validateOrderedPartitionCapability(description, participantId, maxOffersPerPeer) {
152
+ assertPartition(Number.isSafeInteger(maxOffersPerPeer) && maxOffersPerPeer > 0, 'Explicit stage offer limit required');
153
+ assertPartition(description?.schema === 'reploid.partition-capability/v2' && description.participantId === participantId
154
+ && ['idle', 'waiting', 'loading', 'ready', 'stopping', 'failed'].includes(description.phase)
155
+ && Array.isArray(description.models) && description.models.length <= maxOffersPerPeer,
156
+ 'Ordered partition capability identity or inventory mismatch');
157
+ const capacity = description.capacity;
158
+ assertPartition(capacity && typeof capacity.closed === 'boolean' && Number.isSafeInteger(capacity.active) && capacity.active >= 0
159
+ && Number.isSafeInteger(capacity.availableSlots) && capacity.availableSlots >= 0, 'Ordered partition capacity required');
160
+ for (const model of description.models) {
161
+ assertPartition(model && typeof model.id === 'string' && model.id.length > 0 && model.id.length <= 256
162
+ && /^sha256:[a-f0-9]{64}$/.test(model.identity) && Number.isSafeInteger(model.index) && model.index >= 0
163
+ && ['planId', 'residentId', 'executionId'].every(key => typeof model[key] === 'string' && model[key].length > 0 && model[key].length <= 256)
164
+ && ['ready', 'unavailable'].includes(model.availability), 'Invalid ordered partition offer');
165
+ }
166
+ }
167
+
168
+ /** Exact adapter selection travels in the plan digest, separate from the base manifest. */
169
+ export function partitionAdapterIdentities(model) {
170
+ const adapters = model?.adapters ?? [];
171
+ assertPartition(Array.isArray(adapters) && adapters.length <= 1 && adapters.every(adapter =>
172
+ /^sha256:[a-f0-9]{64}$/.test(adapter.identity) && adapter.baseModelIdentity === model.identity),
173
+ 'Partition adapter must target the exact base artifact');
174
+ return adapters.map(adapter => adapter.identity);
175
+ }
176
+ export function samePartitionAdapters(left, right) {
177
+ return canonicalPartitionJson(partitionAdapterIdentities(left)) === canonicalPartitionJson(partitionAdapterIdentities(right));
178
+ }
179
+ export function selectPartitionContribution(model, adapters = model.adapters ?? []) {
180
+ const selection = { ...model, adapters };
181
+ partitionAdapterIdentities(selection);
182
+ selection.adapters = adapters.map(adapter => {
183
+ const pinned = model.availableAdapters?.find(entry => entry.identity === adapter.identity
184
+ && entry.baseModelIdentity === model.identity);
185
+ assertPartition(pinned, 'Partition adapter is absent from the authorized catalog');
186
+ return structuredClone(pinned);
187
+ });
188
+ if (selection.partitionPlan && adapters.length) selection.partitionPlan = {
189
+ ...selection.partitionPlan, adapterIdentities: partitionAdapterIdentities(selection)
190
+ };
191
+ return structuredClone(selection);
192
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "maxControlBytes": 65536,
3
+ "maxCompletedSequences": 2,
4
+ "maxRetentionMs": 60000,
5
+ "maxRetainedBytes": 1048576,
6
+ "maxUnacknowledgedOutputs": 2
7
+ }