@opensoha/contracts 0.1.26 → 0.1.27

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 (129) hide show
  1. package/COMPATIBILITY.md +10 -0
  2. package/auth/catalog_generated.go +2 -2
  3. package/auth/permission-catalog.json +481 -1
  4. package/compat/json-schema-ecosystem-0.1.0.json +1 -1
  5. package/compat/openapi-loose-boundaries-0.1.0.json +1 -1
  6. package/endpointcontrol/decode.go +64 -0
  7. package/endpointcontrol/models.go +138 -0
  8. package/endpointcontrol/models_test.go +51 -0
  9. package/endpointcontrol/protocol.schema.json +397 -0
  10. package/fixtures/json-schema/endpoint-controls/invalid/controls.json +16 -0
  11. package/fixtures/json-schema/endpoint-controls/invalid/missing-channel-decision.json +15 -0
  12. package/fixtures/json-schema/endpoint-controls/invalid/receipt-content.json +36 -0
  13. package/fixtures/json-schema/endpoint-controls/valid/controls.json +16 -0
  14. package/fixtures/json-schema/endpoint-controls/valid/receipt.json +35 -0
  15. package/fixtures/json-schema/network-runtime-protocol/invalid/eap-activate-bad-fingerprint.json +14 -0
  16. package/fixtures/json-schema/network-runtime-protocol/invalid/eap-gateway-request.json +14 -0
  17. package/fixtures/json-schema/network-runtime-protocol/invalid/eap-private-key-request.json +15 -0
  18. package/fixtures/json-schema/network-runtime-protocol/invalid/eap-profile-without-eap-credential.json +22 -0
  19. package/fixtures/json-schema/network-runtime-protocol/invalid/endpoint-dns-observation.json +25 -0
  20. package/fixtures/json-schema/network-runtime-protocol/invalid/gateway-presence.json +22 -0
  21. package/fixtures/json-schema/network-runtime-protocol/invalid/mihomo-protection-zero-version.json +45 -0
  22. package/fixtures/json-schema/network-runtime-protocol/invalid/presence-raw-key.json +23 -0
  23. package/fixtures/json-schema/network-runtime-protocol/invalid/presence-zero-sequence.json +22 -0
  24. package/fixtures/json-schema/network-runtime-protocol/invalid/workload-wide-vpn.json +17 -0
  25. package/fixtures/json-schema/network-runtime-protocol/valid/eap-certificate-activate.json +14 -0
  26. package/fixtures/json-schema/network-runtime-protocol/valid/eap-certificate-request.json +14 -0
  27. package/fixtures/json-schema/network-runtime-protocol/valid/eap-profile-enrollment.json +24 -0
  28. package/fixtures/json-schema/network-runtime-protocol/valid/endpoint-presence.json +22 -0
  29. package/fixtures/json-schema/network-runtime-protocol/valid/ipv6-configuration.json +102 -0
  30. package/fixtures/json-schema/network-runtime-protocol/valid/mihomo-protected-policy.json +45 -0
  31. package/fixtures/json-schema/network-runtime-protocol/valid/site-dns-ipv6.json +25 -0
  32. package/fixtures/json-schema/network-runtime-protocol/valid/workload-direct-ztna.json +20 -0
  33. package/fixtures/openapi/DLPShadowDecision/invalid/step-up-type.json +14 -0
  34. package/fixtures/openapi/DLPShadowDecision/valid/archive-v2.json +14 -0
  35. package/fixtures/openapi/DLPShadowDecision/valid/step-up.json +14 -0
  36. package/fixtures/openapi/DLPShadowInput/invalid/self-reported-result.json +7 -0
  37. package/fixtures/openapi/DLPShadowInput/invalid/too-many-samples.json +14 -0
  38. package/fixtures/openapi/DLPShadowInput/valid/bounded.json +7 -0
  39. package/fixtures/openapi/DockerVolumeDLPAppealInput/invalid/p4-response.json +3 -0
  40. package/fixtures/openapi/DockerVolumeDLPAppealInput/valid/p4-response.json +3 -0
  41. package/fixtures/openapi/DockerVolumeDLPAppealReviewInput/invalid/p4-response.json +5 -0
  42. package/fixtures/openapi/DockerVolumeDLPAppealReviewInput/valid/p4-response.json +5 -0
  43. package/fixtures/openapi/DockerVolumeDLPAuthorizationInput/invalid/p4-response.json +3 -0
  44. package/fixtures/openapi/DockerVolumeDLPAuthorizationInput/valid/p4-response.json +3 -0
  45. package/fixtures/openapi/DockerVolumeDLPContent/invalid/archive-unsupported-channel.json +9 -0
  46. package/fixtures/openapi/DockerVolumeDLPContent/invalid/p4.json +6 -0
  47. package/fixtures/openapi/DockerVolumeDLPContent/invalid/step-up-audit.json +9 -0
  48. package/fixtures/openapi/DockerVolumeDLPContent/invalid/unavailable-engine.json +9 -0
  49. package/fixtures/openapi/DockerVolumeDLPContent/invalid/unknown-data-level.json +12 -0
  50. package/fixtures/openapi/DockerVolumeDLPContent/valid/classified-rule.json +16 -0
  51. package/fixtures/openapi/DockerVolumeDLPContent/valid/p4.json +8 -0
  52. package/fixtures/openapi/DockerVolumeDLPContent/valid/step-up.json +9 -0
  53. package/fixtures/openapi/DockerVolumeDLPDecision/invalid/p4.json +9 -0
  54. package/fixtures/openapi/DockerVolumeDLPDecision/invalid/step-up-type.json +12 -0
  55. package/fixtures/openapi/DockerVolumeDLPDecision/valid/archive-v2.json +12 -0
  56. package/fixtures/openapi/DockerVolumeDLPDecision/valid/p4.json +11 -0
  57. package/fixtures/openapi/DockerVolumeDLPDecision/valid/step-up.json +12 -0
  58. package/fixtures/openapi/DockerVolumeDLPSaveInput/invalid/p4.json +11 -0
  59. package/fixtures/openapi/DockerVolumeDLPSaveInput/valid/p4.json +11 -0
  60. package/fixtures/openapi/DockerVolumeFileReadInput/invalid/p4-response.json +5 -0
  61. package/fixtures/openapi/DockerVolumeFileReadInput/valid/p4-response.json +4 -0
  62. package/fixtures/openapi/EndpointDeviceInput/invalid/reported-management.json +5 -0
  63. package/fixtures/openapi/EndpointDeviceInput/valid/management-state.json +5 -0
  64. package/fixtures/openapi/EndpointDeviceRegistrationInput/invalid/self-promote-management.json +5 -0
  65. package/fixtures/openapi/ManagedContentPasteInput/invalid/boundary.json +5 -0
  66. package/fixtures/openapi/ManagedContentPasteInput/valid/boundary.json +6 -0
  67. package/fixtures/openapi/ManagedContentPolicy/invalid/self-reported-allow.json +8 -0
  68. package/fixtures/openapi/ManagedContentPolicy/invalid/step-up-audit.json +8 -0
  69. package/fixtures/openapi/ManagedContentPolicy/invalid/unknown-extractor.json +8 -0
  70. package/fixtures/openapi/ManagedContentPolicy/valid/archive-v2.json +8 -0
  71. package/fixtures/openapi/ManagedContentPolicy/valid/network-and-level.json +10 -0
  72. package/fixtures/openapi/ManagedContentPolicy/valid/step-up.json +8 -0
  73. package/fixtures/openapi/ManagedContentReviewInput/invalid/boundary.json +5 -0
  74. package/fixtures/openapi/ManagedContentReviewInput/valid/boundary.json +5 -0
  75. package/fixtures/openapi/ManagedContentSaveInput/invalid/boundary.json +14 -0
  76. package/fixtures/openapi/ManagedContentSaveInput/valid/boundary.json +16 -0
  77. package/fixtures/openapi/ManagedContentUploadInput/invalid/boundary.json +7 -0
  78. package/fixtures/openapi/ManagedContentUploadInput/valid/boundary.json +6 -0
  79. package/fixtures/openapi/NetworkAccessPolicyInput/invalid/unknown-management-state.json +35 -0
  80. package/fixtures/openapi/NetworkAccessPolicyInput/valid/managed-device.json +35 -0
  81. package/fixtures/openapi/NetworkAdmissionPreviewRequest/invalid/deviceId-characters.json +4 -0
  82. package/fixtures/openapi/NetworkAdmissionPreviewRequest/invalid/nasBindingId-characters.json +4 -0
  83. package/fixtures/openapi/NetworkAdmissionPreviewRequest/invalid/subject-override.json +1 -0
  84. package/fixtures/openapi/NetworkAdmissionPreviewRequest/valid/device-nas.json +1 -0
  85. package/fixtures/openapi/NetworkAdmissionPreviewResult/invalid/missing-attributeVersion.json +13 -0
  86. package/fixtures/openapi/NetworkAdmissionPreviewResult/invalid/missing-evaluatedAt.json +13 -0
  87. package/fixtures/openapi/NetworkAdmissionPreviewResult/valid/versioned.json +14 -0
  88. package/fixtures/openapi/NetworkConflictAnalysisRequest/invalid/missing-runtime-ranges.json +1 -0
  89. package/fixtures/openapi/NetworkConflictAnalysisRequest/invalid/unknown-source-type.json +9 -0
  90. package/fixtures/openapi/NetworkConflictAnalysisRequest/valid/empty-runtime-ranges.json +3 -0
  91. package/fixtures/openapi/NetworkConflictAnalysisRequest/{invalid → valid}/ipv6-range.json +1 -1
  92. package/fixtures/openapi/NetworkEAPServerTrust/invalid/no-server-validation.json +5 -0
  93. package/fixtures/openapi/NetworkEAPServerTrust/valid/named-server.json +9 -0
  94. package/fixtures/openapi/NetworkEndpointResponsePolicyInput/invalid/all-sites-implicit.json +45 -0
  95. package/fixtures/openapi/NetworkEndpointResponsePolicyInput/invalid/unbounded-stages.json +54 -0
  96. package/fixtures/openapi/NetworkEndpointResponsePolicyInput/invalid/zero-heartbeat.json +47 -0
  97. package/fixtures/openapi/NetworkEndpointResponsePolicyInput/valid/staged-protection.json +47 -0
  98. package/fixtures/openapi/NetworkEndpointResponseRestoreInput/invalid/missing-reason.json +3 -0
  99. package/fixtures/openapi/NetworkEndpointResponseRestoreInput/valid/expected-current.json +4 -0
  100. package/fixtures/openapi/NetworkPolicyPreviewResult/invalid/attribute-version.json +1 -0
  101. package/fixtures/openapi/NetworkPolicyPreviewResult/valid/admission-version.json +1 -0
  102. package/fixtures/openapi/NetworkPolicySubjects/invalid/duplicate-role.json +1 -0
  103. package/fixtures/openapi/NetworkPolicySubjects/invalid/role-characters.json +5 -0
  104. package/fixtures/openapi/NetworkPolicySubjects/valid/role-bound.json +1 -0
  105. package/fixtures/openapi/NetworkProxyConfigurationInput/invalid/missing-content.json +1 -0
  106. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/invalid/bad-binding-id.json +9 -0
  107. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/invalid/empty-content.json +1 -0
  108. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/valid/binding-only.json +11 -0
  109. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/valid/clear-binding-disabled.json +5 -0
  110. package/fixtures/openapi/NetworkProxyConfigurationPatchInput/valid/enabled.json +1 -0
  111. package/fixtures/openapi/NetworkSiteInput/invalid/unknown-governance.json +6 -0
  112. package/fixtures/openapi/NetworkSiteInput/valid/restricted-unmanaged.json +6 -0
  113. package/gen/go/sohaapi/client.go +1 -1
  114. package/gen/go/sohaapi/proxy_configuration_test.go +38 -0
  115. package/gen/go/sohaapi/types.go +2825 -196
  116. package/gen/ts/sohaapi/index.ts +14533 -9883
  117. package/network/README.md +34 -0
  118. package/network/network-radius-accounting.schema.json +4 -1
  119. package/network/network-runtime-protocol.schema.json +2166 -317
  120. package/network/proxy-runtime.schema.json +482 -67
  121. package/openapi/network_control_test.go +64 -0
  122. package/openapi/soha-api.json +52989 -45064
  123. package/openapi/soha-api.yaml +26472 -20872
  124. package/openapi/spec_generated.go +3 -3
  125. package/package.json +6 -3
  126. package/proxytransport/handler.go +271 -0
  127. package/proxytransport/probe.go +30 -0
  128. package/proxytransport/scope.go +89 -0
  129. package/proxytransport/transport_test.go +60 -0
package/network/README.md CHANGED
@@ -57,6 +57,11 @@ for authorization, routing, firewall configuration or accounting.
57
57
  reply attribute and require NAS accounting to echo it as `sessionId` when
58
58
  available. Session commands repeat the bound `subjectId` and `deviceId` so
59
59
  the adapter can target CoA/Disconnect without querying management APIs.
60
+ - Capability `nas-session-accounting-v1` enables the optional command target
61
+ `stationId`, `accountingSessionId` and `nasPortId`. For EAP-TLS, station identity
62
+ comes from saved authorization/accounting, never the opaque Soha device ID.
63
+ A runtime that cannot receive the required target binding must report
64
+ unsupported rather than send a command to a guessed station.
60
65
  - If a requested CoA is unsupported, issue a disconnect command when the NAS
61
66
  binding supports it; otherwise fail closed and record the unsupported
62
67
  enforcement result.
@@ -166,3 +171,32 @@ for authorization, routing, firewall configuration or accounting.
166
171
  Consumers must cover these rules with deterministic tests. Failure is
167
172
  fail-closed for control messages and bounded reject/drop-with-counter behavior
168
173
  for telemetry.
174
+
175
+ ## Endpoint presence and RADIUS response
176
+
177
+ - `endpoint.presence.request` goes to
178
+ `POST /api/network-control/v1/runtimes/{runtimeId}/endpoint:presence` using the
179
+ enrolled endpoint's control identity and `endpoint-presence-v1` capability.
180
+ `bootId` identifies the protected service incarnation; a new incarnation
181
+ starts a new sequence and retires the previous one. A duplicate must not
182
+ refresh server-side liveness. UI presence never implies a healthy service.
183
+ - `POST /api/network-control/v1/runtimes/{runtimeId}/nas:accounting` uses
184
+ the enrolled NAS control identity and the existing RADIUS Accounting schema.
185
+ The control plane checks NAS, subject, station and accounting-session binding
186
+ against the saved authorization before updating session evidence. The
187
+ separate ingest submission remains telemetry and cannot change admission.
188
+ - A Stop remains terminal for that Core session. A reused accounting ID,
189
+ conflicting binding, restart after Start, or missing Start remains unknown.
190
+ Received time alone cannot refresh an old observation. Accounting evidence
191
+ cannot authorize access or prove that a VLAN/ACL was applied.
192
+ - Persist response restrictions before delivering NAS commands. A new
193
+ authentication must observe every active restriction. Policy retirement
194
+ never removes an existing restriction, and restoration requires current
195
+ health plus authorization rather than replaying a previous allow decision.
196
+
197
+ ## Dedicated EAP certificates and native profiles
198
+
199
+ - `eap.certificate.request` uses `POST /api/network-control/v1/runtimes/{runtimeId}/eap:certificate`; `eap.certificate.activate.request` uses the same prefix with `eap:activate`. Both require the endpoint control identity and `eap-profile-v1` plus `radius-eap-tls-v1`. CSR private keys stay on the endpoint. Core binds the subject and device from enrollment and ignores CSR identity extensions.
200
+ - A dedicated EAP issuer signs only client authentication certificates with a `network-eap` URI. EAP certificates cannot authenticate control or ingest requests. Without an issuer, issuance is unavailable. Existing runtime certificates are accepted for legacy EAP only when enrollment explicitly declares `radius-eap-tls-v1`; ordinary control credentials do not imply EAP permission.
201
+ - Issuance is idempotent per runtime/request ID and CSR. Certificates start pending and cannot grant network access. After native installation and readback, activation atomically replaces the old EAP certificate under the parent runtime credential lock. A retired or expired parent invalidates every child. Replaying an older activation cannot revive it.
202
+ - `admission.profiles` is delivered only to `eap-profile-v1` endpoints, with exact EAP server names, CA certificates, subject and the dedicated active EAP certificate fingerprint. Configuration export, installation, 802.1X connection and actual NAS enforcement are separate results.
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://contracts.opensoha.dev/network/network-radius-accounting.schema.json",
4
4
  "title": "OpenSoha FreeRADIUS Accounting Adapter Request",
5
- "description": "A strict credential-free request converted by soha-ingest into one canonical radius.accounting event.",
5
+ "description": "A strict credential-free request converted by soha-ingest into telemetry. When submitted to network-control with an enrolled NAS identity, its NAS and session binding are independently verified before updating admission evidence.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -20,6 +20,9 @@
20
20
  "accountingSessionId": { "$ref": "#/$defs/Identifier" },
21
21
  "sessionId": { "$ref": "#/$defs/Identifier" },
22
22
  "nasId": { "$ref": "#/$defs/Identifier" },
23
+ "accountingDelaySeconds": { "type": "integer", "minimum": 0, "maximum": 86400 },
24
+ "observedVlanId": { "type": "integer", "minimum": 1, "maximum": 4094 },
25
+ "observedFilterId": { "type": "string", "minLength": 1, "maxLength": 128 },
23
26
  "nasPortId": { "type": "string", "minLength": 1, "maxLength": 128 },
24
27
  "userId": { "$ref": "#/$defs/Identifier" },
25
28
  "deviceId": { "$ref": "#/$defs/Identifier" },