@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,315 @@
1
+ package guest
2
+
3
+ import (
4
+ "bytes"
5
+ "crypto/sha256"
6
+ "encoding/base64"
7
+ "encoding/json"
8
+ "errors"
9
+ "fmt"
10
+ "image/jpeg"
11
+ "net/url"
12
+ "regexp"
13
+ "strings"
14
+ "time"
15
+ "unicode/utf8"
16
+
17
+ env "github.com/opensoha/soha-contracts/agent/environment"
18
+ )
19
+
20
+ const RuntimeOperationVersion = "opensoha.dev/personal-guest-runtime/v1"
21
+ const RuntimeResultVersion = "opensoha.dev/personal-guest-runtime-result/v1"
22
+ const ToolsPlanVersion = "opensoha.dev/personal-guest-tools/v1"
23
+ const RuntimeFileLimit = 44 << 10
24
+
25
+ var artifactDigestPattern = regexp.MustCompile(`^[a-f0-9]{64}$`)
26
+ var runtimeImagePattern = regexp.MustCompile(`^docker\.io/library/debian@sha256:[a-f0-9]{64}$`)
27
+ var debianVersionPattern = regexp.MustCompile(`^[0-9][a-zA-Z0-9.+:~_-]{0,127}$`)
28
+
29
+ // Runtime operations are frozen by Core in an encrypted durable task. The SSH
30
+ // launcher carries only the operation identity; mTLS retrieves the exact grant.
31
+ type RuntimeOperation struct {
32
+ SchemaVersion string `json:"schemaVersion"`
33
+ OperationID string `json:"operationId"`
34
+ Binding Binding `json:"binding"`
35
+ Kind string `json:"kind"`
36
+ Request env.Request `json:"request"`
37
+ Deadline time.Time `json:"deadline"`
38
+ File *RuntimeFileInput `json:"file,omitempty"`
39
+ Browser *BrowserAction `json:"browser,omitempty"`
40
+ DockerImage string `json:"dockerImage,omitempty"`
41
+ Source string `json:"source,omitempty"`
42
+ RequestHash string `json:"requestHash,omitempty"`
43
+ ArtifactSHA256 string `json:"artifactSHA256"`
44
+ }
45
+
46
+ type RuntimeFileInput struct {
47
+ Path string `json:"path"`
48
+ ContentBase64 string `json:"contentBase64,omitempty"`
49
+ ExpectedSHA256 string `json:"expectedSHA256,omitempty"`
50
+ }
51
+
52
+ type RuntimeFileResult struct {
53
+ Path string `json:"path"`
54
+ ContentBase64 string `json:"contentBase64,omitempty"`
55
+ SHA256 string `json:"sha256"`
56
+ Size int64 `json:"size"`
57
+ }
58
+
59
+ type BrowserAction struct {
60
+ Action string `json:"action"`
61
+ ControlID string `json:"controlId"`
62
+ Generation int64 `json:"generation"`
63
+ Owner string `json:"owner"`
64
+ TaskID string `json:"taskId,omitempty"`
65
+ URL string `json:"url,omitempty"`
66
+ Selector string `json:"selector,omitempty"`
67
+ Text string `json:"text,omitempty"`
68
+ X *int `json:"x,omitempty"`
69
+ Y *int `json:"y,omitempty"`
70
+ }
71
+
72
+ type BrowserFrame struct {
73
+ ControlID string `json:"controlId"`
74
+ Generation int64 `json:"generation"`
75
+ InstanceID string `json:"instanceId"`
76
+ URL string `json:"url,omitempty"`
77
+ Title string `json:"title,omitempty"`
78
+ Text string `json:"text,omitempty"`
79
+ JPEGBase64 string `json:"jpegBase64,omitempty"`
80
+ Width int `json:"width"`
81
+ Height int `json:"height"`
82
+ }
83
+
84
+ type RuntimeProof struct {
85
+ InstanceID string `json:"instanceId"`
86
+ ArtifactSHA256 string `json:"artifactSHA256"`
87
+ FilesystemID string `json:"filesystemId"`
88
+ Command bool `json:"command"`
89
+ FileRead bool `json:"fileRead"`
90
+ FileWrite bool `json:"fileWrite"`
91
+ Browser bool `json:"browser"`
92
+ Docker bool `json:"docker"`
93
+ CredentialsHidden bool `json:"credentialsHidden"`
94
+ NamespacesIsolated bool `json:"namespacesIsolated"`
95
+ DockerImage string `json:"dockerImage"`
96
+ }
97
+
98
+ type RuntimeResult struct {
99
+ SchemaVersion string `json:"schemaVersion"`
100
+ OperationID string `json:"operationId"`
101
+ BindingID string `json:"bindingId"`
102
+ Environment env.Result `json:"environment"`
103
+ File *RuntimeFileResult `json:"file,omitempty"`
104
+ Browser *BrowserFrame `json:"browser,omitempty"`
105
+ Proof *RuntimeProof `json:"proof,omitempty"`
106
+ }
107
+
108
+ // Tools installation cannot initialize data or accept commands/paths. Existing
109
+ // completed data and current owner authority are required before any host write.
110
+ type ToolsPlan struct {
111
+ SchemaVersion string `json:"schemaVersion"`
112
+ OperationID string `json:"operationId"`
113
+ Binding Binding `json:"binding"`
114
+ ArtifactSHA256 string `json:"artifactSHA256"`
115
+ DiskQuotaBytes int64 `json:"diskQuotaBytes"`
116
+ CPUQuotaMilli int64 `json:"cpuQuotaMilli"`
117
+ MemoryBytes int64 `json:"memoryBytes"`
118
+ PidsLimit int64 `json:"pidsLimit"`
119
+ DockerImage string `json:"dockerImage"`
120
+ BubblewrapVersion string `json:"bubblewrapVersion"`
121
+ DockerVersion string `json:"dockerVersion"`
122
+ ChromiumVersion string `json:"chromiumVersion"`
123
+ }
124
+
125
+ type ToolsResult struct {
126
+ OperationID string `json:"operationId"`
127
+ DataRef string `json:"dataRef"`
128
+ ArtifactSHA256 string `json:"artifactSHA256"`
129
+ }
130
+
131
+ func (p ToolsPlan) Validate(now time.Time) error {
132
+ data := DataPlan{SchemaVersion: DataPlanVersion, OperationID: p.OperationID, Binding: p.Binding, DiskQuotaBytes: p.DiskQuotaBytes}
133
+ quota := env.Quota{DiskBytes: p.DiskQuotaBytes, CPUUnits: p.CPUQuotaMilli, MemoryBytes: p.MemoryBytes, MaxProcesses: int(p.PidsLimit)}
134
+ if p.SchemaVersion != ToolsPlanVersion || data.Validate(now) != nil || quota.ValidatePersonalDocker() != nil || !artifactDigestPattern.MatchString(p.ArtifactSHA256) || !runtimeImagePattern.MatchString(p.DockerImage) {
135
+ return errors.New("invalid personal guest tools plan")
136
+ }
137
+ for _, v := range []string{p.BubblewrapVersion, p.DockerVersion, p.ChromiumVersion} {
138
+ if !debianVersionPattern.MatchString(v) {
139
+ return errors.New("invalid frozen Debian tools version")
140
+ }
141
+ }
142
+ return nil
143
+ }
144
+
145
+ func RuntimeFilePath(path string) bool {
146
+ if path == "" || len(path) > 1024 || !utf8.ValidString(path) || strings.ContainsAny(path, "\x00\r\n\\") {
147
+ return false
148
+ }
149
+ for _, part := range strings.Split(path, "/") {
150
+ if part == "" || part == "." || part == ".." {
151
+ return false
152
+ }
153
+ }
154
+ return true
155
+ }
156
+
157
+ func (op RuntimeOperation) Validate(now time.Time) error {
158
+ if !artifactDigestPattern.MatchString(op.ArtifactSHA256) {
159
+ return errors.New("invalid frozen runtime artifact")
160
+ }
161
+ if op.Source != "" && op.Source != "native" && op.Source != "user" || op.Source == "user" && !artifactDigestPattern.MatchString(op.RequestHash) || op.Source != "user" && op.RequestHash != "" {
162
+ return errors.New("invalid frozen runtime origin")
163
+ }
164
+ if op.SchemaVersion != RuntimeOperationVersion || !uuidPattern.MatchString(op.OperationID) || op.Binding.Validate() != nil || now.Before(op.Binding.IssuedAt) || !now.Before(op.Binding.ExpiresAt) || op.Request.Validate() != nil || op.Request.Lease.OwnerID != op.Binding.AssistantID || op.Request.CreateData || !now.Before(op.Deadline) || op.Deadline.After(op.Request.Lease.ExpiresAt) || op.Deadline.After(op.Binding.ExpiresAt) || op.Deadline.Sub(now) > time.Minute {
165
+ return errors.New("invalid or expired personal runtime operation")
166
+ }
167
+ if op.Request.Action == "delete_data" || op.Request.Action == "export_data" {
168
+ return errors.New("runtime operation cannot dispose or export data")
169
+ }
170
+ return op.validateKind()
171
+ }
172
+
173
+ func (op RuntimeOperation) validateKind() error {
174
+ switch op.Kind {
175
+ case "environment":
176
+ if op.File != nil || op.Browser != nil || op.DockerImage != "" || op.Request.Action == "execute" && op.Request.Lease.Mode != "restricted-write" {
177
+ return errors.New("invalid environment runtime payload")
178
+ }
179
+ case "file_read", "file_write":
180
+ return op.validateFile()
181
+ case "docker":
182
+ if op.File != nil || op.Browser != nil || !runtimeImagePattern.MatchString(op.DockerImage) || op.Request.Action != "execute" || op.Request.Lease.Mode != "restricted-write" {
183
+ return errors.New("invalid Docker runtime payload")
184
+ }
185
+ case "browser":
186
+ if op.Browser == nil || op.File != nil || op.DockerImage != "" || op.Request.Action != "inspect" {
187
+ return errors.New("invalid browser runtime payload")
188
+ }
189
+ if (op.Browser.Action == "click" || op.Browser.Action == "type" || op.Browser.Action == "pointer") && op.Request.Lease.Mode != "restricted-write" {
190
+ return errors.New("browser write requires a writable lease")
191
+ }
192
+ return op.Browser.Validate()
193
+ default:
194
+ return errors.New("unsupported personal runtime kind")
195
+ }
196
+ return nil
197
+ }
198
+
199
+ func (op RuntimeOperation) validateFile() error {
200
+ if op.File == nil || op.Browser != nil || op.DockerImage != "" || op.Request.Action != "inspect" || !RuntimeFilePath(op.File.Path) {
201
+ return errors.New("invalid personal runtime file")
202
+ }
203
+ if op.File.ExpectedSHA256 != "" && !artifactDigestPattern.MatchString(op.File.ExpectedSHA256) {
204
+ return errors.New("invalid expected file identity")
205
+ }
206
+ if op.Kind == "file_read" {
207
+ if op.File.ContentBase64 != "" || op.File.ExpectedSHA256 != "" {
208
+ return errors.New("read cannot contain write material")
209
+ }
210
+ return nil
211
+ }
212
+ data, err := base64.StdEncoding.DecodeString(op.File.ContentBase64)
213
+ if err != nil || len(data) > RuntimeFileLimit || !utf8.Valid(data) || op.Request.Lease.Mode != "restricted-write" {
214
+ return errors.New("invalid bounded personal file write")
215
+ }
216
+ return nil
217
+ }
218
+
219
+ func BrowserURL(value string) bool {
220
+ u, err := url.Parse(value)
221
+ if err != nil || len(value) > 4096 || u.User != nil || strings.ContainsAny(value, "\x00\r\n\\") {
222
+ return false
223
+ }
224
+ if u.Scheme == "file" {
225
+ return u.Host == "" && u.RawQuery == "" && strings.HasPrefix(u.Path, "/data/") && RuntimeFilePath(strings.TrimPrefix(u.Path, "/data/"))
226
+ }
227
+ return u.Scheme == "https" && u.Hostname() != "" && (u.Port() == "" || u.Port() == "443")
228
+ }
229
+
230
+ func (b BrowserAction) Validate() error {
231
+ if !uuidPattern.MatchString(b.ControlID) || b.Generation < 1 || b.Generation > MaxCounter || (b.Owner != "user" && b.Owner != "agent") || (b.TaskID != "" && !uuidPattern.MatchString(b.TaskID)) || (b.Owner == "agent" && b.TaskID == "") || len(b.Selector) > 2048 || len(b.Text) > 4096 || !utf8.ValidString(b.Selector+b.Text) || strings.ContainsRune(b.Selector+b.Text, 0) {
232
+ return errors.New("invalid browser control identity or input")
233
+ }
234
+ switch b.Action {
235
+ case "navigate":
236
+ if !BrowserURL(b.URL) || b.Selector != "" || b.Text != "" || b.X != nil || b.Y != nil {
237
+ return errors.New("bounded browser target required")
238
+ }
239
+ case "click", "type":
240
+ if b.URL != "" || b.Selector == "" || b.X != nil || b.Y != nil || b.Action == "click" && b.Text != "" {
241
+ return errors.New("invalid selected browser action")
242
+ }
243
+ case "pointer":
244
+ if b.Owner != "user" || b.URL != "" || b.Selector != "" || b.Text != "" || b.X == nil || b.Y == nil || *b.X < 0 || *b.X >= 800 || *b.Y < 0 || *b.Y >= 600 {
245
+ return errors.New("invalid human browser pointer")
246
+ }
247
+ case "snapshot", "acquire", "takeover", "return", "release":
248
+ if b.URL != "" || b.Selector != "" || b.Text != "" || b.X != nil || b.Y != nil {
249
+ return errors.New("control action cannot carry a site operation")
250
+ }
251
+ default:
252
+ return errors.New("unsupported browser action")
253
+ }
254
+ return nil
255
+ }
256
+
257
+ func (op RuntimeOperation) Digest() (string, error) {
258
+ raw, err := json.Marshal(op)
259
+ if err != nil || len(raw) > 320<<10 {
260
+ return "", errors.New("personal runtime frame exceeds its bound")
261
+ }
262
+ return fmt.Sprintf("sha256:%x", sha256.Sum256(raw)), nil
263
+ }
264
+
265
+ func (r RuntimeResult) ValidateFor(op RuntimeOperation) error {
266
+ if r.SchemaVersion != RuntimeResultVersion || r.OperationID != op.OperationID || r.BindingID != op.Binding.ID || r.Environment.ValidateFor(op.Request) != nil {
267
+ return errors.New("personal runtime receipt identity mismatch")
268
+ }
269
+ if r.Environment.ObservedAt.Before(op.Request.Lease.IssuedAt) || r.Environment.ObservedAt.After(op.Deadline.Add(5*time.Second)) {
270
+ return errors.New("runtime receipt exceeds original authority window")
271
+ }
272
+ if op.Request.Action != "release" && r.Environment.State != "active" && r.Environment.State != "quota_exceeded" {
273
+ return errors.New("runtime operation has no active observation")
274
+ }
275
+ if (op.Kind == "file_read" || op.Kind == "file_write") != (r.File != nil) || (op.Kind == "browser") != (r.Browser != nil) {
276
+ return errors.New("personal runtime receipt kind mismatch")
277
+ }
278
+ if r.File != nil {
279
+ f := r.File
280
+ if f.Path != op.File.Path || !artifactDigestPattern.MatchString(f.SHA256) || f.Size < 0 || f.Size > RuntimeFileLimit {
281
+ return errors.New("invalid personal file receipt")
282
+ }
283
+ if op.Kind == "file_read" {
284
+ data, err := base64.StdEncoding.DecodeString(f.ContentBase64)
285
+ if err != nil || int64(len(data)) != f.Size || !utf8.Valid(data) || fmt.Sprintf("%x", sha256.Sum256(data)) != f.SHA256 {
286
+ return errors.New("personal file content identity mismatch")
287
+ }
288
+ } else if f.ContentBase64 != "" {
289
+ return errors.New("write receipt must not echo content")
290
+ } else {
291
+ data, err := base64.StdEncoding.DecodeString(op.File.ContentBase64)
292
+ if err != nil || f.Size != int64(len(data)) || f.SHA256 != fmt.Sprintf("%x", sha256.Sum256(data)) {
293
+ return errors.New("write receipt differs from frozen content")
294
+ }
295
+ }
296
+ }
297
+ if r.Browser != nil {
298
+ b := r.Browser
299
+ if b.ControlID != op.Browser.ControlID || b.Generation != op.Browser.Generation || !uuidPattern.MatchString(b.InstanceID) || len(b.JPEGBase64) > 48<<10 || len(b.Text) > 4096 || len(b.Title) > 512 || len(b.URL) > 4096 || b.Width != 800 || b.Height != 600 {
300
+ return errors.New("invalid bounded browser receipt")
301
+ }
302
+ image, err := base64.StdEncoding.DecodeString(b.JPEGBase64)
303
+ if err != nil {
304
+ return errors.New("invalid browser pixels")
305
+ }
306
+ size, err := jpeg.DecodeConfig(bytes.NewReader(image))
307
+ if err != nil || size.Width != 800 || size.Height != 600 || !utf8.ValidString(b.URL+b.Title+b.Text) || b.URL != "about:blank" && !BrowserURL(b.URL) {
308
+ return errors.New("invalid browser frame or target")
309
+ }
310
+ }
311
+ if r.Proof != nil && (op.Kind != "environment" || op.Request.Action != "inspect" || !uuidPattern.MatchString(r.Proof.InstanceID) || r.Proof.ArtifactSHA256 != op.ArtifactSHA256 || r.Proof.FilesystemID != op.Binding.DataRef || !runtimeImagePattern.MatchString(r.Proof.DockerImage)) {
312
+ return errors.New("invalid personal runtime capability proof")
313
+ }
314
+ return nil
315
+ }
@@ -0,0 +1,75 @@
1
+ package guest
2
+
3
+ func (v *RuntimeFileResult) UnmarshalJSON(raw []byte) error {
4
+ type wire RuntimeFileResult
5
+ var d wire
6
+ if err := decodeStrictBounded(raw, &d, 64<<10, "path", "contentBase64", "sha256", "size"); err != nil {
7
+ return err
8
+ }
9
+ *v = RuntimeFileResult(d)
10
+ return nil
11
+ }
12
+ func (v *BrowserFrame) UnmarshalJSON(raw []byte) error {
13
+ type wire BrowserFrame
14
+ var d wire
15
+ if err := decodeStrictBounded(raw, &d, 80<<10, "controlId", "generation", "instanceId", "url", "title", "text", "jpegBase64", "width", "height"); err != nil {
16
+ return err
17
+ }
18
+ *v = BrowserFrame(d)
19
+ return nil
20
+ }
21
+ func (v *RuntimeProof) UnmarshalJSON(raw []byte) error {
22
+ type wire RuntimeProof
23
+ var d wire
24
+ if err := decodeStrictBounded(raw, &d, 4096, "instanceId", "artifactSHA256", "filesystemId", "command", "fileRead", "fileWrite", "browser", "docker", "credentialsHidden", "namespacesIsolated", "dockerImage"); err != nil {
25
+ return err
26
+ }
27
+ *v = RuntimeProof(d)
28
+ return nil
29
+ }
30
+
31
+ func (v *RuntimeOperation) UnmarshalJSON(raw []byte) error {
32
+ type wire RuntimeOperation
33
+ var decoded wire
34
+ if err := decodeStrictBounded(raw, &decoded, 320<<10, "schemaVersion", "operationId", "binding", "kind", "request", "deadline", "file", "browser", "dockerImage", "source", "requestHash", "artifactSHA256"); err != nil {
35
+ return err
36
+ }
37
+ *v = RuntimeOperation(decoded)
38
+ return nil
39
+ }
40
+ func (v *RuntimeFileInput) UnmarshalJSON(raw []byte) error {
41
+ type wire RuntimeFileInput
42
+ var decoded wire
43
+ if err := decodeStrictBounded(raw, &decoded, 64<<10, "path", "contentBase64", "expectedSHA256"); err != nil {
44
+ return err
45
+ }
46
+ *v = RuntimeFileInput(decoded)
47
+ return nil
48
+ }
49
+ func (v *BrowserAction) UnmarshalJSON(raw []byte) error {
50
+ type wire BrowserAction
51
+ var decoded wire
52
+ if err := decodeStrictBounded(raw, &decoded, 32<<10, "action", "controlId", "generation", "owner", "taskId", "url", "selector", "text", "x", "y"); err != nil {
53
+ return err
54
+ }
55
+ *v = BrowserAction(decoded)
56
+ return nil
57
+ }
58
+ func (v *RuntimeResult) UnmarshalJSON(raw []byte) error {
59
+ type wire RuntimeResult
60
+ var decoded wire
61
+ if err := decodeStrictBounded(raw, &decoded, 320<<10, "schemaVersion", "operationId", "bindingId", "environment", "file", "browser", "proof"); err != nil {
62
+ return err
63
+ }
64
+ *v = RuntimeResult(decoded)
65
+ return nil
66
+ }
67
+ func (v *ToolsPlan) UnmarshalJSON(raw []byte) error {
68
+ type wire ToolsPlan
69
+ var decoded wire
70
+ if err := decodeStrictBounded(raw, &decoded, 8192, "schemaVersion", "operationId", "binding", "artifactSHA256", "diskQuotaBytes", "cpuQuotaMilli", "memoryBytes", "pidsLimit", "dockerImage", "bubblewrapVersion", "dockerVersion", "chromiumVersion"); err != nil {
71
+ return err
72
+ }
73
+ *v = ToolsPlan(decoded)
74
+ return nil
75
+ }
@@ -0,0 +1,110 @@
1
+ package guest
2
+
3
+ import (
4
+ "encoding/base64"
5
+ "encoding/json"
6
+ "strings"
7
+ "testing"
8
+ "time"
9
+
10
+ env "github.com/opensoha/soha-contracts/agent/environment"
11
+ )
12
+
13
+ func runtimeFixture(t *testing.T) (RuntimeOperation, time.Time) {
14
+ t.Helper()
15
+ binding := guestFixture[Binding](t, "binding")
16
+ now := binding.IssuedAt.Add(time.Second)
17
+ op := RuntimeOperation{SchemaVersion: RuntimeOperationVersion, OperationID: binding.ID, Binding: binding, Kind: "environment", Deadline: now.Add(30 * time.Second), Request: env.Request{SchemaVersion: env.RequestVersion, Action: "inspect", Lease: env.Lease{SchemaVersion: env.LeaseVersion, ID: binding.ID, RunID: binding.ID, OwnerID: binding.AssistantID, ScopeHash: "sha256:" + strings.Repeat("a", 64), Mode: "restricted-write", IssuedAt: binding.IssuedAt, ExpiresAt: binding.ExpiresAt, Quota: env.Quota{DiskBytes: 16 << 20, MemoryBytes: 2 << 30, CPUUnits: 1000, MaxProcesses: 64}}}}
18
+ op.ArtifactSHA256 = strings.Repeat("a", 64)
19
+ return op, now
20
+ }
21
+
22
+ func TestRuntimeJSONRejectsAmbiguity(t *testing.T) {
23
+ for _, raw := range []string{`{"path":"a","path":"b"}`, `{"Path":"a"}`, `{"path":"a","unknown":true}`} {
24
+ var value RuntimeFileInput
25
+ if json.Unmarshal([]byte(raw), &value) == nil {
26
+ t.Fatal("ambiguous runtime JSON accepted", raw)
27
+ }
28
+ }
29
+ for _, raw := range []string{`{"url":"https://example.com/","method":"GET","method":"POST"}`, `{"url":"https://example.com/","method":"CONNECT"}`, `{"url":"file:///etc/shadow","method":"GET"}`} {
30
+ var value BrowserNetworkRequest
31
+ if json.Unmarshal([]byte(raw), &value) == nil {
32
+ t.Fatal("invalid network frame accepted", raw)
33
+ }
34
+ }
35
+ }
36
+
37
+ func TestRuntimeAuthorityAndWriteBounds(t *testing.T) {
38
+ op, now := runtimeFixture(t)
39
+ if err := op.Validate(now); err != nil {
40
+ t.Fatal(err)
41
+ }
42
+ for name, change := range map[string]func(*RuntimeOperation){
43
+ "owner": func(o *RuntimeOperation) { o.Request.Lease.OwnerID = o.Binding.OwnerID },
44
+ "expired": func(o *RuntimeOperation) { o.Deadline = now },
45
+ "long deadline": func(o *RuntimeOperation) { o.Deadline = now.Add(time.Minute + time.Second) },
46
+ "creation": func(o *RuntimeOperation) { o.Request.CreateData = true },
47
+ "disposal": func(o *RuntimeOperation) { o.Request.Action = "delete_data" },
48
+ "wrong kind": func(o *RuntimeOperation) { o.Kind = "root" },
49
+ } {
50
+ t.Run(name, func(t *testing.T) {
51
+ other := op
52
+ change(&other)
53
+ if other.Validate(now) == nil {
54
+ t.Fatal("invalid authority accepted")
55
+ }
56
+ })
57
+ }
58
+ op.Kind, op.File = "file_write", &RuntimeFileInput{Path: "notes/example.txt", ContentBase64: base64.StdEncoding.EncodeToString([]byte("ok"))}
59
+ if err := op.Validate(now); err != nil {
60
+ t.Fatal(err)
61
+ }
62
+ op.Request.Lease.Mode = "observe-only"
63
+ if op.Validate(now) == nil {
64
+ t.Fatal("read-only lease wrote a file")
65
+ }
66
+ op.Request.Lease.Mode = "restricted-write"
67
+ op.File.ContentBase64 = base64.StdEncoding.EncodeToString(make([]byte, RuntimeFileLimit+1))
68
+ if op.Validate(now) == nil {
69
+ t.Fatal("oversized write accepted")
70
+ }
71
+ }
72
+
73
+ func TestRuntimePathsAndBrowserTargets(t *testing.T) {
74
+ for _, path := range []string{"", "/etc/shadow", "..", "foo/../bar", "a//b", "a/./b", "a\\b", "x\x00"} {
75
+ if RuntimeFilePath(path) {
76
+ t.Fatalf("unsafe path %q accepted", path)
77
+ }
78
+ }
79
+ for _, target := range []string{"file:///etc/passwd", "file:///data/../private", "file:///data/%2e%2e/private", "file://host/data/a", "https://user:pass@example.com/", "https://example.com:8080/", "javascript:alert(1)", "http://example.com"} {
80
+ if BrowserURL(target) {
81
+ t.Fatalf("unsafe target %q accepted", target)
82
+ }
83
+ }
84
+ for _, target := range []string{"https://example.com/", "file:///data/acceptance.html"} {
85
+ if !BrowserURL(target) {
86
+ t.Fatalf("bounded target %q refused", target)
87
+ }
88
+ }
89
+ }
90
+
91
+ func TestRuntimeReceiptCorrelationAndDigest(t *testing.T) {
92
+ op, now := runtimeFixture(t)
93
+ result := RuntimeResult{SchemaVersion: RuntimeResultVersion, OperationID: op.OperationID, BindingID: op.Binding.ID, Environment: env.Result{SchemaVersion: env.ResultVersion, LeaseID: op.Request.Lease.ID, RunID: op.Request.Lease.RunID, State: "active", ObservedAt: now, Snapshot: &env.Snapshot{SchemaVersion: env.SnapshotVersion, LeaseID: op.Request.Lease.ID, RunID: op.Request.Lease.RunID, State: "active", ObservedAt: now}}}
94
+ if err := result.ValidateFor(op); err != nil {
95
+ t.Fatal(err)
96
+ }
97
+ result.BindingID = op.Binding.DataRef
98
+ if result.ValidateFor(op) == nil {
99
+ t.Fatal("foreign receipt accepted")
100
+ }
101
+ digest, err := op.Digest()
102
+ if err != nil {
103
+ t.Fatal(err)
104
+ }
105
+ op.Kind, op.File = "file_read", &RuntimeFileInput{Path: "a"}
106
+ changed, err := op.Digest()
107
+ if err != nil || digest == changed {
108
+ t.Fatal("payload did not change request identity")
109
+ }
110
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "type": "object",
3
+ "additionalProperties": false,
4
+ "required": [
5
+ "url",
6
+ "method"
7
+ ],
8
+ "properties": {
9
+ "url": {
10
+ "type": "string",
11
+ "pattern": "^https://",
12
+ "maxLength": 4096
13
+ },
14
+ "method": {
15
+ "enum": [
16
+ "GET",
17
+ "HEAD",
18
+ "POST",
19
+ "PUT",
20
+ "PATCH",
21
+ "DELETE",
22
+ "OPTIONS"
23
+ ]
24
+ },
25
+ "headers": {
26
+ "type": "object",
27
+ "maxProperties": 64,
28
+ "additionalProperties": {
29
+ "type": "string",
30
+ "maxLength": 8192
31
+ }
32
+ },
33
+ "bodyBase64": {
34
+ "type": "string",
35
+ "maxLength": 87384
36
+ }
37
+ },
38
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
39
+ "$id": "https://opensoha.dev/schemas/personal-browser-network-request.schema.json"
40
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "type": "object",
3
+ "additionalProperties": false,
4
+ "required": [
5
+ "status",
6
+ "headers",
7
+ "bodyBase64"
8
+ ],
9
+ "properties": {
10
+ "status": {
11
+ "type": "integer",
12
+ "minimum": 100,
13
+ "maximum": 599
14
+ },
15
+ "headers": {
16
+ "type": "array",
17
+ "maxItems": 128,
18
+ "items": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "required": [
22
+ "name",
23
+ "value"
24
+ ],
25
+ "properties": {
26
+ "name": {
27
+ "type": "string",
28
+ "maxLength": 128
29
+ },
30
+ "value": {
31
+ "type": "string",
32
+ "maxLength": 8192
33
+ }
34
+ }
35
+ }
36
+ },
37
+ "bodyBase64": {
38
+ "type": "string",
39
+ "maxLength": 1398104
40
+ }
41
+ },
42
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
43
+ "$id": "https://opensoha.dev/schemas/personal-browser-network-response.schema.json"
44
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://opensoha.dev/schemas/personal-environment-export.schema.json",
4
+ "title": "Personal environment export receipt",
5
+ "description": "Final receipt after all length-prefixed TAR chunks, producer completion and owned helper cleanup. The receiver must check size, SHA-256, exact original lease/owner/scope and fresh observedAt before exposing any file.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "schemaVersion",
10
+ "leaseId",
11
+ "runId",
12
+ "ownerId",
13
+ "scopeHash",
14
+ "sizeBytes",
15
+ "sha256",
16
+ "observedAt"
17
+ ],
18
+ "properties": {
19
+ "schemaVersion": {
20
+ "const": "opensoha.dev/personal-environment-export/v1"
21
+ },
22
+ "leaseId": {
23
+ "type": "string",
24
+ "minLength": 1,
25
+ "maxLength": 128
26
+ },
27
+ "runId": {
28
+ "type": "string",
29
+ "minLength": 1,
30
+ "maxLength": 128
31
+ },
32
+ "ownerId": {
33
+ "type": "string",
34
+ "minLength": 1,
35
+ "maxLength": 128
36
+ },
37
+ "scopeHash": {
38
+ "type": "string",
39
+ "pattern": "^sha256:[a-f0-9]{64}$"
40
+ },
41
+ "sizeBytes": {
42
+ "type": "integer",
43
+ "minimum": 1,
44
+ "maximum": 268435456
45
+ },
46
+ "sha256": {
47
+ "type": "string",
48
+ "pattern": "^sha256:[a-f0-9]{64}$"
49
+ },
50
+ "observedAt": {
51
+ "type": "string",
52
+ "format": "date-time"
53
+ }
54
+ }
55
+ }