@opensoha/contracts 0.1.16 → 0.1.18

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 (391) hide show
  1. package/COMPATIBILITY.md +91 -0
  2. package/README.md +49 -3
  3. package/auth/catalog_generated.go +2 -2
  4. package/auth/catalog_test.go +32 -0
  5. package/auth/outpost_headers.go +17 -0
  6. package/auth/outpost_headers_test.go +16 -0
  7. package/auth/permission-catalog.json +1097 -187
  8. package/compat/consumer-revisions.json +3 -0
  9. package/compat/json-schema-ecosystem-0.1.0.json +1 -1
  10. package/compat/openapi-loose-boundaries-0.1.0.json +78 -46
  11. package/delivery/canonical.go +136 -0
  12. package/delivery/document.go +203 -0
  13. package/delivery/document.schema.json +1527 -0
  14. package/delivery/document_test.go +191 -0
  15. package/delivery/parser.go +155 -0
  16. package/evaluation/evaluation-run.schema.json +64 -10
  17. package/fixtures/json-schema/gateway-manifest/invalid/bad-execution-mode.json +73 -0
  18. package/fixtures/json-schema/gateway-manifest/invalid/unversioned-check.json +85 -0
  19. package/fixtures/json-schema/gateway-manifest/valid/checks.json +86 -0
  20. package/fixtures/json-schema/gateway-manifest/valid/versioned-task.json +73 -0
  21. package/fixtures/json-schema/network-ingest-event/invalid/duplicate-event.json +33 -0
  22. package/fixtures/json-schema/network-ingest-event/invalid/message-payload-mismatch.json +23 -0
  23. package/fixtures/json-schema/network-ingest-event/invalid/password.json +22 -0
  24. package/fixtures/json-schema/network-ingest-event/invalid/proxy-flow-destination.json +28 -0
  25. package/fixtures/json-schema/network-ingest-event/invalid/vpn-probe-negative-rtt.json +34 -0
  26. package/fixtures/json-schema/network-ingest-event/invalid/vpn-stats-secret.json +27 -0
  27. package/fixtures/json-schema/network-ingest-event/valid/endpoint-proxy-flow.json +27 -0
  28. package/fixtures/json-schema/network-ingest-event/valid/freeradius-accounting.json +25 -0
  29. package/fixtures/json-schema/network-ingest-event/valid/gateway.json +53 -0
  30. package/fixtures/json-schema/network-ingest-event/valid/vpn-probe-batch.json +45 -0
  31. package/fixtures/json-schema/network-ingest-event/valid/vpn-tunnel-stats.json +26 -0
  32. package/fixtures/json-schema/network-radius-accounting/invalid/password.json +9 -0
  33. package/fixtures/json-schema/network-radius-accounting/valid/accounting.json +16 -0
  34. package/fixtures/json-schema/network-runtime-protocol/invalid/invalid-cidr.json +31 -0
  35. package/fixtures/json-schema/network-runtime-protocol/invalid/message-payload-mismatch.json +19 -0
  36. package/fixtures/json-schema/network-runtime-protocol/invalid/mihomo-raw-subscription.json +33 -0
  37. package/fixtures/json-schema/network-runtime-protocol/invalid/nas-authorization-password.json +18 -0
  38. package/fixtures/json-schema/network-runtime-protocol/invalid/nas-eap-without-certificate.json +17 -0
  39. package/fixtures/json-schema/network-runtime-protocol/invalid/private-key.json +20 -0
  40. package/fixtures/json-schema/network-runtime-protocol/invalid/raw-credential-reference.json +16 -0
  41. package/fixtures/json-schema/network-runtime-protocol/invalid/runtime-interval-too-short.json +23 -0
  42. package/fixtures/json-schema/network-runtime-protocol/invalid/vpn-managed-bad-token.json +15 -0
  43. package/fixtures/json-schema/network-runtime-protocol/invalid/vpn-managed-mode-override.json +16 -0
  44. package/fixtures/json-schema/network-runtime-protocol/invalid/wireguard-capability-without-key.json +19 -0
  45. package/fixtures/json-schema/network-runtime-protocol/invalid/wireguard-default-route.json +31 -0
  46. package/fixtures/json-schema/network-runtime-protocol/invalid/ztna-without-access-grant.json +17 -0
  47. package/fixtures/json-schema/network-runtime-protocol/valid/configuration-desired.json +86 -0
  48. package/fixtures/json-schema/network-runtime-protocol/valid/direct-ztna-connect.json +19 -0
  49. package/fixtures/json-schema/network-runtime-protocol/valid/direct-ztna-result.json +33 -0
  50. package/fixtures/json-schema/network-runtime-protocol/valid/internal-ztna-connect.json +19 -0
  51. package/fixtures/json-schema/network-runtime-protocol/valid/managed-vpn-macos-enrollment.json +27 -0
  52. package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-app-subscription-configuration.json +32 -0
  53. package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-manual-node-configuration.json +32 -0
  54. package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-wireguard-configuration.json +70 -0
  55. package/fixtures/json-schema/network-runtime-protocol/valid/nas-authorization-request.json +19 -0
  56. package/fixtures/json-schema/network-runtime-protocol/valid/nas-authorization-result.json +24 -0
  57. package/fixtures/json-schema/network-runtime-protocol/valid/nas-session-command-result.json +17 -0
  58. package/fixtures/json-schema/network-runtime-protocol/valid/nas-session-command.json +27 -0
  59. package/fixtures/json-schema/network-runtime-protocol/valid/vpn-connect-request.json +17 -0
  60. package/fixtures/json-schema/network-runtime-protocol/valid/vpn-connect-result.json +34 -0
  61. package/fixtures/json-schema/network-runtime-protocol/valid/vpn-managed-connect.json +15 -0
  62. package/fixtures/json-schema/network-runtime-protocol/valid/wireguard-enrollment.json +20 -0
  63. package/fixtures/json-schema/network-runtime-protocol/valid/wireguard-site-link.json +48 -0
  64. package/fixtures/openapi/AgentChatInput/invalid/empty-question.json +4 -0
  65. package/fixtures/openapi/AgentChatInput/invalid/system-role.json +9 -0
  66. package/fixtures/openapi/AgentChatInput/valid/authorized-background.json +5 -0
  67. package/fixtures/openapi/AgentChatInput/valid/conversation.json +15 -0
  68. package/fixtures/openapi/AgentRunnerToolCallRequest/invalid/model-identity.json +1 -0
  69. package/fixtures/openapi/AgentRunnerToolCallRequest/valid/search.json +1 -0
  70. package/fixtures/openapi/AnnouncementReceiptPageEnvelope/invalid/missing-read-count.json +9 -0
  71. package/fixtures/openapi/AnnouncementReceiptPageEnvelope/valid/read-and-unread.json +28 -0
  72. package/fixtures/openapi/ApplicationInput/invalid/negative-version.json +6 -0
  73. package/fixtures/openapi/ApplicationInput/invalid/zero-version.json +6 -0
  74. package/fixtures/openapi/ApplicationInput/valid/versioned.json +6 -0
  75. package/fixtures/openapi/ApprovalRequest/invalid/resolved-scopes.json +20 -0
  76. package/fixtures/openapi/ApprovalRequest/valid/resolved-scopes.json +21 -0
  77. package/fixtures/openapi/BuildTemplateInput/invalid/negative-revision.json +6 -0
  78. package/fixtures/openapi/BuildTemplateInput/valid/draft.json +11 -0
  79. package/fixtures/openapi/BuildTriggerRequest/invalid/no-ref.json +1 -0
  80. package/fixtures/openapi/BuildTriggerRequest/valid/no-environment.json +1 -0
  81. package/fixtures/openapi/BuildpacksConfiguration/invalid/mutable-builder.json +5 -0
  82. package/fixtures/openapi/BuildpacksConfiguration/invalid/privilege-override.json +6 -0
  83. package/fixtures/openapi/BuildpacksConfiguration/invalid/unsupported-platform.json +5 -0
  84. package/fixtures/openapi/BuildpacksConfiguration/valid/pinned.json +5 -0
  85. package/fixtures/openapi/CapabilityAssessment/invalid/contract.json +16 -0
  86. package/fixtures/openapi/CapabilityAssessment/valid/contract.json +16 -0
  87. package/fixtures/openapi/CapabilityPlan/invalid/contract.json +16 -0
  88. package/fixtures/openapi/CapabilityPlan/valid/contract.json +19 -0
  89. package/fixtures/openapi/CapabilityPlanValidation/invalid/contract.json +5 -0
  90. package/fixtures/openapi/CapabilityPlanValidation/valid/contract.json +11 -0
  91. package/fixtures/openapi/CapabilityTask/invalid/contract.json +33 -0
  92. package/fixtures/openapi/CapabilityTask/valid/contract.json +34 -0
  93. package/fixtures/openapi/CapabilityTaskInput/invalid/contract.json +22 -0
  94. package/fixtures/openapi/CapabilityTaskInput/valid/contract.json +22 -0
  95. package/fixtures/openapi/CapabilityValueSemantic/invalid/contract.json +4 -0
  96. package/fixtures/openapi/CapabilityValueSemantic/valid/contract.json +7 -0
  97. package/fixtures/openapi/DeliveryAccessResult/invalid/release-verification.json +8 -0
  98. package/fixtures/openapi/DeliveryAccessResult/valid/release-verification.json +8 -0
  99. package/fixtures/openapi/DeliveryBatchAssessment/invalid/release-verification.json +11 -0
  100. package/fixtures/openapi/DeliveryBatchAssessment/valid/release-verification.json +11 -0
  101. package/fixtures/openapi/DeliveryBatchAssessmentInput/invalid/release-verification.json +9 -0
  102. package/fixtures/openapi/DeliveryBatchAssessmentInput/valid/release-verification.json +8 -0
  103. package/fixtures/openapi/DeliveryBatchInput/invalid/ambiguous-source.json +19 -0
  104. package/fixtures/openapi/DeliveryBatchInput/invalid/missing-idempotency.json +16 -0
  105. package/fixtures/openapi/DeliveryBatchInput/valid/quick-deploy.json +17 -0
  106. package/fixtures/openapi/DeliveryBatchInput/valid/saved-workflow.json +5 -0
  107. package/fixtures/openapi/DeliveryBlueprintInput/invalid/unversioned-preset.json +31 -0
  108. package/fixtures/openapi/DeliveryBlueprintInput/valid/service-preset.json +32 -0
  109. package/fixtures/openapi/DeliveryDocument/invalid/unknown-field.json +24 -0
  110. package/fixtures/openapi/DeliveryDocument/invalid/unknown-kind.json +23 -0
  111. package/fixtures/openapi/DeliveryDocument/invalid/unknown-version.json +23 -0
  112. package/fixtures/openapi/DeliveryDocument/valid/build-template.json +26 -0
  113. package/fixtures/openapi/DeliveryDocument/valid/deployment-template.json +26 -0
  114. package/fixtures/openapi/DeliveryDocument/valid/legacy-dag.json +39 -0
  115. package/fixtures/openapi/DeliveryDocument/valid/workflow-template.json +23 -0
  116. package/fixtures/openapi/DeliveryDocument/valid/workflow.json +20 -0
  117. package/fixtures/openapi/DeliveryDraftInput/invalid/short-idempotency-key.json +30 -0
  118. package/fixtures/openapi/DeliveryDraftInput/valid/idempotent-create.json +30 -0
  119. package/fixtures/openapi/DeliveryDraftService/invalid/zero-version.json +7 -0
  120. package/fixtures/openapi/DeliveryDraftService/valid/versioned-template.json +9 -0
  121. package/fixtures/openapi/DeliveryExecutionHistoryPage/invalid-kind.json +9 -0
  122. package/fixtures/openapi/DeliveryExecutionHistoryPage/valid-build.json +16 -0
  123. package/fixtures/openapi/DeliveryExecutionHistoryPage/valid-empty.json +3 -0
  124. package/fixtures/openapi/DeliveryPlanApprovalInput/invalid/confirm.json +1 -0
  125. package/fixtures/openapi/DeliveryPlanApprovalInput/valid/approve.json +1 -0
  126. package/fixtures/openapi/DeliveryPlanInput/invalid/draft-manifest-revision.json +1 -0
  127. package/fixtures/openapi/DeliveryPlanInput/valid/manifest-revision.json +1 -0
  128. package/fixtures/openapi/DeliveryTargetInput/invalid/deploy-without-environment.json +7 -0
  129. package/fixtures/openapi/DeliveryTargetInput/valid/build-without-environment.json +6 -0
  130. package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/duplicate-kind.json +13 -0
  131. package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/embedded-credentials.json +16 -0
  132. package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/empty-kinds.json +10 -0
  133. package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/negative-generation.json +15 -0
  134. package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/unknown-kind.json +12 -0
  135. package/fixtures/openapi/DeliveryTemplateSourceInput/valid/manual-source.json +15 -0
  136. package/fixtures/openapi/DeliveryWorkflowDefinition/invalid/unversioned-template.json +15 -0
  137. package/fixtures/openapi/DeliveryWorkflowDefinition/valid/cross-app.json +24 -0
  138. package/fixtures/openapi/DeliveryWorkflowInput/invalid/short-key.json +1 -0
  139. package/fixtures/openapi/DeliveryWorkflowInput/valid/idempotent-create.json +1 -0
  140. package/fixtures/openapi/DeploymentTemplatePreviewInput/invalid/invalid-target-override.json +7 -0
  141. package/fixtures/openapi/DeploymentTemplatePreviewInput/invalid/invalid-zero-version.json +6 -0
  142. package/fixtures/openapi/DeploymentTemplatePreviewInput/valid/valid.json +6 -0
  143. package/fixtures/openapi/DockerDeliveryConfiguration/invalid/empty-mappings.json +5 -0
  144. package/fixtures/openapi/DockerDeliveryConfiguration/valid/compose.json +7 -0
  145. package/fixtures/openapi/DockerDeliverySnapshot/invalid/missing-required-field.json +19 -0
  146. package/fixtures/openapi/DockerDeliverySnapshot/valid/frozen-project.json +20 -0
  147. package/fixtures/openapi/DockerOperationCallbackRequest/invalid/cancellation-acknowledged-not-boolean.json +13 -0
  148. package/fixtures/openapi/DockerOperationCallbackRequest/valid/cancellation-acknowledged.json +13 -0
  149. package/fixtures/openapi/DockerProjectInput/invalid/empty-host.json +4 -0
  150. package/fixtures/openapi/DockerProjectInput/valid/inline-idempotent.json +7 -0
  151. package/fixtures/openapi/DockerQuickCreateHostInput/invalid/capacity-type.json +1 -0
  152. package/fixtures/openapi/DockerQuickCreateHostInput/valid/capacity.json +1 -0
  153. package/fixtures/openapi/EndpointDevice/invalid/missing-owner.json +8 -0
  154. package/fixtures/openapi/EndpointDevice/valid/active.json +35 -0
  155. package/fixtures/openapi/EndpointDeviceRegistrationInput/invalid/client-owned-status.json +5 -0
  156. package/fixtures/openapi/EndpointDeviceRegistrationInput/invalid/oversized-network-inventory.json +18 -0
  157. package/fixtures/openapi/EndpointDeviceRegistrationInput/valid/macos.json +28 -0
  158. package/fixtures/openapi/HelmDeliveryConfiguration/invalid/plain-credential.json +21 -0
  159. package/fixtures/openapi/HelmDeliveryConfiguration/invalid/release-name.json +20 -0
  160. package/fixtures/openapi/HelmDeliveryConfiguration/valid/private-oci.json +20 -0
  161. package/fixtures/openapi/HelmDeliverySnapshot/invalid/secret-plan.json +31 -0
  162. package/fixtures/openapi/HelmDeliverySnapshot/invalid/unknown-operation.json +28 -0
  163. package/fixtures/openapi/HelmDeliverySnapshot/valid/first-install.json +28 -0
  164. package/fixtures/openapi/IdentityApplication/invalid/outpost-status.json +1 -0
  165. package/fixtures/openapi/IdentityApplicationOnboardingInput/invalid/missing-access.json +12 -0
  166. package/fixtures/openapi/IdentityApplicationOnboardingInput/valid/link.json +13 -0
  167. package/fixtures/openapi/IdentityOutpost/invalid/missing-runtime-status.json +9 -0
  168. package/fixtures/openapi/IdentityOutpost/valid/healthy.json +17 -0
  169. package/fixtures/openapi/IdentityOutpost/valid/unregistered.json +12 -0
  170. package/fixtures/openapi/IdentityOutpostDeployment/invalid/missing-protocol.json +3 -0
  171. package/fixtures/openapi/IdentityOutpostDeployment/valid/remote.json +6 -0
  172. package/fixtures/openapi/IdentityOutpostInput/invalid/forged-observation.json +7 -0
  173. package/fixtures/openapi/IdentityOutpostInput/valid/forward-auth.json +6 -0
  174. package/fixtures/openapi/IdentityOutpostInput/valid/legacy.json +8 -0
  175. package/fixtures/openapi/IdentityProviderSetup/invalid/fake-verification.json +1 -0
  176. package/fixtures/openapi/IdentityProviderSetup/valid/incomplete.json +1 -0
  177. package/fixtures/openapi/IdentityProviderSetup/valid/remote.json +1 -0
  178. package/fixtures/openapi/IdentityProviderUserMetadata/invalid/attributes.json +8 -0
  179. package/fixtures/openapi/IdentityProviderUserMetadata/valid/attributes.json +13 -0
  180. package/fixtures/openapi/KubernetesCustomResource/invalid/negative-generation.json +1 -0
  181. package/fixtures/openapi/KubernetesCustomResource/valid/deleting-operator.json +1 -0
  182. package/fixtures/openapi/KubernetesResourceCreationReceipt/invalid/manifest-content.json +20 -0
  183. package/fixtures/openapi/KubernetesResourceCreationReceipt/valid/confirmed.json +20 -0
  184. package/fixtures/openapi/KubernetesResourceCreationReceipt/valid/unknown.json +19 -0
  185. package/fixtures/openapi/LLMUpstreamTestResultEnvelope/invalid/models-not-array.json +9 -0
  186. package/fixtures/openapi/LLMUpstreamTestResultEnvelope/valid/discovered-models.json +11 -0
  187. package/fixtures/openapi/ManifestDeploymentEnvelope/invalid/negative-resource-observation.json +57 -0
  188. package/fixtures/openapi/ManifestDeploymentEnvelope/valid/operator-deleting.json +57 -0
  189. package/fixtures/openapi/ManifestExecutionTaskPayload/invalid/gitops-empty.json +7 -0
  190. package/fixtures/openapi/ManifestExecutionTaskPayload/valid/gitops-frozen-children.json +18 -0
  191. package/fixtures/openapi/ManifestKustomizeOptions/invalid/escaping-entry.json +1 -0
  192. package/fixtures/openapi/ManifestKustomizeOptions/invalid/mutable-image.json +1 -0
  193. package/fixtures/openapi/ManifestKustomizeOptions/valid/overlay-images.json +7 -0
  194. package/fixtures/openapi/ManifestRevisionInput/invalid/missing-version.json +1 -0
  195. package/fixtures/openapi/ManifestRevisionInput/valid/current-draft.json +1 -0
  196. package/fixtures/openapi/ManifestServiceTemplateInputs/invalid/mutable-image.json +8 -0
  197. package/fixtures/openapi/ManifestServiceTemplateInputs/valid/frozen-service-image.json +9 -0
  198. package/fixtures/openapi/NetworkAccessGrant/invalid/token-digest.json +15 -0
  199. package/fixtures/openapi/NetworkAccessGrant/valid/issued.json +14 -0
  200. package/fixtures/openapi/NetworkAccessGrantInput/invalid/no-resources.json +7 -0
  201. package/fixtures/openapi/NetworkAccessGrantInput/valid/direct-ztna.json +8 -0
  202. package/fixtures/openapi/NetworkAccessGrantSecret/invalid/short-token.json +17 -0
  203. package/fixtures/openapi/NetworkAccessGrantSecret/valid/issued.json +17 -0
  204. package/fixtures/openapi/NetworkAccessPolicy/invalid/missing-version.json +20 -0
  205. package/fixtures/openapi/NetworkAccessPolicy/valid/published-draft.json +21 -0
  206. package/fixtures/openapi/NetworkAccessPolicyInput/invalid/duplicate-team.json +17 -0
  207. package/fixtures/openapi/NetworkAccessPolicyInput/valid/team-and-tag-allow.json +17 -0
  208. package/fixtures/openapi/NetworkConflictAnalysisRequest/invalid/ipv6-range.json +9 -0
  209. package/fixtures/openapi/NetworkConflictAnalysisRequest/valid/runtime-ranges.json +15 -0
  210. package/fixtures/openapi/NetworkConnectionOption/invalid/secret.json +10 -0
  211. package/fixtures/openapi/NetworkConnectionOption/valid/wifi.json +9 -0
  212. package/fixtures/openapi/NetworkConnectionOption/valid/wired.json +8 -0
  213. package/fixtures/openapi/NetworkGateway/invalid/missing-site.json +7 -0
  214. package/fixtures/openapi/NetworkGateway/valid/online.json +23 -0
  215. package/fixtures/openapi/NetworkGatewayInput/invalid/default-route-overlay.json +13 -0
  216. package/fixtures/openapi/NetworkGatewayInput/valid/routed.json +14 -0
  217. package/fixtures/openapi/NetworkLease/invalid/empty-cidrs.json +12 -0
  218. package/fixtures/openapi/NetworkLease/valid/active.json +12 -0
  219. package/fixtures/openapi/NetworkMihomoProfile/invalid/subscription-leak.json +20 -0
  220. package/fixtures/openapi/NetworkMihomoProfile/valid/managed.json +21 -0
  221. package/fixtures/openapi/NetworkMihomoProfileInput/invalid/app-subscription-secret.json +14 -0
  222. package/fixtures/openapi/NetworkMihomoProfileInput/invalid/manual-node-with-subscription.json +20 -0
  223. package/fixtures/openapi/NetworkMihomoProfileInput/valid/app-subscription.json +14 -0
  224. package/fixtures/openapi/NetworkMihomoProfileInput/valid/managed.json +16 -0
  225. package/fixtures/openapi/NetworkMihomoProfileInput/valid/manual-node.json +21 -0
  226. package/fixtures/openapi/NetworkNASBinding/invalid/secret.json +13 -0
  227. package/fixtures/openapi/NetworkNASBinding/valid/default.json +16 -0
  228. package/fixtures/openapi/NetworkNASBindingInput/invalid/secret.json +10 -0
  229. package/fixtures/openapi/NetworkNASBindingInput/valid/default.json +13 -0
  230. package/fixtures/openapi/NetworkPolicyPreviewRequest/invalid/unknown-mode.json +6 -0
  231. package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/external-direct-ztna.json +6 -0
  232. package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/external-vpn-ztna.json +6 -0
  233. package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/external-vpn.json +6 -0
  234. package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/internal-direct.json +7 -0
  235. package/fixtures/openapi/NetworkPolicyPreviewRequest/valid/internal-ztna.json +7 -0
  236. package/fixtures/openapi/NetworkPolicyPreviewResult/invalid/bypass-path.json +9 -0
  237. package/fixtures/openapi/NetworkPolicyPreviewResult/valid/onboarding.json +10 -0
  238. package/fixtures/openapi/NetworkPolicyPreviewResult/valid/protected-broad-path-denied.json +9 -0
  239. package/fixtures/openapi/NetworkPolicyPreviewResult/valid/protected-resource.json +9 -0
  240. package/fixtures/openapi/NetworkPolicyPreviewResult/valid/quarantine.json +10 -0
  241. package/fixtures/openapi/NetworkPolicyPreviewResult/valid/restricted.json +10 -0
  242. package/fixtures/openapi/NetworkPolicySnapshot/invalid/zero-version.json +7 -0
  243. package/fixtures/openapi/NetworkPolicySnapshot/valid/published.json +7 -0
  244. package/fixtures/openapi/NetworkResourceInput/invalid/invalid-port.json +10 -0
  245. package/fixtures/openapi/NetworkResourceInput/valid/protected-cidr.json +10 -0
  246. package/fixtures/openapi/NetworkRuntimeEnrollmentInput/invalid/excessive-ttl.json +7 -0
  247. package/fixtures/openapi/NetworkRuntimeEnrollmentInput/valid/endpoint.json +7 -0
  248. package/fixtures/openapi/NetworkRuntimeEnrollmentSecret/invalid/short-token.json +15 -0
  249. package/fixtures/openapi/NetworkRuntimeEnrollmentSecret/valid/pending.json +15 -0
  250. package/fixtures/openapi/NetworkSession/invalid/unknown-status.json +14 -0
  251. package/fixtures/openapi/NetworkSession/valid/restricted-vpn-ztna.json +16 -0
  252. package/fixtures/openapi/NetworkSessionActionExecuteInput/invalid/short-hash.json +6 -0
  253. package/fixtures/openapi/NetworkSessionActionExecuteInput/valid/restrict.json +6 -0
  254. package/fixtures/openapi/NetworkSessionActionInput/invalid/unknown-action.json +5 -0
  255. package/fixtures/openapi/NetworkSessionActionInput/valid/restrict.json +5 -0
  256. package/fixtures/openapi/NetworkSessionActionPlan/invalid/missing-hash.json +12 -0
  257. package/fixtures/openapi/NetworkSessionActionPlan/valid/fallback-disconnect.json +13 -0
  258. package/fixtures/openapi/NetworkSessionCommand/invalid/unknown-status.json +14 -0
  259. package/fixtures/openapi/NetworkSessionCommand/valid/pending.json +14 -0
  260. package/fixtures/openapi/NetworkSiteInput/invalid/unknown-status.json +4 -0
  261. package/fixtures/openapi/NetworkSiteInput/valid/shanghai.json +5 -0
  262. package/fixtures/openapi/NetworkSiteProfileBinding/invalid/vlan.json +9 -0
  263. package/fixtures/openapi/NetworkSiteProfileBinding/valid/default.json +10 -0
  264. package/fixtures/openapi/NetworkSiteProfileBindingInput/invalid/timeout.json +7 -0
  265. package/fixtures/openapi/NetworkSiteProfileBindingInput/valid/default.json +7 -0
  266. package/fixtures/openapi/NetworkSpaceInput/invalid/empty-cidrs.json +6 -0
  267. package/fixtures/openapi/NetworkSpaceInput/invalid/malformed-cidr.json +6 -0
  268. package/fixtures/openapi/NetworkSpaceInput/valid/corp.json +6 -0
  269. package/fixtures/openapi/NetworkTelemetrySummary/invalid/raw-destination.json +29 -0
  270. package/fixtures/openapi/NetworkTelemetrySummary/valid/proxy-flow.json +36 -0
  271. package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/auto-with-gateway.json +6 -0
  272. package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/missing-manual-gateway.json +5 -0
  273. package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/mode-override.json +7 -0
  274. package/fixtures/openapi/NetworkVPNConnectionIntentInput/valid/auto-intent.json +5 -0
  275. package/fixtures/openapi/NetworkVPNConnectionIntentInput/valid/manual-intent.json +6 -0
  276. package/fixtures/openapi/NetworkVPNProfileInput/invalid/unknown-private-key.json +25 -0
  277. package/fixtures/openapi/NetworkVPNProfileInput/valid/assigned-profile.json +24 -0
  278. package/fixtures/openapi/NetworkVPNSelectionPolicyInput/invalid/unbounded-attempts.json +21 -0
  279. package/fixtures/openapi/NetworkVPNSelectionPolicyInput/valid/provider-policy.json +21 -0
  280. package/fixtures/openapi/ObservabilityMetricAssessmentInput/invalid/arbitrary-expression.json +10 -0
  281. package/fixtures/openapi/ObservabilityMetricAssessmentInput/valid/metric-window.json +10 -0
  282. package/fixtures/openapi/PluginManifest/valid/hermes-api.json +53 -0
  283. package/fixtures/openapi/ProgressiveRolloutControlInput/invalid/missing-identity.json +5 -0
  284. package/fixtures/openapi/ProgressiveRolloutControlInput/invalid/skip-analysis.json +5 -0
  285. package/fixtures/openapi/ProgressiveRolloutControlInput/valid/pause.json +5 -0
  286. package/fixtures/openapi/ReleaseTargetInput/invalid/missing-required-field.json +15 -0
  287. package/fixtures/openapi/ReleaseTargetInput/valid/docker-compose.json +16 -0
  288. package/fixtures/openapi/RepositoryAnalysis/invalid/moving-ref.json +11 -0
  289. package/fixtures/openapi/RepositoryAnalysis/valid/go.json +29 -0
  290. package/fixtures/openapi/RepositoryAnalysisInput/invalid/arbitrary-source-url.json +6 -0
  291. package/fixtures/openapi/RepositoryAnalysisInput/invalid/missing-ref.json +4 -0
  292. package/fixtures/openapi/RepositoryAnalysisInput/valid/nested-project.json +6 -0
  293. package/fixtures/openapi/ResourceLease/invalid/missing-session.json +10 -0
  294. package/fixtures/openapi/ResourceLease/valid/active.json +11 -0
  295. package/fixtures/openapi/ServiceDeploymentTemplateBinding/invalid/invalid-unpinned.json +4 -0
  296. package/fixtures/openapi/ServiceDeploymentTemplateBinding/valid/valid.json +7 -0
  297. package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-executable-schema.json +25 -0
  298. package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-missing-schema.json +21 -0
  299. package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-mixed-renderers.json +40 -0
  300. package/fixtures/openapi/ServiceDeploymentTemplateInput/valid/valid-nested-parameters.json +41 -0
  301. package/fixtures/openapi/ServiceDeploymentTemplateInput/valid/valid-yaml.json +34 -0
  302. package/fixtures/openapi/TemplatePublishInput/invalid/missing-revision.json +1 -0
  303. package/fixtures/openapi/TemplatePublishInput/invalid/unknown-field.json +4 -0
  304. package/fixtures/openapi/TemplatePublishInput/invalid/zero-revision.json +3 -0
  305. package/fixtures/openapi/TemplatePublishInput/valid/revision.json +3 -0
  306. package/fixtures/openapi/ToolCapability/invalid/bad-idempotency.json +35 -0
  307. package/fixtures/openapi/ToolCapability/invalid/unversioned-check.json +47 -0
  308. package/fixtures/openapi/ToolCapability/valid/checks.json +48 -0
  309. package/fixtures/openapi/ToolCapability/valid/versioned-task.json +35 -0
  310. package/fixtures/openapi/ToolInvocationRequest/invalid/empty-version.json +8 -0
  311. package/fixtures/openapi/ToolInvocationRequest/valid/versioned.json +8 -0
  312. package/fixtures/openapi/ToolInvocationResult/invalid/missing-status-call.json +13 -0
  313. package/fixtures/openapi/ToolInvocationResult/valid/task.json +20 -0
  314. package/fixtures/openapi/VirtualMachineCreateInput/invalid/capacity-type.json +1 -0
  315. package/fixtures/openapi/VirtualMachineCreateInput/valid/capacity.json +1 -0
  316. package/fixtures/openapi/VirtualizationCapacityInput/invalid/missing-disk.json +1 -0
  317. package/fixtures/openapi/VirtualizationCapacityInput/valid/running-vm.json +1 -0
  318. package/fixtures/openapi/VirtualizationCapacityResult/invalid/invented-status.json +1 -0
  319. package/fixtures/openapi/VirtualizationCapacityResult/valid/available.json +1 -0
  320. package/fixtures/openapi/VirtualizationWorkerCreateInput/invalid/secret-input.json +5 -0
  321. package/fixtures/openapi/VirtualizationWorkerCreateInput/valid/one-worker.json +4 -0
  322. package/fixtures/openapi/VirtualizationWorkerPoolInput/invalid/external-owner.json +27 -0
  323. package/fixtures/openapi/VirtualizationWorkerPoolInput/invalid/zero-budget.json +27 -0
  324. package/fixtures/openapi/VirtualizationWorkerPoolInput/valid/registered.json +27 -0
  325. package/fixtures/openapi/VirtualizationWorkerPoolListEnvelope/invalid/nested-data.json +5 -0
  326. package/fixtures/openapi/VirtualizationWorkerPoolListEnvelope/valid/empty.json +3 -0
  327. package/fixtures/openapi/WorkbenchContextSelection/invalid/missing-file-content.json +8 -0
  328. package/fixtures/openapi/WorkbenchContextSelection/invalid/unknown-reference-kind.json +8 -0
  329. package/fixtures/openapi/WorkbenchContextSelection/valid/text-and-resource.json +17 -0
  330. package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/trigger-without-plan.json +10 -0
  331. package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/unknown-trigger.json +28 -0
  332. package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/zero-interval.json +28 -0
  333. package/fixtures/openapi/WorkbenchInspectionTaskInput/valid/alert-capability.json +30 -0
  334. package/fixtures/openapi/WorkbenchInspectionTaskInput/valid/scheduled-capability.json +28 -0
  335. package/fixtures/openapi/WorkbenchInspectionTrigger/invalid/missing-rule.json +3 -0
  336. package/fixtures/openapi/WorkbenchInspectionTrigger/invalid/schedule-alert-selector.json +4 -0
  337. package/fixtures/openapi/WorkbenchInspectionTrigger/valid/schedule.json +3 -0
  338. package/fixtures/openapi/WorkbenchModelPreferences/invalid/unknown-effort.json +1 -0
  339. package/fixtures/openapi/WorkbenchModelPreferences/valid/selected.json +1 -0
  340. package/fixtures/openapi/WorkflowCatalogPage/invalid/unsupported-kind.json +6 -0
  341. package/fixtures/openapi/WorkflowCatalogPage/valid/build-without-environment.json +6 -0
  342. package/fixtures/openapi/WorkflowTemplateInput/invalid/zero-revision.json +6 -0
  343. package/fixtures/openapi/WorkflowTemplateInput/valid/draft.json +10 -0
  344. package/gen/go/sohaapi/client.go +1 -1
  345. package/gen/go/sohaapi/types.go +15610 -6415
  346. package/gen/go/sohaapi/types_test.go +100 -1
  347. package/gen/go/sohaapi/vpn_models_test.go +21 -0
  348. package/gen/ts/sohaapi/index.ts +27859 -17518
  349. package/go.mod +107 -5
  350. package/go.sum +379 -16
  351. package/helmrelease/delivery.go +283 -0
  352. package/helmrelease/delivery_test.go +102 -0
  353. package/helmrelease/frozen_chart.go +91 -0
  354. package/helmrelease/result.go +27 -0
  355. package/helmrelease/runtime/delivery.go +508 -0
  356. package/helmrelease/runtime/delivery_test.go +236 -0
  357. package/helmrelease/runtime/resources_test.go +60 -0
  358. package/helmrelease/values.go +99 -0
  359. package/knowledge/context-envelope.schema.json +1 -0
  360. package/mcp/gateway-manifest.schema.json +387 -72
  361. package/network/README.md +156 -0
  362. package/network/network-ingest-event.schema.json +415 -0
  363. package/network/network-radius-accounting.schema.json +41 -0
  364. package/network/network-runtime-protocol.schema.json +1208 -0
  365. package/network/schema.go +26 -0
  366. package/network/schema_test.go +22 -0
  367. package/openapi/soha-api.json +59260 -35960
  368. package/openapi/soha-api.yaml +36064 -26232
  369. package/openapi/spec_generated.go +3 -3
  370. package/package.json +7 -1
  371. package/resource/manifest_health.go +155 -0
  372. package/resource/manifest_health_test.go +37 -0
  373. package/resource/models.go +14 -0
  374. package/resource/runtime/argocd.go +166 -0
  375. package/resource/runtime/argocd_integration_test.go +142 -0
  376. package/resource/runtime/argocd_observation.go +143 -0
  377. package/resource/runtime/argocd_operation.go +253 -0
  378. package/resource/runtime/argocd_resources.go +115 -0
  379. package/resource/runtime/argocd_resources_test.go +107 -0
  380. package/resource/runtime/argocd_test.go +386 -0
  381. package/resource/runtime/health.go +115 -0
  382. package/resource/runtime/health_test.go +78 -0
  383. package/resource/runtime/operator_integration_test.go +174 -0
  384. package/resource/runtime/ownership.go +178 -0
  385. package/resource/runtime/ownership_test.go +178 -0
  386. package/resource/runtime/rollout_integration_test.go +287 -0
  387. package/resource/runtime/rollout_observation.go +252 -0
  388. package/resource/runtime/rollout_operation.go +398 -0
  389. package/resource/runtime/rollout_resources.go +385 -0
  390. package/resource/runtime/rollout_task.go +165 -0
  391. package/resource/runtime/rollout_test.go +450 -0
@@ -5,9 +5,9 @@ package openapi
5
5
  import _ "embed"
6
6
 
7
7
  const (
8
- Version = "0.1.16"
9
- YAMLSHA256 = "1e34ae057d295a8ffa0b13beb54a8a2a1998b1fed5f949cb57d13360914c4ee0"
10
- JSONSHA256 = "ca3886e57c2dd1dca5a7dc10c0d1041394a8118cb901f783ebf28eadd0c90f15"
8
+ Version = "0.1.18"
9
+ YAMLSHA256 = "d614ce858924d94da353c7a9380971a3e80964f3e6c865e8b27108e859500cf4"
10
+ JSONSHA256 = "a98ea9ad99b6f044ce14e42f37b90b42e28347d53e37c727a04210b076aba960"
11
11
  )
12
12
 
13
13
  //go:embed soha-api.yaml
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensoha/contracts",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "private": false,
5
5
  "description": "Shared OpenSoha API, agent, MCP, skill, event, and auth contracts.",
6
6
  "license": "Apache-2.0",
@@ -28,6 +28,7 @@
28
28
  "test": "npm run check:version-sync && npm run check:generated && npm run validate && npm run lint:openapi && npm run check:openapi-breaking && npm run check:openapi-loose-boundaries && npm run check:json-schema-compat && npm run typecheck && go test ./..."
29
29
  },
30
30
  "exports": {
31
+ "./delivery/document.schema.json": "./delivery/document.schema.json",
31
32
  "./gen/ts/sohaapi": "./gen/ts/sohaapi/index.ts",
32
33
  "./openapi/soha-api.yaml": "./openapi/soha-api.yaml",
33
34
  "./openapi/soha-api.json": "./openapi/soha-api.json",
@@ -61,6 +62,9 @@
61
62
  "./auth/permission-catalog.json": "./auth/permission-catalog.json",
62
63
  "./auth/permission-catalog.schema.json": "./auth/permission-catalog.schema.json",
63
64
  "./connectors/connector-event-envelope.schema.json": "./connectors/connector-event-envelope.schema.json",
65
+ "./network/network-runtime-protocol.schema.json": "./network/network-runtime-protocol.schema.json",
66
+ "./network/network-ingest-event.schema.json": "./network/network-ingest-event.schema.json",
67
+ "./network/network-radius-accounting.schema.json": "./network/network-radius-accounting.schema.json",
64
68
  "./knowledge/knowledge-base.schema.json": "./knowledge/knowledge-base.schema.json",
65
69
  "./knowledge/knowledge-source.schema.json": "./knowledge/knowledge-source.schema.json",
66
70
  "./knowledge/knowledge-search.schema.json": "./knowledge/knowledge-search.schema.json",
@@ -87,6 +91,7 @@
87
91
  "./memory/memory-policy.schema.json": "./memory/memory-policy.schema.json"
88
92
  },
89
93
  "files": [
94
+ "delivery",
90
95
  "COMPATIBILITY.md",
91
96
  "go.mod",
92
97
  "go.sum",
@@ -112,6 +117,7 @@
112
117
  "events",
113
118
  "auth",
114
119
  "connectors",
120
+ "network",
115
121
  "knowledge",
116
122
  "memory"
117
123
  ],
@@ -0,0 +1,155 @@
1
+ package resource
2
+
3
+ import "encoding/json"
4
+
5
+ // ManifestHealth gives Direct and Agent execution the same conservative health
6
+ // interpretation. Successful persistence alone is health evidence only for
7
+ // built-in configuration objects, never for an unknown custom resource.
8
+ func ManifestHealth(object map[string]any) string {
9
+ if object == nil {
10
+ return "unknown"
11
+ }
12
+ encoded, err := json.Marshal(object)
13
+ if err != nil {
14
+ return "unknown"
15
+ }
16
+ var item manifestHealthObject
17
+ if err := json.Unmarshal(encoded, &item); err != nil {
18
+ return "unknown"
19
+ }
20
+ if item.Metadata.DeletionTimestamp != "" {
21
+ return "degraded"
22
+ }
23
+ kind := item.APIVersion + "/" + item.Kind
24
+ switch kind {
25
+ case "workloads.soha.io/v1alpha1/WorkloadCronJob":
26
+ // Ready describes configuration synchronization. Its source and owned
27
+ // CronJob must also be observed by the runtime before declaring health.
28
+ if item.conditionHealth(true) == "degraded" {
29
+ return "degraded"
30
+ }
31
+ return "unknown"
32
+ case "v1/ConfigMap", "v1/Secret", "v1/ServiceAccount", "rbac.authorization.k8s.io/v1/Role", "rbac.authorization.k8s.io/v1/RoleBinding", "networking.k8s.io/v1/NetworkPolicy":
33
+ return "healthy"
34
+ case "apps/v1/Deployment", "apps/v1/StatefulSet", "apps/v1/DaemonSet":
35
+ return item.workloadHealth()
36
+ case "v1/PersistentVolumeClaim":
37
+ if item.Status.Phase == "Bound" {
38
+ return "healthy"
39
+ }
40
+ if item.Status.Phase == "Lost" {
41
+ return "degraded"
42
+ }
43
+ return "progressing"
44
+ case "v1/Service":
45
+ if item.Spec.Type != "LoadBalancer" || len(item.Status.LoadBalancer.Ingress) > 0 {
46
+ return "healthy"
47
+ }
48
+ return "progressing"
49
+ case "v1/Pod", "batch/v1/Job":
50
+ if item.Status.Phase == "Succeeded" {
51
+ return "healthy"
52
+ }
53
+ if item.Status.Phase == "Failed" {
54
+ return "degraded"
55
+ }
56
+ return item.conditionHealth(false)
57
+ default:
58
+ return item.conditionHealth(true)
59
+ }
60
+ }
61
+
62
+ type manifestHealthObject struct {
63
+ APIVersion string `json:"apiVersion"`
64
+ Kind string `json:"kind"`
65
+ Metadata struct {
66
+ Generation int64 `json:"generation"`
67
+ DeletionTimestamp string `json:"deletionTimestamp"`
68
+ } `json:"metadata"`
69
+ Spec struct {
70
+ Replicas *int64 `json:"replicas"`
71
+ Type string `json:"type"`
72
+ UpdateStrategy struct {
73
+ RollingUpdate struct {
74
+ Partition int64 `json:"partition"`
75
+ } `json:"rollingUpdate"`
76
+ } `json:"updateStrategy"`
77
+ } `json:"spec"`
78
+ Status struct {
79
+ ObservedGeneration int64 `json:"observedGeneration"`
80
+ Replicas int64 `json:"replicas"`
81
+ ReadyReplicas int64 `json:"readyReplicas"`
82
+ UpdatedReplicas int64 `json:"updatedReplicas"`
83
+ AvailableReplicas int64 `json:"availableReplicas"`
84
+ DesiredNumberScheduled int64 `json:"desiredNumberScheduled"`
85
+ UpdatedNumberScheduled int64 `json:"updatedNumberScheduled"`
86
+ NumberAvailable int64 `json:"numberAvailable"`
87
+ Phase string `json:"phase"`
88
+ LoadBalancer struct {
89
+ Ingress []json.RawMessage `json:"ingress"`
90
+ } `json:"loadBalancer"`
91
+ Conditions []struct {
92
+ Type string `json:"type"`
93
+ Status string `json:"status"`
94
+ Reason string `json:"reason"`
95
+ ObservedGeneration int64 `json:"observedGeneration"`
96
+ } `json:"conditions"`
97
+ } `json:"status"`
98
+ }
99
+
100
+ func (item manifestHealthObject) workloadHealth() string {
101
+ if item.Metadata.Generation < 1 || item.Status.ObservedGeneration < item.Metadata.Generation {
102
+ return "progressing"
103
+ }
104
+ if item.conditionHealth(false) == "degraded" {
105
+ return "degraded"
106
+ }
107
+ desired := int64(1)
108
+ if item.Spec.Replicas != nil {
109
+ desired = *item.Spec.Replicas
110
+ }
111
+ healthy := false
112
+ switch item.Kind {
113
+ case "Deployment":
114
+ healthy = item.Status.UpdatedReplicas == desired && item.Status.Replicas == desired && item.Status.AvailableReplicas >= desired
115
+ case "StatefulSet":
116
+ updated := max(int64(0), desired-item.Spec.UpdateStrategy.RollingUpdate.Partition)
117
+ healthy = item.Status.ReadyReplicas >= desired && item.Status.UpdatedReplicas >= updated
118
+ case "DaemonSet":
119
+ healthy = item.Status.UpdatedNumberScheduled == item.Status.DesiredNumberScheduled && item.Status.NumberAvailable >= item.Status.DesiredNumberScheduled
120
+ }
121
+ if healthy {
122
+ return "healthy"
123
+ }
124
+ return "progressing"
125
+ }
126
+
127
+ func (item manifestHealthObject) conditionHealth(requireCurrentGeneration bool) string {
128
+ result := "progressing"
129
+ notReady := false
130
+ if requireCurrentGeneration {
131
+ result = "unknown"
132
+ }
133
+ for _, condition := range item.Status.Conditions {
134
+ if requireCurrentGeneration && (item.Metadata.Generation < 1 || max(condition.ObservedGeneration, item.Status.ObservedGeneration) < item.Metadata.Generation) {
135
+ continue
136
+ }
137
+ if condition.Status == "True" && (condition.Type == "Failed" || condition.Type == "Degraded" || condition.Type == "ReplicaFailure") {
138
+ return "degraded"
139
+ }
140
+ if condition.Type == "Progressing" && condition.Status == "False" && condition.Reason == "ProgressDeadlineExceeded" {
141
+ return "degraded"
142
+ }
143
+ if condition.Type == "Ready" || condition.Type == "Available" || condition.Type == "Complete" || condition.Type == "Established" {
144
+ if condition.Status == "True" {
145
+ result = "healthy"
146
+ } else {
147
+ notReady = true
148
+ }
149
+ }
150
+ }
151
+ if notReady {
152
+ return "progressing"
153
+ }
154
+ return result
155
+ }
@@ -0,0 +1,37 @@
1
+ package resource
2
+
3
+ import (
4
+ "encoding/json"
5
+ "testing"
6
+ )
7
+
8
+ func TestManifestHealth(t *testing.T) {
9
+ for _, test := range []struct{ name, object, want string }{
10
+ {"configuration", `{"apiVersion":"v1","kind":"ConfigMap"}`, "healthy"},
11
+ {"unknown CR", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2}}`, "unknown"},
12
+ {"custom kind cannot impersonate built-in", `{"apiVersion":"example.com/v1","kind":"ConfigMap"}`, "unknown"},
13
+ {"unobserved generation", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"spec":{"replicas":1},"status":{"observedGeneration":1,"replicas":1,"updatedReplicas":1,"availableReplicas":1,"conditions":[{"type":"Available","status":"True"}]}}`, "progressing"},
14
+ {"old replicas still available", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"spec":{"replicas":1},"status":{"observedGeneration":2,"replicas":2,"updatedReplicas":1,"availableReplicas":2}}`, "progressing"},
15
+ {"available but not updated", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"spec":{"replicas":1},"status":{"observedGeneration":2,"replicas":1,"updatedReplicas":0,"availableReplicas":1}}`, "progressing"},
16
+ {"deployment converged", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"spec":{"replicas":1},"status":{"observedGeneration":2,"replicas":1,"updatedReplicas":1,"availableReplicas":1}}`, "healthy"},
17
+ {"deployment failed after available", `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"generation":2},"status":{"observedGeneration":2,"replicas":1,"updatedReplicas":1,"availableReplicas":1,"conditions":[{"type":"Available","status":"True"},{"type":"Progressing","status":"False","reason":"ProgressDeadlineExceeded"}]}}`, "degraded"},
18
+ {"stale custom ready", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2},"status":{"conditions":[{"type":"Ready","status":"True","observedGeneration":1}]}}`, "unknown"},
19
+ {"current custom ready", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2},"status":{"conditions":[{"type":"Ready","status":"True","observedGeneration":2}]}}`, "healthy"},
20
+ {"conflicting current ready conditions", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2},"status":{"observedGeneration":2,"conditions":[{"type":"Available","status":"True"},{"type":"Ready","status":"False"}]}}`, "progressing"},
21
+ {"current custom failure", `{"apiVersion":"example.com/v1","kind":"Database","metadata":{"generation":2},"status":{"observedGeneration":2,"conditions":[{"type":"Ready","status":"True"},{"type":"Failed","status":"True"}]}}`, "degraded"},
22
+ {"deleting object", `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"deletionTimestamp":"2026-09-12T00:00:00Z"}}`, "degraded"},
23
+ {"pod not ready", `{"apiVersion":"v1","kind":"Pod","status":{"phase":"Running","conditions":[{"type":"Ready","status":"False"}]}}`, "progressing"},
24
+ {"statefulset partition", `{"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"generation":2},"spec":{"replicas":3,"updateStrategy":{"rollingUpdate":{"partition":2}}},"status":{"observedGeneration":2,"readyReplicas":3,"updatedReplicas":1}}`, "healthy"},
25
+ {"daemonset rolling", `{"apiVersion":"apps/v1","kind":"DaemonSet","metadata":{"generation":2},"status":{"observedGeneration":2,"desiredNumberScheduled":3,"updatedNumberScheduled":2,"numberAvailable":3}}`, "progressing"},
26
+ } {
27
+ t.Run(test.name, func(t *testing.T) {
28
+ var object map[string]any
29
+ if err := json.Unmarshal([]byte(test.object), &object); err != nil {
30
+ t.Fatal(err)
31
+ }
32
+ if got := ManifestHealth(object); got != test.want {
33
+ t.Fatalf("health = %s, want %s", got, test.want)
34
+ }
35
+ })
36
+ }
37
+ }
@@ -1097,6 +1097,10 @@ type CRDView struct {
1097
1097
  }
1098
1098
 
1099
1099
  type CustomResourceView struct {
1100
+ UID string `json:"uid,omitempty"`
1101
+ Generation int64 `json:"generation,omitempty"`
1102
+ DeletingAt string `json:"deletingAt,omitempty"`
1103
+ Finalizers []string `json:"finalizers,omitempty"`
1100
1104
  APIVersion string `json:"apiVersion,omitempty"`
1101
1105
  Kind string `json:"kind"`
1102
1106
  Name string `json:"name"`
@@ -1107,6 +1111,16 @@ type CustomResourceView struct {
1107
1111
  AllowedActions []string `json:"allowedActions,omitempty"`
1108
1112
  }
1109
1113
 
1114
+ // CustomResourceYAMLRequest is the Core/Agent custom-resource transport.
1115
+ // ExpectedUID fences deletion against replacement after a user viewed the object.
1116
+ type CustomResourceYAMLRequest struct {
1117
+ Definition CRDResourceDefinition `json:"definition"`
1118
+ Namespace string `json:"namespace"`
1119
+ Name string `json:"name,omitempty"`
1120
+ Content string `json:"content,omitempty"`
1121
+ ExpectedUID string `json:"expectedUid,omitempty"`
1122
+ }
1123
+
1110
1124
  type HelmReleaseView struct {
1111
1125
  Name string `json:"name"`
1112
1126
  Namespace string `json:"namespace"`
@@ -0,0 +1,166 @@
1
+ package runtime
2
+
3
+ import (
4
+ "encoding/hex"
5
+ "fmt"
6
+ "net/url"
7
+ "path"
8
+ "reflect"
9
+ "regexp"
10
+ "slices"
11
+ "strings"
12
+
13
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14
+ "k8s.io/apimachinery/pkg/runtime/schema"
15
+ )
16
+
17
+ const (
18
+ ArgoOwnerAnnotation = "delivery.soha.io/argocd-owner"
19
+ ArgoOperationAnnotation = "delivery.soha.io/argocd-operation"
20
+ )
21
+
22
+ var argoApplications = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
23
+ var argoImageOverride = regexp.MustCompile(`^(?:([A-Za-z0-9][A-Za-z0-9._:/-]*)=)?([A-Za-z0-9][A-Za-z0-9._:/-]*)@sha256:[a-f0-9]{64}$`)
24
+
25
+ // ParseArgoImageOverride accepts Kustomize's image@digest and name=image@digest forms.
26
+ func ParseArgoImageOverride(value string) (name, image string, err error) {
27
+ parts := argoImageOverride.FindStringSubmatch(value)
28
+ if len(parts) != 3 || strings.LastIndex(parts[2], ":") > strings.LastIndex(parts[2], "/") {
29
+ return "", "", fmt.Errorf("GitOps image overrides require an immutable image digest without a tag")
30
+ }
31
+ name = parts[1]
32
+ if name == "" {
33
+ name = parts[2]
34
+ }
35
+ _, image, found := strings.Cut(value, "=")
36
+ if !found {
37
+ image = value
38
+ }
39
+ return name, image, nil
40
+ }
41
+
42
+ func IsArgoApplication(object *unstructured.Unstructured) bool {
43
+ return object != nil && object.GetAPIVersion() == "argoproj.io/v1alpha1" && object.GetKind() == "Application"
44
+ }
45
+
46
+ // ValidateArgoApplication defines the supported, single-namespace GitOps path.
47
+ // AppProject and repository credentials remain administrator-managed in Argo CD.
48
+ func ValidateArgoApplication(object *unstructured.Unstructured) error {
49
+ if !IsArgoApplication(object) || object.GetNamespace() == "" || object.GetName() == "" {
50
+ return fmt.Errorf("GitOps requires a namespaced Argo CD Application")
51
+ }
52
+ if object.GetDeletionTimestamp() != nil || len(object.GetOwnerReferences()) != 0 || len(object.GetFinalizers()) != 0 {
53
+ return fmt.Errorf("GitOps Application must be an independent, non-deleting root without cascading deletion")
54
+ }
55
+ for key := range object.GetAnnotations() {
56
+ if strings.HasPrefix(key, "argocd.argoproj.io/") {
57
+ return fmt.Errorf("GitOps Application must not override controller behavior with annotations")
58
+ }
59
+ }
60
+ spec, _, err := unstructured.NestedMap(object.Object, "spec")
61
+ if err != nil {
62
+ return err
63
+ }
64
+ if err := argoAllowedFields(spec, "project", "source", "destination", "syncPolicy"); err != nil {
65
+ return err
66
+ }
67
+ if argoString(object, "spec", "project") == "" || argoString(object, "spec", "project") == "default" {
68
+ return fmt.Errorf("GitOps requires a dedicated restricted AppProject")
69
+ }
70
+ destination, _, err := unstructured.NestedStringMap(object.Object, "spec", "destination")
71
+ if err != nil || len(destination) != 2 || destination["namespace"] != object.GetNamespace() || destination["server"] != "https://kubernetes.default.svc" {
72
+ return fmt.Errorf("GitOps destination must be this cluster and the Application namespace")
73
+ }
74
+ source, _, err := unstructured.NestedMap(object.Object, "spec", "source")
75
+ if err != nil {
76
+ return err
77
+ }
78
+ if err := argoAllowedFields(source, "repoURL", "targetRevision", "path", "kustomize"); err != nil {
79
+ return err
80
+ }
81
+ revision := argoString(object, "spec", "source", "targetRevision")
82
+ if decoded, err := hex.DecodeString(revision); err != nil || len(decoded) != 20 && len(decoded) != 32 {
83
+ return fmt.Errorf("GitOps targetRevision must be a full immutable Git commit")
84
+ }
85
+ repository, err := url.Parse(argoString(object, "spec", "source", "repoURL"))
86
+ if err != nil || repository.Host == "" || repository.User != nil || repository.RawQuery != "" || repository.Fragment != "" || (repository.Scheme != "https" && repository.Scheme != "http" && repository.Scheme != "ssh") {
87
+ return fmt.Errorf("GitOps repository must be an HTTP(S) or SSH URL without embedded credentials")
88
+ }
89
+ directory := argoString(object, "spec", "source", "path")
90
+ if directory == "" || path.IsAbs(directory) || path.Clean(directory) != directory || directory == ".." || strings.HasPrefix(directory, "../") || strings.Contains(directory, "\\") {
91
+ return fmt.Errorf("GitOps path must stay within the pinned repository")
92
+ }
93
+ kustomize, _, err := unstructured.NestedMap(object.Object, "spec", "source", "kustomize")
94
+ if err != nil {
95
+ return err
96
+ }
97
+ if err := argoAllowedFields(kustomize, "images", "namespace"); err != nil {
98
+ return err
99
+ }
100
+ if argoString(object, "spec", "source", "kustomize", "namespace") != object.GetNamespace() {
101
+ return fmt.Errorf("GitOps Kustomize namespace must be explicitly fixed to the delivery namespace")
102
+ }
103
+ images, _, err := unstructured.NestedStringSlice(object.Object, "spec", "source", "kustomize", "images")
104
+ if err != nil || len(images) == 0 || len(images) > 16 {
105
+ return fmt.Errorf("GitOps requires 1 to 16 fixed Kustomize image overrides")
106
+ }
107
+ imageNames := map[string]bool{}
108
+ for _, image := range images {
109
+ name, _, err := ParseArgoImageOverride(image)
110
+ if err != nil {
111
+ return err
112
+ }
113
+ if imageNames[name] {
114
+ return fmt.Errorf("GitOps image overrides must use unique image names")
115
+ }
116
+ imageNames[name] = true
117
+ }
118
+ policy, _, err := unstructured.NestedMap(object.Object, "spec", "syncPolicy")
119
+ if err != nil {
120
+ return err
121
+ }
122
+ if err := argoAllowedFields(policy, "syncOptions"); err != nil {
123
+ return err
124
+ }
125
+ options, _, err := unstructured.NestedStringSlice(object.Object, "spec", "syncPolicy", "syncOptions")
126
+ if err != nil || !reflect.DeepEqual(options, []string{"FailOnSharedResource=true"}) {
127
+ return fmt.Errorf("GitOps requires manual sync and FailOnSharedResource=true")
128
+ }
129
+ return nil
130
+ }
131
+
132
+ func argoAllowedFields(object map[string]any, allowed ...string) error {
133
+ for key := range object {
134
+ if !slices.Contains(allowed, key) {
135
+ return fmt.Errorf("unsupported GitOps field %q", key)
136
+ }
137
+ }
138
+ return nil
139
+ }
140
+
141
+ func argoString(object *unstructured.Unstructured, fields ...string) string {
142
+ value, _, _ := unstructured.NestedString(object.Object, fields...)
143
+ return value
144
+ }
145
+
146
+ func argoOperationID(object *unstructured.Unstructured, fields ...string) string {
147
+ items, _, _ := unstructured.NestedSlice(object.Object, append(fields, "info")...)
148
+ for _, raw := range items {
149
+ item, _ := raw.(map[string]any)
150
+ if item["name"] == ArgoOperationAnnotation {
151
+ value, _ := item["value"].(string)
152
+ return value
153
+ }
154
+ }
155
+ return ""
156
+ }
157
+
158
+ var argoResourceTypes = map[string]schema.GroupVersionResource{
159
+ "apps/Deployment": {Group: "apps", Version: "v1", Resource: "deployments"},
160
+ "apps/StatefulSet": {Group: "apps", Version: "v1", Resource: "statefulsets"},
161
+ "/Service": {Version: "v1", Resource: "services"},
162
+ "/ConfigMap": {Version: "v1", Resource: "configmaps"},
163
+ "/Secret": {Version: "v1", Resource: "secrets"},
164
+ "/ServiceAccount": {Version: "v1", Resource: "serviceaccounts"},
165
+ "/PersistentVolumeClaim": {Version: "v1", Resource: "persistentvolumeclaims"},
166
+ }
@@ -0,0 +1,142 @@
1
+ package runtime
2
+
3
+ import (
4
+ "context"
5
+ "encoding/json"
6
+ "errors"
7
+ "net/url"
8
+ "os"
9
+ "strings"
10
+ "testing"
11
+ "time"
12
+
13
+ "github.com/google/uuid"
14
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
15
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
17
+ "k8s.io/apimachinery/pkg/runtime/schema"
18
+ "k8s.io/apimachinery/pkg/util/wait"
19
+ "k8s.io/client-go/dynamic"
20
+ "k8s.io/client-go/kubernetes"
21
+ "k8s.io/client-go/tools/clientcmd"
22
+ )
23
+
24
+ func TestArgoCDWithKubernetes(t *testing.T) {
25
+ kubeconfig := os.Getenv("SOHA_ARGOCD_TEST_KUBECONFIG")
26
+ if kubeconfig == "" {
27
+ t.Skip("set explicit loopback kubeconfig and SOHA_ARGOCD_TEST_STATE for the private GitOps fixture")
28
+ }
29
+ config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
30
+ operatorRequire(t, err)
31
+ endpoint, err := url.Parse(config.Host)
32
+ if err != nil || endpoint.Hostname() != "127.0.0.1" {
33
+ t.Fatal("only the loopback fixture cluster is supported")
34
+ }
35
+ config.Timeout = 15 * time.Second
36
+ client, err := dynamic.NewForConfig(config)
37
+ operatorRequire(t, err)
38
+ kube, err := kubernetes.NewForConfig(config)
39
+ operatorRequire(t, err)
40
+ data, err := os.ReadFile(os.Getenv("SOHA_ARGOCD_TEST_STATE"))
41
+ operatorRequire(t, err)
42
+ var state map[string]json.RawMessage
43
+ operatorRequire(t, json.Unmarshal(data, &state))
44
+ value := func(key string) string {
45
+ var value string
46
+ operatorRequire(t, json.Unmarshal(state[key], &value))
47
+ return value
48
+ }
49
+ namespace, name := "soha-workflow-r6-gitops", "r6-gitops-runtime"
50
+ app := &unstructured.Unstructured{Object: map[string]any{"apiVersion": "argoproj.io/v1alpha1", "kind": "Application", "metadata": map[string]any{"name": name, "namespace": namespace}, "spec": map[string]any{"project": "r6-gitops", "source": map[string]any{"repoURL": value("repositoryURL"), "path": ".", "targetRevision": value("v1"), "kustomize": map[string]any{"namespace": namespace, "images": []any{"app=" + value("image")}}}, "destination": map[string]any{"server": "https://kubernetes.default.svc", "namespace": namespace}, "syncPolicy": map[string]any{"syncOptions": []any{"FailOnSharedResource=true"}}}}}
51
+ children := func(version string) []*unstructured.Unstructured {
52
+ var documents map[string][]*unstructured.Unstructured
53
+ operatorRequire(t, json.Unmarshal(state["documents"], &documents))
54
+ if len(documents[version]) != 2 {
55
+ t.Fatal("frozen fixture requires two source-derived child documents")
56
+ }
57
+ return documents[version]
58
+ }
59
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
60
+ defer cancel()
61
+ resources := client.Resource(argoApplications).Namespace(namespace)
62
+ options := metav1.PatchOptions{FieldManager: "soha-r6-gitops-integration"}
63
+ id := uuid.NewString()
64
+ preflight := options
65
+ preflight.DryRun = []string{metav1.DryRunAll}
66
+ _, err = ApplyArgoApplication(ctx, client, app, children("v1"), preflight, id+":preflight")
67
+ operatorRequire(t, err)
68
+ if _, err = resources.Get(ctx, name, metav1.GetOptions{}); !apierrors.IsNotFound(err) {
69
+ t.Fatal("preflight created an Application or fixture already exists")
70
+ }
71
+ t.Cleanup(func() {
72
+ if t.Failed() {
73
+ t.Log("failed fixture retained for diagnosis in its isolated namespace")
74
+ return
75
+ }
76
+ cleanup, stop := context.WithTimeout(context.Background(), 15*time.Second)
77
+ defer stop()
78
+ _ = resources.Delete(cleanup, name, metav1.DeleteOptions{})
79
+ for _, gvr := range []schema.GroupVersionResource{{Group: "apps", Version: "v1", Resource: "deployments"}, {Version: "v1", Resource: "services"}} {
80
+ _ = client.Resource(gvr).Namespace(namespace).Delete(cleanup, "r6-gitops-http", metav1.DeleteOptions{})
81
+ }
82
+ })
83
+ apply := func(version, operation string) {
84
+ operatorRequire(t, unstructured.SetNestedField(app.Object, value(version), "spec", "source", "targetRevision"))
85
+ live, err := ApplyArgoApplication(ctx, client, app, children(version), options, id+operation)
86
+ if err != nil {
87
+ var status any
88
+ if live != nil {
89
+ status = live.Object["status"]
90
+ }
91
+ t.Fatalf("sync %s: %v; status=%v", version, err, status)
92
+ }
93
+ err = wait.PollUntilContextTimeout(ctx, time.Second, 45*time.Second, true, func(ctx context.Context) (bool, error) {
94
+ live, err := resources.Get(ctx, name, metav1.GetOptions{})
95
+ if err != nil {
96
+ return false, err
97
+ }
98
+ observed, err := ObserveArgoApplication(ctx, client, app, live, children(version), options.FieldManager)
99
+ if err != nil {
100
+ return false, err
101
+ }
102
+ return observed.Health == "healthy" && observed.OperationID == id+operation && observed.Revision == value(version), nil
103
+ })
104
+ operatorRequire(t, err)
105
+ body, err := kube.CoreV1().RESTClient().Get().Namespace(namespace).Resource("services").Name("r6-gitops-http:8080").SubResource("proxy").DoRaw(ctx)
106
+ if err != nil || strings.TrimSpace(string(body)) != "gitops-"+version {
107
+ t.Fatalf("actual HTTP %s: %q %v", version, body, err)
108
+ }
109
+ t.Logf("Argo operation %s applied commit %s with owned, healthy resources and actual HTTP %s", operation, value(version), version)
110
+ }
111
+ apply("v1", ":deploy")
112
+ apply("v2", ":upgrade")
113
+ apply("v1", ":rollback")
114
+ operatorRequire(t, unstructured.SetNestedField(app.Object, value("hold"), "spec", "source", "targetRevision"))
115
+ stopping, stop := context.WithCancel(ctx)
116
+ observedHold := make(chan error, 1)
117
+ go func() {
118
+ err := wait.PollUntilContextTimeout(ctx, time.Second, 45*time.Second, true, func(ctx context.Context) (bool, error) {
119
+ live, err := resources.Get(ctx, name, metav1.GetOptions{})
120
+ if err != nil {
121
+ return false, err
122
+ }
123
+ return argoOperationID(live, "status", "operationState", "operation") == id+":cancel" && argoString(live, "status", "operationState", "phase") == "Running", nil
124
+ })
125
+ observedHold <- err
126
+ stop()
127
+ }()
128
+ live, err := ApplyArgoApplication(stopping, client, app, children("hold"), options, id+":cancel")
129
+ operatorRequire(t, <-observedHold)
130
+ if !errors.Is(err, context.Canceled) || live == nil || argoOperationActive(live) {
131
+ t.Fatalf("cancel was not confirmed: %v", err)
132
+ }
133
+ t.Log("controller confirmed cancellation; restoring through a new operation")
134
+ apply("v1", ":restore")
135
+ // The direct writer cannot adopt a resource managed by Argo CD, even with force.
136
+ deployment, err := client.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}).Namespace(namespace).Get(ctx, "r6-gitops-http", metav1.GetOptions{})
137
+ operatorRequire(t, err)
138
+ if ValidateDirectManifestOwner(deployment) == nil {
139
+ t.Fatal("direct writer accepted Argo CD-owned resource")
140
+ }
141
+ t.Logf("real Argo CD GitOps lifecycle passed for %s/%s", namespace, name)
142
+ }