@opensoha/contracts 0.1.23 → 0.1.25

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 (415) hide show
  1. package/COMPATIBILITY.md +95 -0
  2. package/README.md +24 -6
  3. package/agent/environment/README.md +55 -0
  4. package/agent/environment/export.go +139 -0
  5. package/agent/environment/export_test.go +62 -0
  6. package/agent/environment/preflight.go +62 -0
  7. package/agent/environment/preflight_test.go +41 -0
  8. package/agent/environment/protocol.go +221 -0
  9. package/agent/environment/protocol_test.go +81 -0
  10. package/agent/environment-lease.schema.json +2 -2
  11. package/agent/environment-template.schema.json +1 -1
  12. package/agent/guest/browser_network.go +77 -0
  13. package/agent/guest/data.go +62 -0
  14. package/agent/guest/protocol.go +235 -0
  15. package/agent/guest/protocol_test.go +142 -0
  16. package/agent/guest/renewal.go +56 -0
  17. package/agent/guest/renewal_test.go +53 -0
  18. package/agent/guest/runtime.go +315 -0
  19. package/agent/guest/runtime_json.go +75 -0
  20. package/agent/guest/runtime_test.go +110 -0
  21. package/agent/personal-browser-network-request.schema.json +40 -0
  22. package/agent/personal-browser-network-response.schema.json +44 -0
  23. package/agent/personal-environment-export.schema.json +55 -0
  24. package/agent/personal-environment-preflight-request.schema.json +52 -0
  25. package/agent/personal-environment-preflight-result.schema.json +69 -0
  26. package/agent/personal-environment-request.schema.json +137 -0
  27. package/agent/personal-environment-result.schema.json +101 -0
  28. package/agent/personal-guest-binding.schema.json +92 -0
  29. package/agent/personal-guest-data-plan.schema.json +32 -0
  30. package/agent/personal-guest-enrollment.schema.json +21 -0
  31. package/agent/personal-guest-heartbeat.schema.json +44 -0
  32. package/agent/personal-guest-provisioning.schema.json +71 -0
  33. package/agent/personal-guest-renewal.schema.json +33 -0
  34. package/agent/personal-guest-runtime-result.schema.json +164 -0
  35. package/agent/personal-guest-runtime.schema.json +304 -0
  36. package/agent/personal-guest-tools.schema.json +75 -0
  37. package/auth/catalog_generated.go +2 -2
  38. package/auth/gateway_permissions_test.go +48 -0
  39. package/auth/permission-catalog.json +331 -1
  40. package/auth/token-claims.schema.json +5 -0
  41. package/compat/consumer-revisions.json +3 -2
  42. package/compat/json-schema-ecosystem-0.1.0.json +1 -1
  43. package/compat/openapi-loose-boundaries-0.1.0.json +1 -1
  44. package/connectors/connector-action.schema.json +138 -0
  45. package/fixtures/json-schema/connector-action/invalid/empty-values.json +11 -0
  46. package/fixtures/json-schema/connector-action/invalid/invalid-expiry.json +16 -0
  47. package/fixtures/json-schema/connector-action/invalid/lowercase-alias.json +16 -0
  48. package/fixtures/json-schema/connector-action/invalid/unknown-entry-field.json +17 -0
  49. package/fixtures/json-schema/connector-action/invalid/unknown-frame-field.json +17 -0
  50. package/fixtures/json-schema/connector-action/invalid/unknown-version.json +16 -0
  51. package/fixtures/json-schema/connector-action/invalid/unversioned-reference.json +16 -0
  52. package/fixtures/json-schema/connector-action/invalid/zero-version.json +16 -0
  53. package/fixtures/json-schema/connector-action/valid/pinned.json +16 -0
  54. package/fixtures/json-schema/connector-definition/invalid/credential-userinfo.json +8 -0
  55. package/fixtures/json-schema/connector-definition/invalid/reference-traversal.json +8 -0
  56. package/fixtures/json-schema/connector-definition/invalid/zero-version.json +8 -0
  57. package/fixtures/json-schema/connector-definition/valid/canonical.json +8 -0
  58. package/fixtures/json-schema/connector-definition/valid/pinned.json +8 -0
  59. package/fixtures/json-schema/personal-browser-network-request/invalid/bounded.json +4 -0
  60. package/fixtures/json-schema/personal-browser-network-request/valid/bounded.json +4 -0
  61. package/fixtures/json-schema/personal-browser-network-response/invalid/bounded.json +5 -0
  62. package/fixtures/json-schema/personal-browser-network-response/valid/bounded.json +5 -0
  63. package/fixtures/json-schema/personal-environment-export/invalid/extra.json +11 -0
  64. package/fixtures/json-schema/personal-environment-export/invalid/ownerId.json +10 -0
  65. package/fixtures/json-schema/personal-environment-export/invalid/sha256.json +10 -0
  66. package/fixtures/json-schema/personal-environment-export/invalid/sizeBytes.json +10 -0
  67. package/fixtures/json-schema/personal-environment-export/valid/complete.json +10 -0
  68. package/fixtures/json-schema/personal-environment-preflight-request/invalid/disk.json +11 -0
  69. package/fixtures/json-schema/personal-environment-preflight-request/invalid/network.json +11 -0
  70. package/fixtures/json-schema/personal-environment-preflight-request/invalid/node.json +12 -0
  71. package/fixtures/json-schema/personal-environment-preflight-request/invalid/owner.json +12 -0
  72. package/fixtures/json-schema/personal-environment-preflight-request/valid/bounded.json +11 -0
  73. package/fixtures/json-schema/personal-environment-preflight-result/invalid/disk.json +15 -0
  74. package/fixtures/json-schema/personal-environment-preflight-result/invalid/network.json +15 -0
  75. package/fixtures/json-schema/personal-environment-preflight-result/invalid/no-hard-quota.json +15 -0
  76. package/fixtures/json-schema/personal-environment-preflight-result/invalid/node.json +16 -0
  77. package/fixtures/json-schema/personal-environment-preflight-result/invalid/not-ready.json +15 -0
  78. package/fixtures/json-schema/personal-environment-preflight-result/invalid/owner.json +16 -0
  79. package/fixtures/json-schema/personal-environment-preflight-result/valid/bounded.json +15 -0
  80. package/fixtures/json-schema/personal-environment-request/invalid/delete-data-argv.json +24 -0
  81. package/fixtures/json-schema/personal-environment-request/invalid/delete-data-createData.json +22 -0
  82. package/fixtures/json-schema/personal-environment-request/invalid/delete-data-taskId.json +22 -0
  83. package/fixtures/json-schema/personal-environment-request/invalid/export-argv.json +22 -0
  84. package/fixtures/json-schema/personal-environment-request/invalid/export-createData.json +22 -0
  85. package/fixtures/json-schema/personal-environment-request/invalid/export-taskId.json +22 -0
  86. package/fixtures/json-schema/personal-environment-request/invalid/extra-command.json +24 -0
  87. package/fixtures/json-schema/personal-environment-request/invalid/host-workspace.json +22 -0
  88. package/fixtures/json-schema/personal-environment-request/invalid/image-injection.json +22 -0
  89. package/fixtures/json-schema/personal-environment-request/invalid/network-egress.json +21 -0
  90. package/fixtures/json-schema/personal-environment-request/invalid/no-command.json +21 -0
  91. package/fixtures/json-schema/personal-environment-request/invalid/recreate-on-release.json +22 -0
  92. package/fixtures/json-schema/personal-environment-request/invalid/relative-command.json +24 -0
  93. package/fixtures/json-schema/personal-environment-request/invalid/released-identity.json +22 -0
  94. package/fixtures/json-schema/personal-environment-request/invalid/task-empty.json +27 -0
  95. package/fixtures/json-schema/personal-environment-request/invalid/task-host-path.json +27 -0
  96. package/fixtures/json-schema/personal-environment-request/invalid/task-noncanonical.json +27 -0
  97. package/fixtures/json-schema/personal-environment-request/invalid/task-null.json +27 -0
  98. package/fixtures/json-schema/personal-environment-request/invalid/task-on-inspection.json +22 -0
  99. package/fixtures/json-schema/personal-environment-request/invalid/unbounded-quota.json +21 -0
  100. package/fixtures/json-schema/personal-environment-request/valid/acquire.json +21 -0
  101. package/fixtures/json-schema/personal-environment-request/valid/delete-data.json +21 -0
  102. package/fixtures/json-schema/personal-environment-request/valid/execute.json +26 -0
  103. package/fixtures/json-schema/personal-environment-request/valid/export-data.json +21 -0
  104. package/fixtures/json-schema/personal-environment-request/valid/inspect.json +21 -0
  105. package/fixtures/json-schema/personal-environment-request/valid/provision.json +22 -0
  106. package/fixtures/json-schema/personal-environment-request/valid/release.json +21 -0
  107. package/fixtures/json-schema/personal-environment-request/valid/task-workspace.json +27 -0
  108. package/fixtures/json-schema/personal-environment-result/invalid/active-without-observation.json +7 -0
  109. package/fixtures/json-schema/personal-environment-result/invalid/deleted-output.json +8 -0
  110. package/fixtures/json-schema/personal-environment-result/invalid/deleted-snapshot.json +8 -0
  111. package/fixtures/json-schema/personal-environment-result/invalid/deleted-taskId.json +8 -0
  112. package/fixtures/json-schema/personal-environment-result/invalid/private-handle.json +8 -0
  113. package/fixtures/json-schema/personal-environment-result/invalid/stopped-fake-observation.json +20 -0
  114. package/fixtures/json-schema/personal-environment-result/invalid/task-empty.json +22 -0
  115. package/fixtures/json-schema/personal-environment-result/invalid/task-host-path.json +22 -0
  116. package/fixtures/json-schema/personal-environment-result/invalid/task-null.json +22 -0
  117. package/fixtures/json-schema/personal-environment-result/invalid/task-on-stopped.json +8 -0
  118. package/fixtures/json-schema/personal-environment-result/invalid/task-without-command-output.json +21 -0
  119. package/fixtures/json-schema/personal-environment-result/valid/active.json +21 -0
  120. package/fixtures/json-schema/personal-environment-result/valid/deleted.json +7 -0
  121. package/fixtures/json-schema/personal-environment-result/valid/expired.json +7 -0
  122. package/fixtures/json-schema/personal-environment-result/valid/missing.json +7 -0
  123. package/fixtures/json-schema/personal-environment-result/valid/released.json +7 -0
  124. package/fixtures/json-schema/personal-environment-result/valid/task-workspace.json +22 -0
  125. package/fixtures/json-schema/personal-guest-binding/invalid/host-path.json +17 -0
  126. package/fixtures/json-schema/personal-guest-binding/invalid/invalid-policy.json +17 -0
  127. package/fixtures/json-schema/personal-guest-binding/invalid/invalid-serial.json +17 -0
  128. package/fixtures/json-schema/personal-guest-binding/invalid/legacy-version.json +17 -0
  129. package/fixtures/json-schema/personal-guest-binding/invalid/missing-generation.json +17 -0
  130. package/fixtures/json-schema/personal-guest-binding/invalid/missing-identity.json +16 -0
  131. package/fixtures/json-schema/personal-guest-binding/invalid/missing-provider-identity.json +16 -0
  132. package/fixtures/json-schema/personal-guest-binding/invalid/raw-provider-identity.json +17 -0
  133. package/fixtures/json-schema/personal-guest-binding/valid/minimal.json +17 -0
  134. package/fixtures/json-schema/personal-guest-data-plan/invalid/quota.json +1 -0
  135. package/fixtures/json-schema/personal-guest-data-plan/valid/basic.json +1 -0
  136. package/fixtures/json-schema/personal-guest-enrollment/invalid/capabilities.json +7 -0
  137. package/fixtures/json-schema/personal-guest-enrollment/invalid/legacy-version.json +4 -0
  138. package/fixtures/json-schema/personal-guest-enrollment/invalid/missing-identity.json +3 -0
  139. package/fixtures/json-schema/personal-guest-enrollment/invalid/ownerId.json +5 -0
  140. package/fixtures/json-schema/personal-guest-enrollment/invalid/token.json +5 -0
  141. package/fixtures/json-schema/personal-guest-enrollment/valid/minimal.json +4 -0
  142. package/fixtures/json-schema/personal-guest-heartbeat/invalid/argv.json +11 -0
  143. package/fixtures/json-schema/personal-guest-heartbeat/invalid/legacy-version.json +8 -0
  144. package/fixtures/json-schema/personal-guest-heartbeat/invalid/missing-identity.json +7 -0
  145. package/fixtures/json-schema/personal-guest-heartbeat/invalid/owner.json +9 -0
  146. package/fixtures/json-schema/personal-guest-heartbeat/invalid/self-ready.json +9 -0
  147. package/fixtures/json-schema/personal-guest-heartbeat/invalid/zero-sequence.json +8 -0
  148. package/fixtures/json-schema/personal-guest-heartbeat/valid/minimal.json +8 -0
  149. package/fixtures/json-schema/personal-guest-provisioning/invalid/network.json +1 -0
  150. package/fixtures/json-schema/personal-guest-provisioning/valid/basic.json +1 -0
  151. package/fixtures/json-schema/personal-guest-renewal/invalid/unknown.json +1 -0
  152. package/fixtures/json-schema/personal-guest-renewal/valid/basic.json +1 -0
  153. package/fixtures/json-schema/personal-guest-runtime/invalid/minimal.json +45 -0
  154. package/fixtures/json-schema/personal-guest-runtime/valid/minimal.json +45 -0
  155. package/fixtures/json-schema/personal-guest-runtime-result/invalid/minimal.json +28 -0
  156. package/fixtures/json-schema/personal-guest-runtime-result/valid/minimal.json +27 -0
  157. package/fixtures/json-schema/personal-guest-tools/invalid/minimal.json +31 -0
  158. package/fixtures/json-schema/personal-guest-tools/valid/minimal.json +30 -0
  159. package/fixtures/json-schema/source-import-policy/invalid/case-variant-scope.json +4 -0
  160. package/fixtures/json-schema/source-import-policy/invalid/case-variant-version.json +4 -0
  161. package/fixtures/json-schema/source-import-policy/invalid/duplicate-grant.json +4 -0
  162. package/fixtures/json-schema/source-import-policy/invalid/empty-id.json +4 -0
  163. package/fixtures/json-schema/source-import-policy/invalid/future-version.json +4 -0
  164. package/fixtures/json-schema/source-import-policy/invalid/unknown-field.json +4 -0
  165. package/fixtures/json-schema/source-import-policy/valid/deny-all.json +4 -0
  166. package/fixtures/json-schema/source-import-policy/valid/shared.json +4 -0
  167. package/fixtures/json-schema/token-claims/invalid/authz-negative.json +23 -0
  168. package/fixtures/json-schema/token-claims/invalid/authz-string.json +23 -0
  169. package/fixtures/json-schema/token-claims/invalid/authz-zero.json +23 -0
  170. package/fixtures/json-schema/token-claims/valid/versioned-session.json +23 -0
  171. package/fixtures/openapi/AgentRunEventListEnvelope/invalid/basic.json +8 -0
  172. package/fixtures/openapi/AgentRunEventListEnvelope/valid/basic.json +8 -0
  173. package/fixtures/openapi/AgentRunEventListEnvelope/valid/native.json +1 -0
  174. package/fixtures/openapi/AgentRunResumeRequest/invalid/basic.json +3 -0
  175. package/fixtures/openapi/AgentRunResumeRequest/valid/basic.json +3 -0
  176. package/fixtures/openapi/AgentSSHCommandRequest/invalid/task51.json +24 -0
  177. package/fixtures/openapi/AgentSSHCommandRequest/valid/task51.json +23 -0
  178. package/fixtures/openapi/AgentSSHCommandResult/invalid/task51.json +8 -0
  179. package/fixtures/openapi/AgentSSHCommandResult/valid/task51.json +7 -0
  180. package/fixtures/openapi/AgentSSHSessionRequest/invalid/task51.json +20 -0
  181. package/fixtures/openapi/AgentSSHSessionRequest/valid/task51.json +19 -0
  182. package/fixtures/openapi/CapabilityTaskInput/invalid/duplicate-scope.json +36 -0
  183. package/fixtures/openapi/CapabilityTaskInput/valid/scoped.json +32 -0
  184. package/fixtures/openapi/DockerQuickCreateHostInput/invalid/guest-access.json +7 -0
  185. package/fixtures/openapi/DockerQuickCreateHostInput/valid/guest-access.json +9 -0
  186. package/fixtures/openapi/KnowledgeSourceDocument/invalid/tombstone-with-content.json +1 -0
  187. package/fixtures/openapi/KnowledgeSourceDocument/valid/tombstone.json +1 -0
  188. package/fixtures/openapi/KubernetesAgentActionCheck/invalid/unproven-state.json +6 -0
  189. package/fixtures/openapi/KubernetesAgentActionCheck/valid/configured-denied.json +9 -0
  190. package/fixtures/openapi/KubernetesAgentActionProfile/invalid/wildcard.json +1 -0
  191. package/fixtures/openapi/KubernetesAgentActionProfile/valid/workbench.json +1 -0
  192. package/fixtures/openapi/KubernetesClusterRegistrationRequest/invalid/action-profile.json +3 -0
  193. package/fixtures/openapi/KubernetesClusterRegistrationRequest/valid/action-profile.json +5 -0
  194. package/fixtures/openapi/KubernetesNodeListEnvelope/invalid/condition-status-boolean.json +17 -0
  195. package/fixtures/openapi/KubernetesNodeListEnvelope/invalid/taint-effect-missing.json +16 -0
  196. package/fixtures/openapi/KubernetesNodeListEnvelope/valid/legacy-fields-absent.json +11 -0
  197. package/fixtures/openapi/KubernetesNodeListEnvelope/valid/no-taints.json +13 -0
  198. package/fixtures/openapi/KubernetesNodeListEnvelope/valid/ready-cordoned-tainted.json +28 -0
  199. package/fixtures/openapi/KubernetesNodeListEnvelope/valid/unknown-fields-null.json +13 -0
  200. package/fixtures/openapi/KubernetesResourceUpdatePlanRequest/invalid/missing-required.json +1 -0
  201. package/fixtures/openapi/KubernetesResourceYaml/invalid/missing-required.json +1 -0
  202. package/fixtures/openapi/KubernetesTopologyCoverage/invalid/empty-as-success.json +5 -0
  203. package/fixtures/openapi/KubernetesTopologyCoverage/valid/not-installed.json +6 -0
  204. package/fixtures/openapi/KubernetesTopologyGraphEdge/invalid/negative-weight.json +8 -0
  205. package/fixtures/openapi/KubernetesTopologyGraphEdge/valid/zero-weight.json +20 -0
  206. package/fixtures/openapi/KubernetesTopologyGraphNode/invalid/fake-flow-kind.json +42 -0
  207. package/fixtures/openapi/KubernetesTopologyGraphNode/valid/rule.json +42 -0
  208. package/fixtures/openapi/KubernetesTopologySnapshot/invalid/raw-source-field.json +33 -0
  209. package/fixtures/openapi/KubernetesTopologySnapshot/valid/projected-source.json +32 -0
  210. package/fixtures/openapi/KubernetesTopologySnapshotRequest/invalid/arbitrary-gvr.json +8 -0
  211. package/fixtures/openapi/KubernetesTopologySnapshotRequest/valid/bounded-sources.json +11 -0
  212. package/fixtures/openapi/KubernetesTopologySourceQuery/invalid/cluster-namespace.json +4 -0
  213. package/fixtures/openapi/KubernetesTopologySourceQuery/valid/all-namespaces.json +4 -0
  214. package/fixtures/openapi/ObservabilityDataSource/invalid/missing-required.json +1 -0
  215. package/fixtures/openapi/PersonalAgentAccess/invalid/invalid.json +1 -0
  216. package/fixtures/openapi/PersonalAgentAccess/valid/valid.json +1 -0
  217. package/fixtures/openapi/PersonalAgentAccessDecision/invalid/invalid.json +1 -0
  218. package/fixtures/openapi/PersonalAgentAccessDecision/valid/valid.json +1 -0
  219. package/fixtures/openapi/PersonalAssistantArtifactListEnvelope/invalid/owner.json +8 -0
  220. package/fixtures/openapi/PersonalAssistantArtifactListEnvelope/valid/side-chat.json +9 -0
  221. package/fixtures/openapi/PersonalAssistantAvatarRequest/invalid/motion.json +1 -0
  222. package/fixtures/openapi/PersonalAssistantAvatarRequest/invalid/owner.json +1 -0
  223. package/fixtures/openapi/PersonalAssistantAvatarRequest/invalid/remote.json +1 -0
  224. package/fixtures/openapi/PersonalAssistantAvatarRequest/invalid/svg.json +1 -0
  225. package/fixtures/openapi/PersonalAssistantAvatarRequest/valid/raster.json +1 -0
  226. package/fixtures/openapi/PersonalAssistantAvatarRequest/valid/reset.json +1 -0
  227. package/fixtures/openapi/PersonalAssistantUpdateRequest/invalid/empty.json +1 -0
  228. package/fixtures/openapi/PersonalAssistantUpdateRequest/invalid/owner.json +1 -0
  229. package/fixtures/openapi/PersonalAssistantUpdateRequest/valid/name.json +1 -0
  230. package/fixtures/openapi/PersonalDockerEnvironmentPolicy/invalid/host-url.json +12 -0
  231. package/fixtures/openapi/PersonalDockerEnvironmentPolicy/invalid/lifetime.json +11 -0
  232. package/fixtures/openapi/PersonalDockerEnvironmentPolicy/invalid/unknown-agent.json +11 -0
  233. package/fixtures/openapi/PersonalDockerEnvironmentPolicy/valid/basic.json +11 -0
  234. package/fixtures/openapi/PersonalEnvironment/invalid/invented-state.json +6 -0
  235. package/fixtures/openapi/PersonalEnvironment/invalid/missing-state.json +5 -0
  236. package/fixtures/openapi/PersonalEnvironment/invalid/negative-generation.json +7 -0
  237. package/fixtures/openapi/PersonalEnvironment/invalid/network.json +23 -0
  238. package/fixtures/openapi/PersonalEnvironment/invalid/private-node.json +24 -0
  239. package/fixtures/openapi/PersonalEnvironment/invalid/unsupported-backend.json +7 -0
  240. package/fixtures/openapi/PersonalEnvironment/valid/deleted.json +23 -0
  241. package/fixtures/openapi/PersonalEnvironment/valid/deleting.json +23 -0
  242. package/fixtures/openapi/PersonalEnvironment/valid/deletion_unknown.json +23 -0
  243. package/fixtures/openapi/PersonalEnvironment/valid/frozen-binding.json +13 -0
  244. package/fixtures/openapi/PersonalEnvironment/valid/ready.json +23 -0
  245. package/fixtures/openapi/PersonalEnvironment/valid/unknown.json +23 -0
  246. package/fixtures/openapi/PersonalEnvironment/valid/unprovisioned.json +6 -0
  247. package/fixtures/openapi/PersonalEnvironmentActionRequest/invalid/command.json +3 -0
  248. package/fixtures/openapi/PersonalEnvironmentActionRequest/invalid/node.json +4 -0
  249. package/fixtures/openapi/PersonalEnvironmentActionRequest/invalid/owner.json +4 -0
  250. package/fixtures/openapi/PersonalEnvironmentActionRequest/invalid/quota.json +10 -0
  251. package/fixtures/openapi/PersonalEnvironmentActionRequest/valid/reconcile.json +3 -0
  252. package/fixtures/openapi/PersonalEnvironmentActionRequest/valid/sleep.json +3 -0
  253. package/fixtures/openapi/PersonalEnvironmentActionRequest/valid/wake.json +4 -0
  254. package/fixtures/openapi/PersonalEnvironmentDataDeletionRequest/invalid/empty-name.json +5 -0
  255. package/fixtures/openapi/PersonalEnvironmentDataDeletionRequest/invalid/long-name.json +5 -0
  256. package/fixtures/openapi/PersonalEnvironmentDataDeletionRequest/invalid/missing-name.json +4 -0
  257. package/fixtures/openapi/PersonalEnvironmentDataDeletionRequest/invalid/owner.json +6 -0
  258. package/fixtures/openapi/PersonalEnvironmentDataDeletionRequest/invalid/stale-version.json +5 -0
  259. package/fixtures/openapi/PersonalEnvironmentDataDeletionRequest/valid/confirmed.json +5 -0
  260. package/fixtures/openapi/PersonalEnvironmentDataExportRequest/invalid/assistantId.json +4 -0
  261. package/fixtures/openapi/PersonalEnvironmentDataExportRequest/invalid/environmentVersion.json +4 -0
  262. package/fixtures/openapi/PersonalEnvironmentDataExportRequest/invalid/ownerId.json +5 -0
  263. package/fixtures/openapi/PersonalEnvironmentDataExportRequest/valid/current.json +4 -0
  264. package/fixtures/openapi/PersonalEnvironmentEnvelope/invalid/missing-data.json +1 -0
  265. package/fixtures/openapi/PersonalEnvironmentEnvelope/valid/state.json +8 -0
  266. package/fixtures/openapi/PersonalEnvironmentQuota/invalid/cpu.json +7 -0
  267. package/fixtures/openapi/PersonalEnvironmentQuota/invalid/disk.json +7 -0
  268. package/fixtures/openapi/PersonalEnvironmentQuota/invalid/network.json +7 -0
  269. package/fixtures/openapi/PersonalEnvironmentQuota/invalid/processes.json +7 -0
  270. package/fixtures/openapi/PersonalEnvironmentQuota/valid/basic.json +7 -0
  271. package/fixtures/openapi/PersonalEnvironmentTemplate/invalid/mode.json +13 -0
  272. package/fixtures/openapi/PersonalEnvironmentTemplate/invalid/network.json +13 -0
  273. package/fixtures/openapi/PersonalEnvironmentTemplate/invalid/node.json +14 -0
  274. package/fixtures/openapi/PersonalEnvironmentTemplate/valid/template.json +13 -0
  275. package/fixtures/openapi/PersonalEnvironmentTemplateListEnvelope/invalid/missing.json +1 -0
  276. package/fixtures/openapi/PersonalEnvironmentTemplateListEnvelope/invalid/unbounded.json +1317 -0
  277. package/fixtures/openapi/PersonalEnvironmentTemplateListEnvelope/valid/choices.json +17 -0
  278. package/fixtures/openapi/PersonalEnvironmentTemplateListEnvelope/valid/empty.json +3 -0
  279. package/fixtures/openapi/PersonalEnvironmentUsage/invalid/negative.json +6 -0
  280. package/fixtures/openapi/PersonalEnvironmentUsage/valid/usage.json +6 -0
  281. package/fixtures/openapi/PersonalFeedFeedback/invalid/owner.json +4 -0
  282. package/fixtures/openapi/PersonalFeedFeedback/valid/liked.json +3 -0
  283. package/fixtures/openapi/PersonalFeedItem/invalid/no-source.json +9 -0
  284. package/fixtures/openapi/PersonalFeedItem/valid/post.json +15 -0
  285. package/fixtures/openapi/PersonalFeedSettings/invalid/owner.json +6 -0
  286. package/fixtures/openapi/PersonalFeedSettings/valid/default.json +5 -0
  287. package/fixtures/openapi/PersonalGuestPreparationInput/invalid/foreign-owner.json +5 -0
  288. package/fixtures/openapi/PersonalGuestPreparationInput/invalid/unsafe-counter.json +4 -0
  289. package/fixtures/openapi/PersonalGuestPreparationInput/valid/ed25519-csr.json +4 -0
  290. package/fixtures/openapi/PersonalGuestRuntimeEnvelope/invalid/minimal.json +10 -0
  291. package/fixtures/openapi/PersonalGuestRuntimeEnvelope/valid/minimal.json +9 -0
  292. package/fixtures/openapi/PersonalGuestRuntimeInput/invalid/minimal.json +6 -0
  293. package/fixtures/openapi/PersonalGuestRuntimeInput/valid/minimal.json +8 -0
  294. package/fixtures/openapi/PersonalGuestToolsInput/invalid/minimal.json +5 -0
  295. package/fixtures/openapi/PersonalGuestToolsInput/valid/minimal.json +4 -0
  296. package/fixtures/openapi/PersonalVMEnvironmentPolicy/invalid/device.json +15 -0
  297. package/fixtures/openapi/PersonalVMEnvironmentPolicy/invalid/fractional-size.json +15 -0
  298. package/fixtures/openapi/PersonalVMEnvironmentPolicy/invalid/network.json +15 -0
  299. package/fixtures/openapi/PersonalVMEnvironmentPolicy/invalid/owner.json +15 -0
  300. package/fixtures/openapi/PersonalVMEnvironmentPolicy/invalid/provider.json +15 -0
  301. package/fixtures/openapi/PersonalVMEnvironmentPolicy/invalid/unknown.json +16 -0
  302. package/fixtures/openapi/PersonalVMEnvironmentPolicy/valid/pve.json +15 -0
  303. package/fixtures/openapi/PersonalWorkspaceRuntime/invalid/shared.json +3 -0
  304. package/fixtures/openapi/PersonalWorkspaceRuntime/valid/shared.json +4 -0
  305. package/fixtures/openapi/PersonalWorkspaceRuntimeEnvelope/invalid/shared.json +5 -0
  306. package/fixtures/openapi/PersonalWorkspaceRuntimeEnvelope/valid/shared.json +6 -0
  307. package/fixtures/openapi/ResourceMonitoringPanel/invalid/unknown-quality.json +7 -0
  308. package/fixtures/openapi/ResourceMonitoringPanel/valid/not-collected.json +7 -0
  309. package/fixtures/openapi/ResourceMonitoringPoint/invalid/string-zero.json +4 -0
  310. package/fixtures/openapi/ResourceMonitoringPoint/valid/missing.json +4 -0
  311. package/fixtures/openapi/ResourceMonitoringPoint/valid/zero.json +4 -0
  312. package/fixtures/openapi/ResourceMonitoringView/invalid/raw-query.json +12 -0
  313. package/fixtures/openapi/ResourceMonitoringView/valid/unconfigured.json +12 -0
  314. package/fixtures/openapi/SecretGenerateSSHKeyRequest/invalid/task51.json +6 -0
  315. package/fixtures/openapi/SecretGenerateSSHKeyRequest/valid/task51.json +12 -0
  316. package/fixtures/openapi/SecretReferenceUsage/invalid/version.json +6 -0
  317. package/fixtures/openapi/SecretReferenceUsage/valid/version.json +8 -0
  318. package/fixtures/openapi/VMSSHCredentialRedemptionInput/invalid/task51.json +7 -0
  319. package/fixtures/openapi/VMSSHCredentialRedemptionInput/valid/task51.json +6 -0
  320. package/fixtures/openapi/VirtualMachineAccessBindingInput/invalid/task51.json +10 -0
  321. package/fixtures/openapi/VirtualMachineAccessBindingInput/valid/task51.json +10 -0
  322. package/fixtures/openapi/VirtualMachineActionInput/invalid/capacity-not-boolean.json +1 -0
  323. package/fixtures/openapi/VirtualMachineActionInput/valid/new-data-with-admission.json +1 -0
  324. package/fixtures/openapi/VirtualMachineCreateInput/valid/cloud-image.json +12 -0
  325. package/fixtures/openapi/VirtualMachineDiskChange/invalid/serial-config-injection.json +1 -0
  326. package/fixtures/openapi/VirtualMachineDiskChange/invalid/serial-existing-disk.json +1 -0
  327. package/fixtures/openapi/VirtualMachineDiskChange/invalid/serial-too-long.json +1 -0
  328. package/fixtures/openapi/VirtualMachineDiskChange/valid/personal-data.json +1 -0
  329. package/fixtures/openapi/VirtualMachineGuestAccessInput/invalid/task51.json +4 -0
  330. package/fixtures/openapi/VirtualMachineGuestAccessInput/valid/task51.json +6 -0
  331. package/fixtures/openapi/VirtualMachineSSHCommand/invalid/task51.json +9 -0
  332. package/fixtures/openapi/VirtualMachineSSHCommand/valid/task51.json +8 -0
  333. package/fixtures/openapi/VirtualMachineSSHCommandInput/invalid/task51.json +5 -0
  334. package/fixtures/openapi/VirtualMachineSSHCommandInput/valid/task51.json +5 -0
  335. package/fixtures/openapi/VirtualMachineSSHFrame/invalid/encoding.json +5 -0
  336. package/fixtures/openapi/VirtualMachineSSHFrame/invalid/task51.json +4 -0
  337. package/fixtures/openapi/VirtualMachineSSHFrame/valid/binary.json +5 -0
  338. package/fixtures/openapi/VirtualMachineSSHFrame/valid/task51.json +5 -0
  339. package/fixtures/openapi/VirtualMachineSSHSessionInput/invalid/task51.json +4 -0
  340. package/fixtures/openapi/VirtualMachineSSHSessionInput/valid/task51.json +5 -0
  341. package/fixtures/openapi/VirtualizationCapacityPolicyInput/invalid/task51.json +12 -0
  342. package/fixtures/openapi/VirtualizationCapacityPolicyInput/valid/task51.json +13 -0
  343. package/fixtures/openapi/VirtualizationImageInput/invalid/cloud-image-config-type.json +6 -0
  344. package/fixtures/openapi/VirtualizationImageInput/valid/cloud-image.json +11 -0
  345. package/fixtures/openapi/WorkbenchAssistantTask/invalid/kind.json +1 -0
  346. package/fixtures/openapi/WorkbenchAssistantTask/invalid/owner.json +1 -0
  347. package/fixtures/openapi/WorkbenchAssistantTask/valid/generate.json +1 -0
  348. package/fixtures/openapi/WorkbenchAssistantTask/valid/undo.json +1 -0
  349. package/fixtures/openapi/WorkbenchContextSelection/invalid/remote-image.json +1 -0
  350. package/fixtures/openapi/WorkbenchContextSelection/invalid/too-many-images.json +1 -0
  351. package/fixtures/openapi/WorkbenchContextSelection/valid/chat-image.json +9 -0
  352. package/fixtures/openapi/WorkbenchContextUsage/invalid/fractional-capacity.json +1 -0
  353. package/fixtures/openapi/WorkbenchContextUsage/invalid/negative.json +1 -0
  354. package/fixtures/openapi/WorkbenchContextUsage/valid/reported.json +1 -0
  355. package/fixtures/openapi/WorkbenchContextUsage/valid/unknown.json +1 -0
  356. package/fixtures/openapi/WorkbenchMainSessionRequest/invalid/owner.json +1 -0
  357. package/fixtures/openapi/WorkbenchMainSessionRequest/invalid/provider.json +1 -0
  358. package/fixtures/openapi/WorkbenchMainSessionRequest/valid/empty.json +1 -0
  359. package/fixtures/openapi/WorkbenchMainSessionRequest/valid/provider.json +1 -0
  360. package/fixtures/openapi/WorkbenchMessageSearchPage/invalid/role.json +12 -0
  361. package/fixtures/openapi/WorkbenchMessageSearchPage/valid/page.json +12 -0
  362. package/fixtures/openapi/WorkbenchMessageWindow/invalid/missing.json +1 -0
  363. package/fixtures/openapi/WorkbenchMessageWindow/valid/empty.json +3 -0
  364. package/fixtures/openapi/WorkbenchModelOption/invalid/zero-capacity.json +1 -0
  365. package/fixtures/openapi/WorkbenchModelOption/valid/capacity.json +1 -0
  366. package/fixtures/openapi/WorkbenchModelOption/valid/unknown.json +1 -0
  367. package/fixtures/openapi/WorkbenchProfessionalCapabilityInput/invalid/basic.json +7 -0
  368. package/fixtures/openapi/WorkbenchProfessionalCapabilityInput/valid/basic.json +11 -0
  369. package/fixtures/openapi/WorkbenchProfessionalCapabilityInput/valid/selected-profile.json +23 -0
  370. package/fixtures/openapi/WorkbenchSendMessageStreamRequest/invalid/missing-required.json +1 -0
  371. package/fixtures/openapi/WorkbenchSendMessageStreamRequest/valid/mixed-professional-task.json +22 -0
  372. package/fixtures/openapi/WorkbenchTaskIntent/invalid/basic.json +5 -0
  373. package/fixtures/openapi/WorkbenchTaskIntent/invalid/oversized-capability-source.json +3 -0
  374. package/fixtures/openapi/WorkbenchTaskIntent/valid/basic.json +9 -0
  375. package/fixtures/openapi/WorkbenchTaskIntent/valid/independent-sources.json +17 -0
  376. package/gen/go/sohaapi/client.go +1 -1
  377. package/gen/go/sohaapi/kubernetes_monitoring_test.go +67 -0
  378. package/gen/go/sohaapi/personal_environment_test.go +25 -0
  379. package/gen/go/sohaapi/types.go +3460 -433
  380. package/gen/go/sohaapi/vm_disk_test.go +21 -0
  381. package/gen/ts/sohaapi/index.ts +5454 -1397
  382. package/go.mod +61 -49
  383. package/go.sum +202 -176
  384. package/knowledge/connector-definition.schema.json +6 -1
  385. package/knowledge/source-import-policy.schema.json +32 -0
  386. package/memory/memory-record.schema.json +1 -1
  387. package/openapi/soha-api.json +24199 -16810
  388. package/openapi/soha-api.yaml +2537 -14
  389. package/openapi/spec_generated.go +3 -3
  390. package/openapi/spec_test.go +34 -0
  391. package/package.json +19 -3
  392. package/resource/agent_access.go +12 -0
  393. package/resource/agent_profiles.go +62 -0
  394. package/resource/agent_profiles_test.go +36 -0
  395. package/resource/kubernetes_kinds.go +73 -0
  396. package/resource/kubernetes_kinds_test.go +38 -0
  397. package/resource/models.go +23 -14
  398. package/resource/network_topology.go +166 -0
  399. package/resource/network_topology_projection.go +208 -0
  400. package/resource/network_topology_projection_test.go +53 -0
  401. package/resource/network_topology_test.go +51 -0
  402. package/resource/network_topology_validation.go +29 -0
  403. package/resource/node_drain.go +34 -0
  404. package/resource/resource_monitoring.go +99 -0
  405. package/resource/runtime/network_topology.go +232 -0
  406. package/resource/runtime/network_topology_test.go +62 -0
  407. package/resource/runtime/node_drain.go +124 -0
  408. package/resource/runtime/node_drain_test.go +95 -0
  409. package/resource/runtime/resource_edit.go +128 -0
  410. package/resource/runtime/resource_edit_test.go +183 -0
  411. package/resource/runtime/resource_monitoring.go +243 -0
  412. package/resource/runtime/resource_monitoring_test.go +88 -0
  413. /package/fixtures/openapi/DeliveryExecutionHistoryPage/{invalid-kind.json → invalid/invalid-kind.json} +0 -0
  414. /package/fixtures/openapi/DeliveryExecutionHistoryPage/{valid-build.json → valid/valid-build.json} +0 -0
  415. /package/fixtures/openapi/DeliveryExecutionHistoryPage/{valid-empty.json → valid/valid-empty.json} +0 -0
@@ -0,0 +1,221 @@
1
+ // Package environment carries the Core-to-Agent personal environment protocol.
2
+ // JSON Schemas in agent/ own the wire shapes; Docker handles stay Agent-private.
3
+ package environment
4
+
5
+ import (
6
+ "bytes"
7
+ "encoding/json"
8
+ "errors"
9
+ "regexp"
10
+ "strings"
11
+ "time"
12
+ "unicode/utf8"
13
+ )
14
+
15
+ const RequestVersion = "opensoha.dev/personal-environment-request/v1"
16
+ const ResultVersion = "opensoha.dev/personal-environment-result/v1"
17
+ const LeaseVersion = "opensoha.dev/environment-lease/v1"
18
+ const SnapshotVersion = "opensoha.dev/environment-snapshot/v1"
19
+
20
+ type Quota struct {
21
+ DiskBytes int64 `json:"diskBytes"`
22
+ MemoryBytes int64 `json:"memoryBytes"`
23
+ CPUUnits int64 `json:"cpuUnits"`
24
+ MaxProcesses int `json:"maxProcesses"`
25
+ NetworkEgress bool `json:"networkEgress"`
26
+ }
27
+
28
+ type Lease struct {
29
+ SchemaVersion string `json:"schemaVersion"`
30
+ ID string `json:"id"`
31
+ RunID string `json:"runId"`
32
+ OwnerID string `json:"ownerId"`
33
+ ScopeHash string `json:"scopeHash"`
34
+ Mode string `json:"mode"`
35
+ WorkspacePath string `json:"workspacePath,omitempty"`
36
+ Quota Quota `json:"quota"`
37
+ IssuedAt time.Time `json:"issuedAt"`
38
+ ExpiresAt time.Time `json:"expiresAt"`
39
+ ReleasedAt *time.Time `json:"releasedAt,omitempty"`
40
+ }
41
+
42
+ type Usage struct {
43
+ DiskBytes int64 `json:"diskBytes"`
44
+ MemoryBytes int64 `json:"memoryBytes"`
45
+ CPUTimeNanos int64 `json:"cpuTimeNanos"`
46
+ Processes int `json:"processes"`
47
+ }
48
+
49
+ type Snapshot struct {
50
+ SchemaVersion string `json:"schemaVersion"`
51
+ LeaseID string `json:"leaseId"`
52
+ RunID string `json:"runId"`
53
+ State string `json:"state"`
54
+ ResourceUsage Usage `json:"resourceUsage"`
55
+ ArtifactRefs []string `json:"artifactRefs,omitempty"`
56
+ ObservedAt time.Time `json:"observedAt"`
57
+ }
58
+
59
+ // Request is sent only by the trusted Core connection. Owner, scope, quota and
60
+ // the original lease timestamps are server-derived, never user-selected.
61
+ // Execute is not retryable: a lost receipt must be treated as an unknown outcome.
62
+ type Request struct {
63
+ SchemaVersion string `json:"schemaVersion"`
64
+ Action string `json:"action"`
65
+ Lease Lease `json:"lease"`
66
+ Argv []string `json:"argv,omitempty"`
67
+ // TaskID selects a server-owned temporary directory inside the sandbox.
68
+ // It never changes the personal owner, lease or persistent mount.
69
+ TaskID string `json:"taskId,omitempty"`
70
+ // Core permits this only before the first successful provisioning receipt.
71
+ CreateData bool `json:"createData,omitempty"`
72
+ }
73
+
74
+ type Result struct {
75
+ SchemaVersion string `json:"schemaVersion"`
76
+ LeaseID string `json:"leaseId"`
77
+ RunID string `json:"runId"`
78
+ TaskID string `json:"taskId,omitempty"`
79
+ State string `json:"state"`
80
+ Snapshot *Snapshot `json:"snapshot,omitempty"`
81
+ Output *string `json:"output,omitempty"`
82
+ ExitCode *int `json:"exitCode,omitempty"`
83
+ ObservedAt time.Time `json:"observedAt"`
84
+ }
85
+
86
+ var scopePattern = regexp.MustCompile(`^sha256:[a-f0-9]{64}$`)
87
+ var canonicalUUIDPattern = regexp.MustCompile(`^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$`)
88
+
89
+ func (r *Request) UnmarshalJSON(data []byte) error {
90
+ if !utf8.Valid(data) {
91
+ return errors.New("environment request must be UTF-8")
92
+ }
93
+ type wireRequest Request
94
+ var decoded wireRequest
95
+ decoder := json.NewDecoder(bytes.NewReader(data))
96
+ decoder.DisallowUnknownFields()
97
+ if err := decoder.Decode(&decoded); err != nil {
98
+ return err
99
+ }
100
+ var properties map[string]json.RawMessage
101
+ if err := json.Unmarshal(data, &properties); err != nil {
102
+ return err
103
+ }
104
+ if _, present := properties["argv"]; present && decoded.Action != "execute" {
105
+ return errors.New("only execute accepts command arguments")
106
+ }
107
+ if _, present := properties["taskId"]; present && (decoded.Action != "execute" || !canonicalUUIDPattern.MatchString(decoded.TaskID)) {
108
+ return errors.New("only execute accepts a canonical server task identity")
109
+ }
110
+ *r = Request(decoded)
111
+ return r.Validate()
112
+ }
113
+
114
+ func (r Request) Validate() error {
115
+ l := r.Lease
116
+ if r.SchemaVersion != RequestVersion || l.SchemaVersion != LeaseVersion || !scopePattern.MatchString(l.ScopeHash) {
117
+ return errors.New("invalid environment protocol identity")
118
+ }
119
+ for _, id := range []string{l.ID, l.RunID, l.OwnerID} {
120
+ if strings.TrimSpace(id) == "" || utf8.RuneCountInString(id) > 128 || !utf8.ValidString(id) || strings.ContainsRune(id, '\x00') {
121
+ return errors.New("invalid environment lease identity")
122
+ }
123
+ }
124
+ if (l.Mode != "observe-only" && l.Mode != "restricted-write") || l.WorkspacePath != "" || l.ReleasedAt != nil || l.IssuedAt.IsZero() || !l.ExpiresAt.After(l.IssuedAt) || l.ExpiresAt.Sub(l.IssuedAt) > 24*time.Hour {
125
+ return errors.New("invalid original personal environment lease")
126
+ }
127
+ if err := l.Quota.ValidatePersonalDocker(); err != nil {
128
+ return err
129
+ }
130
+ return r.validateAction()
131
+ }
132
+
133
+ func (q Quota) ValidatePersonalDocker() error {
134
+ if q.DiskBytes < 16<<20 || q.DiskBytes > 1<<40 || q.MemoryBytes < 32<<20 || q.MemoryBytes > 16<<30 || q.CPUUnits < 1 || q.CPUUnits > 8000 || q.MaxProcesses < 4 || q.MaxProcesses > 256 || q.NetworkEgress {
135
+ return errors.New("unsupported personal environment quota")
136
+ }
137
+ return nil
138
+ }
139
+
140
+ func (r Request) validateAction() error {
141
+ if r.TaskID != "" && (r.Action != "execute" || !canonicalUUIDPattern.MatchString(r.TaskID)) {
142
+ return errors.New("invalid personal execution task identity")
143
+ }
144
+ if r.CreateData && r.Action != "acquire" {
145
+ return errors.New("only initial acquisition can create persistent data")
146
+ }
147
+ if r.Action != "execute" {
148
+ if (r.Action != "acquire" && r.Action != "inspect" && r.Action != "release" && r.Action != "delete_data" && r.Action != "export_data") || r.Argv != nil {
149
+ return errors.New("invalid environment action")
150
+ }
151
+ return nil
152
+ }
153
+ if len(r.Argv) == 0 || len(r.Argv) > 64 || !strings.HasPrefix(r.Argv[0], "/") {
154
+ return errors.New("bounded absolute sandbox command required")
155
+ }
156
+ for _, arg := range r.Argv {
157
+ if len(arg) > 4096 || !utf8.ValidString(arg) || strings.ContainsRune(arg, '\x00') {
158
+ return errors.New("invalid sandbox command argument")
159
+ }
160
+ }
161
+ return nil
162
+ }
163
+
164
+ // ValidateFor binds a receipt to the dispatched operation. Stopped or missing
165
+ // containers cannot claim fresh resource measurements or successful commands.
166
+ func (result Result) ValidateFor(request Request) error {
167
+ if result.ExitCode != nil && (request.Action != "execute" || *result.ExitCode < 0 || *result.ExitCode > 255) {
168
+ return errors.New("invalid command exit receipt")
169
+ }
170
+ if request.Action == "export_data" {
171
+ return errors.New("export requires its complete binary stream receipt")
172
+ }
173
+ if result.SchemaVersion != ResultVersion || result.LeaseID != request.Lease.ID || result.RunID != request.Lease.RunID || result.TaskID != request.TaskID || result.ObservedAt.IsZero() {
174
+ return errors.New("environment receipt identity mismatch")
175
+ }
176
+ active := result.State == "active" || result.State == "quota_exceeded"
177
+ if !active && result.State != "released" && result.State != "expired" && result.State != "missing" && result.State != "deleted" {
178
+ return errors.New("invalid environment receipt state")
179
+ }
180
+ if request.Action == "release" && (result.State != "released" && result.State != "missing") {
181
+ return errors.New("release has no stopped receipt")
182
+ }
183
+ if request.Action == "delete_data" && result.State != "deleted" {
184
+ return errors.New("data deletion has no confirmed receipt")
185
+ }
186
+ if (request.Action == "acquire" || request.Action == "execute") && !active {
187
+ return errors.New("operation has no active receipt")
188
+ }
189
+ if !active {
190
+ if result.Snapshot != nil || result.Output != nil {
191
+ return errors.New("inactive receipt contains invented observation")
192
+ }
193
+ return nil
194
+ }
195
+ if err := result.validateObservation(); err != nil {
196
+ return err
197
+ }
198
+ if (request.Action == "execute") != (result.Output != nil) || (result.Output != nil && len(*result.Output) > 64<<10) {
199
+ return errors.New("invalid command receipt")
200
+ }
201
+ return nil
202
+ }
203
+
204
+ func (result Result) validateObservation() error {
205
+ s := result.Snapshot
206
+ if s == nil || s.SchemaVersion != SnapshotVersion || s.LeaseID != result.LeaseID || s.RunID != result.RunID || s.State != result.State || !s.ObservedAt.Equal(result.ObservedAt) {
207
+ return errors.New("environment observation identity mismatch")
208
+ }
209
+ u := s.ResourceUsage
210
+ if u.DiskBytes < 0 || u.MemoryBytes < 0 || u.CPUTimeNanos < 0 || u.Processes < 0 || len(s.ArtifactRefs) > 128 {
211
+ return errors.New("invalid environment observation")
212
+ }
213
+ seen := make(map[string]bool, len(s.ArtifactRefs))
214
+ for _, ref := range s.ArtifactRefs {
215
+ if utf8.RuneCountInString(ref) > 512 || seen[ref] {
216
+ return errors.New("invalid environment artifact reference")
217
+ }
218
+ seen[ref] = true
219
+ }
220
+ return nil
221
+ }
@@ -0,0 +1,81 @@
1
+ package environment
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "testing"
8
+ "time"
9
+ )
10
+
11
+ func TestRequestFixturesMatchRuntimeBoundary(t *testing.T) {
12
+ for _, kind := range []string{"valid", "invalid"} {
13
+ files, err := filepath.Glob(filepath.Join("..", "..", "fixtures", "json-schema", "personal-environment-request", kind, "*.json"))
14
+ if err != nil || len(files) == 0 {
15
+ t.Fatalf("missing fixtures: %v", err)
16
+ }
17
+ for _, file := range files {
18
+ t.Run(kind+"/"+filepath.Base(file), func(t *testing.T) {
19
+ data, err := os.ReadFile(file)
20
+ if err != nil {
21
+ t.Fatal(err)
22
+ }
23
+ var request Request
24
+ err = json.Unmarshal(data, &request)
25
+ if (err == nil) != (kind == "valid") {
26
+ t.Fatalf("fixture acceptance=%t; want %s", err == nil, kind)
27
+ }
28
+ })
29
+ }
30
+ }
31
+ }
32
+
33
+ func TestRequestRejectsUnboundedOriginalLeaseAndNullCommand(t *testing.T) {
34
+ data, err := os.ReadFile(filepath.Join("..", "..", "fixtures", "json-schema", "personal-environment-request", "valid", "acquire.json"))
35
+ if err != nil {
36
+ t.Fatal(err)
37
+ }
38
+ var request Request
39
+ if err := json.Unmarshal(data, &request); err != nil {
40
+ t.Fatal(err)
41
+ }
42
+ request.Lease.ExpiresAt = request.Lease.IssuedAt.Add(25 * time.Hour)
43
+ if request.Validate() == nil {
44
+ t.Fatal("unbounded lease accepted")
45
+ }
46
+ var properties map[string]any
47
+ if err := json.Unmarshal(data, &properties); err != nil {
48
+ t.Fatal(err)
49
+ }
50
+ properties["argv"] = nil
51
+ nullCommand, err := json.Marshal(properties)
52
+ if err != nil {
53
+ t.Fatal(err)
54
+ }
55
+ if json.Unmarshal(nullCommand, &request) == nil {
56
+ t.Fatal("null command accepted by non-execute action")
57
+ }
58
+ }
59
+
60
+ func TestCommandReceiptPinsTheExactTemporaryTask(t *testing.T) {
61
+ now := time.Now().UTC()
62
+ request := Request{SchemaVersion: RequestVersion, Action: "execute", TaskID: "00000000-0000-4000-8000-000000000048", Argv: []string{"/bin/true"}, Lease: Lease{ID: "lease", RunID: "run"}}
63
+ text := "fixture"
64
+ result := Result{SchemaVersion: ResultVersion, LeaseID: "lease", RunID: "run", State: "active", Output: &text, ObservedAt: now, Snapshot: &Snapshot{SchemaVersion: SnapshotVersion, LeaseID: "lease", RunID: "run", State: "active", ObservedAt: now}}
65
+ for _, task := range []string{"", "00000000-0000-4000-8000-000000000049", request.TaskID} {
66
+ result.TaskID = task
67
+ if err := result.ValidateFor(request); (err == nil) != (task == request.TaskID) {
68
+ t.Fatal("command receipt changed or omitted its task identity")
69
+ }
70
+ }
71
+ }
72
+
73
+ func TestDataDeletionRequiresAConfirmedReceipt(t *testing.T) {
74
+ request := Request{Action: "delete_data", Lease: Lease{ID: "lease", RunID: "run"}}
75
+ for _, state := range []string{"missing", "released", "active", "deleted"} {
76
+ result := Result{SchemaVersion: ResultVersion, LeaseID: "lease", RunID: "run", State: state, ObservedAt: time.Now().UTC()}
77
+ if err := result.ValidateFor(request); (err == nil) != (state == "deleted") {
78
+ t.Fatalf("unconfirmed %s data deletion: %v", state, err)
79
+ }
80
+ }
81
+ }
@@ -17,9 +17,9 @@
17
17
  "type": "object", "additionalProperties": false,
18
18
  "required": ["diskBytes", "memoryBytes", "cpuUnits", "maxProcesses", "networkEgress"],
19
19
  "properties": {
20
- "diskBytes": { "type": "integer", "minimum": 1 },
20
+ "diskBytes": { "type": "integer", "minimum": 1, "description": "Maximum charged persistent storage bytes. The Docker personal backend uses a verified Btrfs subvolume quota; temporary task files use separately bounded memory-backed mounts and count against memoryBytes. Unsupported hard enforcement must fail acquisition." },
21
21
  "memoryBytes": { "type": "integer", "minimum": 1 },
22
- "cpuUnits": { "type": "integer", "minimum": 1 },
22
+ "cpuUnits": { "type": "integer", "minimum": 1, "description": "CPU millicores: 1000 is one CPU. Docker personal leases must hard-enforce this ceiling." },
23
23
  "maxProcesses": { "type": "integer", "minimum": 1 },
24
24
  "networkEgress": { "type": "boolean" }
25
25
  }
@@ -11,7 +11,7 @@
11
11
  "version": { "type": "string", "minLength": 1 },
12
12
  "backend": { "enum": ["local-process", "container", "kubernetes"] },
13
13
  "isolationMode": { "enum": ["observe-only", "restricted-write", "disposable-write"] },
14
- "quota": { "type": "object", "required": ["cpuUnits", "memoryBytes", "diskBytes", "maxProcesses"], "properties": { "cpuUnits": { "type": "integer", "minimum": 1 }, "memoryBytes": { "type": "integer", "minimum": 1 }, "diskBytes": { "type": "integer", "minimum": 1 }, "maxProcesses": { "type": "integer", "minimum": 1 } }, "additionalProperties": false },
14
+ "quota": { "type": "object", "required": ["cpuUnits", "memoryBytes", "diskBytes", "maxProcesses"], "properties": { "cpuUnits": { "type": "integer", "minimum": 1, "description": "CPU millicores; 1000 is one CPU." }, "memoryBytes": { "type": "integer", "minimum": 1 }, "diskBytes": { "type": "integer", "minimum": 1, "description": "Maximum charged persistent storage bytes; memory-backed temporary mounts count against memoryBytes." }, "maxProcesses": { "type": "integer", "minimum": 1 } }, "additionalProperties": false },
15
15
  "networkEgress": { "type": "boolean" },
16
16
  "ttlSeconds": { "type": "integer", "minimum": 1, "maximum": 86400 },
17
17
  "enabled": { "type": "boolean" }
@@ -0,0 +1,77 @@
1
+ package guest
2
+
3
+ import (
4
+ "encoding/base64"
5
+ "errors"
6
+ "net/url"
7
+ "strings"
8
+ )
9
+
10
+ // Only the private browser controller sends these frames over current mTLS.
11
+ type BrowserNetworkRequest struct {
12
+ URL string `json:"url"`
13
+ Method string `json:"method"`
14
+ Headers map[string]string `json:"headers,omitempty"`
15
+ BodyBase64 string `json:"bodyBase64,omitempty"`
16
+ }
17
+ type BrowserNetworkHeader struct {
18
+ Name string `json:"name"`
19
+ Value string `json:"value"`
20
+ }
21
+ type BrowserNetworkResponse struct {
22
+ Status int `json:"status"`
23
+ Headers []BrowserNetworkHeader `json:"headers"`
24
+ BodyBase64 string `json:"bodyBase64"`
25
+ }
26
+
27
+ func (v BrowserNetworkResponse) Validate() error {
28
+ if v.Status < 100 || v.Status > 599 || len(v.Headers) > 128 {
29
+ return errors.New("invalid browser network receipt")
30
+ }
31
+ data, err := base64.StdEncoding.DecodeString(v.BodyBase64)
32
+ if err != nil || len(data) > 1<<20 {
33
+ return errors.New("browser response exceeds its bound")
34
+ }
35
+ for _, h := range v.Headers {
36
+ if h.Name == "" || len(h.Name) > 128 || len(h.Value) > 8192 || strings.ContainsAny(h.Name+h.Value, "\x00\r\n") {
37
+ return errors.New("invalid browser response header")
38
+ }
39
+ }
40
+ return nil
41
+ }
42
+
43
+ func (v BrowserNetworkRequest) Validate() error {
44
+ u, err := url.Parse(v.URL)
45
+ if err != nil || !BrowserURL(v.URL) || u.Scheme != "https" || u.Fragment != "" || u.Opaque != "" || len(v.Headers) > 64 {
46
+ return errors.New("invalid browser network request")
47
+ }
48
+ switch v.Method {
49
+ case "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS":
50
+ default:
51
+ return errors.New("unsupported browser network method")
52
+ }
53
+ body, err := base64.StdEncoding.DecodeString(v.BodyBase64)
54
+ if err != nil || len(body) > 64<<10 || (v.Method == "GET" || v.Method == "HEAD") && len(body) > 0 {
55
+ return errors.New("browser network body exceeds its bound")
56
+ }
57
+ size := 0
58
+ for name, value := range v.Headers {
59
+ size += len(name) + len(value)
60
+ if name == "" || strings.ContainsAny(name+value, "\x00\r\n") || len(name) > 128 || len(value) > 8192 {
61
+ return errors.New("invalid browser network header")
62
+ }
63
+ }
64
+ if size > 16<<10 {
65
+ return errors.New("browser network headers exceed their bound")
66
+ }
67
+ return nil
68
+ }
69
+ func (v *BrowserNetworkRequest) UnmarshalJSON(raw []byte) error {
70
+ type wire BrowserNetworkRequest
71
+ var decoded wire
72
+ if err := decodeStrictBounded(raw, &decoded, 128<<10, "url", "method", "headers", "bodyBase64"); err != nil {
73
+ return err
74
+ }
75
+ *v = BrowserNetworkRequest(decoded)
76
+ return v.Validate()
77
+ }
@@ -0,0 +1,62 @@
1
+ package guest
2
+
3
+ import (
4
+ "errors"
5
+ "net/url"
6
+ "regexp"
7
+ "strings"
8
+ "time"
9
+ )
10
+
11
+ const DataPlanVersion = "opensoha.dev/personal-guest-data-plan/v1"
12
+
13
+ // Core reserves operationId before fixed privileged dispatch. Liveness alone
14
+ // cannot authorize this plan; GET /bindings/{id}/data-operations/{operationId}
15
+ // returns it only for that current, running owner-authorized operation.
16
+ type DataPlan struct {
17
+ SchemaVersion string `json:"schemaVersion"`
18
+ OperationID string `json:"operationId"`
19
+ Binding Binding `json:"binding"`
20
+ DiskQuotaBytes int64 `json:"diskQuotaBytes"`
21
+ }
22
+
23
+ func (p DataPlan) Validate(now time.Time) error {
24
+ if p.SchemaVersion != DataPlanVersion || !uuidPattern.MatchString(p.OperationID) || p.Binding.Validate() != nil || p.Binding.Provider != "pve" || now.Before(p.Binding.IssuedAt) || !now.Before(p.Binding.ExpiresAt) || p.DiskQuotaBytes < 16<<20 || p.DiskQuotaBytes%1024 != 0 || p.DiskQuotaBytes >= p.Binding.DataDiskBytes {
25
+ return errors.New("invalid or expired personal data operation")
26
+ }
27
+ return nil
28
+ }
29
+
30
+ type DataResult struct {
31
+ DataRef string `json:"dataRef"`
32
+ FilesystemID string `json:"filesystemId"`
33
+ DiskQuota int64 `json:"diskQuotaBytes"`
34
+ DiskUsed int64 `json:"diskUsedBytes"`
35
+ Reconciled bool `json:"reconciled"`
36
+ }
37
+
38
+ // Provisioning contains public identity material and a bootstrap-only challenge.
39
+ // It is encrypted in the durable task and supplied only to the fixed QGA installer.
40
+ // The private key is generated in the VM and never transported through Core.
41
+ type Provisioning struct {
42
+ Plan DataPlan `json:"plan"`
43
+ Endpoint string `json:"endpoint"`
44
+ EnrollmentID string `json:"enrollmentId"`
45
+ Challenge string `json:"challenge"`
46
+ CertificatePEM string `json:"certificatePEM"`
47
+ ClientCAPEM string `json:"clientCAPEM"`
48
+ ArtifactSHA256 string `json:"artifactSHA256"`
49
+ CPUQuotaMilli int64 `json:"cpuQuotaMilli"`
50
+ MemoryBytes int64 `json:"memoryBytes"`
51
+ PidsLimit int64 `json:"pidsLimit"`
52
+ Network bool `json:"network"`
53
+ }
54
+
55
+ func (p Provisioning) Validate(now time.Time) error {
56
+ u, err := url.Parse(p.Endpoint)
57
+ _, tokenErr := EnrollmentTokenHash("Bearer " + p.Challenge)
58
+ if p.Plan.Validate(now) != nil || err != nil || u.Scheme != "https" || u.Host == "" || u.Path != "" || u.User != nil || u.RawQuery != "" || u.Fragment != "" || len(p.Endpoint) > 512 || strings.TrimSpace(p.Endpoint) != p.Endpoint || tokenErr != nil || (Enrollment{SchemaVersion: EnrollmentVersion, EnrollmentID: p.EnrollmentID}).Validate() != nil || !regexp.MustCompile(`^[a-f0-9]{64}$`).MatchString(p.ArtifactSHA256) || len(p.CertificatePEM) < 1 || len(p.CertificatePEM) > 4096 || len(p.ClientCAPEM) < 1 || len(p.ClientCAPEM) > 8192 || p.CPUQuotaMilli < 1 || p.CPUQuotaMilli > 8000 || p.MemoryBytes < 32<<20 || p.MemoryBytes > 16<<30 || p.PidsLimit < 4 || p.PidsLimit > 256 || p.Network {
59
+ return errors.New("invalid personal guest provisioning bundle")
60
+ }
61
+ return nil
62
+ }