@7h3/protocol 0.1.2 → 0.4.0

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 (230) hide show
  1. package/.dockerignore +19 -0
  2. package/.github/dependabot.yml +32 -0
  3. package/.github/workflows/ci.yml +31 -0
  4. package/.github/workflows/publish.yml +59 -0
  5. package/.github/workflows/scorecard.yml +37 -0
  6. package/7h3.example.yaml +125 -0
  7. package/CHANGELOG.md +92 -0
  8. package/CONTRIBUTING.md +82 -0
  9. package/Dockerfile +73 -0
  10. package/GOVERNANCE.md +62 -0
  11. package/README.md +323 -6
  12. package/SECURITY.md +70 -0
  13. package/bench-results/replay-cache-full-1777891033256.json +10 -0
  14. package/bench-results/replay-cache-full-1777896317488.json +10 -0
  15. package/bench-results/replay-cache-full-1777900993184.json +10 -0
  16. package/bench-results/replay-cache-full-1777901019285.json +10 -0
  17. package/bench-results/replay-cache-quick-1777870170126.json +10 -0
  18. package/bench-results/signature-profiles-quick-1775875160079.json +85 -0
  19. package/bench-results/signature-profiles-quick-1775983539716.json +85 -0
  20. package/bench-results/signature-profiles-quick-1776237913190.json +85 -0
  21. package/bench-results/wire-codecs-full-1777891019803.json +93 -0
  22. package/bench-results/wire-codecs-full-1777896260964.json +93 -0
  23. package/bench-results/wire-codecs-full-1777901004247.json +93 -0
  24. package/bench-results/wire-codecs-quick-1775972879056.json +93 -0
  25. package/bench-results/wire-codecs-quick-1775983541111.json +93 -0
  26. package/bench-results/wire-codecs-quick-1776237914299.json +93 -0
  27. package/bench-results/wire-codecs-quick-1777841285236.json +93 -0
  28. package/bench-results/wire-codecs-quick-1777841321772.json +93 -0
  29. package/bench-results/wire-codecs-quick-1777841330408.json +93 -0
  30. package/bench-results/wire-codecs-quick-1777852886082.json +93 -0
  31. package/bench-results/wire-codecs-quick-1777852988773.json +93 -0
  32. package/bench-results/wire-codecs-quick-1777870188095.json +93 -0
  33. package/bench-results/wire-codecs-quick-1777870263918.json +93 -0
  34. package/bench-results/wire-codecs-quick-1777870455034.json +93 -0
  35. package/bench-results/wire-codecs-quick-1778816163081.json +93 -0
  36. package/bench-results/wire-codecs-quick-1778843936130.json +93 -0
  37. package/bin/7h3.ts +385 -0
  38. package/conformance/7h3_v0_1.json +77 -0
  39. package/conformance/7h3_v0_1_binary.json +20 -0
  40. package/conformance/aip_v0_1_binary.json +20 -0
  41. package/docker-compose.yaml +77 -0
  42. package/docs/ADOPTION_PLAN.md +120 -0
  43. package/docs/AGENTS.md +77 -0
  44. package/docs/AIP_RFC_v0.1.md +97 -0
  45. package/docs/AI_DECISION_CARD.md +122 -0
  46. package/docs/AI_RUNTIME_POLICY.json +126 -0
  47. package/docs/AI_RUNTIME_POLICY.yaml +110 -0
  48. package/docs/BACKPRESSURE_TUNING.md +65 -0
  49. package/docs/BENCHMARK_CLAIM_MATRIX.md +42 -0
  50. package/docs/BENCHMARK_REPORT_TEMPLATE.md +169 -0
  51. package/docs/BINARY_CODEC_BENCH.md +23 -0
  52. package/docs/CLEAN_CLONE_RUNBOOK.md +36 -0
  53. package/docs/CLOCK_SKEW_POLICY.md +30 -0
  54. package/docs/DISTRIBUTED_REPLAY.md +142 -0
  55. package/docs/FUZZ_CAMPAIGN.md +121 -0
  56. package/docs/GATEWAY.md +195 -0
  57. package/docs/KEY_MANAGEMENT_POLICY.md +53 -0
  58. package/docs/KEY_REVOCATION.md +69 -0
  59. package/docs/MCP_WRAPPER.md +159 -0
  60. package/docs/MIGRATION_GUIDE.md +40 -0
  61. package/docs/OPERATORS.md +184 -0
  62. package/docs/PERF_REGRESSION_POLICY.md +34 -0
  63. package/docs/PROJECT_EXAMINATION_2026-05-31.md +219 -0
  64. package/docs/RELEASE_BENCHMARK_REPORT_2026-05-15.md +135 -0
  65. package/docs/RELEASE_GATE.md +25 -0
  66. package/docs/RELEASE_NOTES_v0.1.0.md +54 -0
  67. package/docs/SECURITY_REVIEW_2026-06-05.md +165 -0
  68. package/docs/TELEMETRY.md +41 -0
  69. package/docs/THREAT_MODEL.md +89 -0
  70. package/docs/VERSIONING_POLICY.md +30 -0
  71. package/docs/assets/banner.png +0 -0
  72. package/eslint.config.js +15 -0
  73. package/fuzz/ts/harness-decode.ts +136 -0
  74. package/fuzz/ts/harness-verify.ts +121 -0
  75. package/fuzz/ts/run.ts +35 -0
  76. package/mcp-server/README.md +38 -0
  77. package/mcp-server/package-lock.json +1187 -0
  78. package/mcp-server/package.json +35 -0
  79. package/mcp-server/src/index.ts +236 -0
  80. package/mcp-server/tsconfig.json +14 -0
  81. package/package.json +79 -13
  82. package/scripts/aip-framework-quickstart.ts +110 -0
  83. package/scripts/aip-mcp-gateway.ts +38 -0
  84. package/scripts/aip-mcp-wrap-demo.ts +72 -0
  85. package/scripts/aip-quickstart.ts +60 -0
  86. package/scripts/bench-diff.ts +118 -0
  87. package/scripts/bench-protocol-e2e.ts +937 -0
  88. package/scripts/bench-protocol-openloop.ts +1397 -0
  89. package/scripts/bench-replay-cache.ts +76 -0
  90. package/scripts/bench-signature-profiles.ts +180 -0
  91. package/scripts/bench-wire-codecs.ts +161 -0
  92. package/scripts/build-binary-conformance.ts +36 -0
  93. package/scripts/build-release-dashboard.ts +175 -0
  94. package/scripts/canary-rollout.ts +38 -0
  95. package/scripts/mcpGatewayCli.test.ts +116 -0
  96. package/scripts/prepare-aip-package.ts +88 -0
  97. package/scripts/regen-conformance-sigs.ts +18 -0
  98. package/scripts/release-gate.ts +19 -0
  99. package/scripts/validate-runtime-policy.ts +18 -0
  100. package/sdk/browser/index.test.ts +162 -0
  101. package/sdk/browser/index.ts +257 -0
  102. package/sdk/browser/package.json +13 -0
  103. package/sdk/go/go.mod +3 -0
  104. package/sdk/go/http.go +135 -0
  105. package/sdk/go/protocol.go +324 -0
  106. package/sdk/go/protocol_test.go +334 -0
  107. package/sdk/go/webhook.go +136 -0
  108. package/sdk/python/README.md +18 -0
  109. package/sdk/python/protocol_7h3/__init__.py +46 -0
  110. package/sdk/python/protocol_7h3/http.py +212 -0
  111. package/sdk/python/protocol_7h3/keys.py +149 -0
  112. package/sdk/python/protocol_7h3/protocol.py +525 -0
  113. package/sdk/python/protocol_7h3/queue.py +118 -0
  114. package/sdk/python/protocol_7h3/webhook.py +116 -0
  115. package/sdk/python/pyproject.toml +40 -0
  116. package/sdk/python/tests/test_conformance.py +110 -0
  117. package/sdk/python/tests/test_http.py +305 -0
  118. package/sdk/python/tests/test_keys.py +417 -0
  119. package/sdk/python/tests/test_queue.py +120 -0
  120. package/sdk/python/tests/test_webhook.py +345 -0
  121. package/sdk/rust/Cargo.lock +371 -0
  122. package/sdk/rust/Cargo.toml +25 -0
  123. package/sdk/rust/README.md +31 -0
  124. package/sdk/rust/fuzz/Cargo.toml +29 -0
  125. package/sdk/rust/fuzz/fuzz_targets/fuzz_canonicalize.rs +46 -0
  126. package/sdk/rust/fuzz/fuzz_targets/fuzz_decode.rs +11 -0
  127. package/sdk/rust/src/bin/aip_mcp_gateway.rs +59 -0
  128. package/sdk/rust/src/http.rs +145 -0
  129. package/sdk/rust/src/keys.rs +161 -0
  130. package/sdk/rust/src/lib.rs +688 -0
  131. package/sdk/rust/src/queue.rs +79 -0
  132. package/sdk/rust/src/webhook.rs +86 -0
  133. package/sdk/rust/tests/conformance.rs +148 -0
  134. package/sdk/rust/tests/gateway.rs +130 -0
  135. package/sdk/rust/tests/http_webhook_queue.rs +201 -0
  136. package/sdk/rust/tests/keys.rs +189 -0
  137. package/src/agentAdapter.test.ts +48 -0
  138. package/src/agentAdapter.ts +56 -0
  139. package/src/auditLog.test.ts +145 -0
  140. package/src/auditLog.ts +147 -0
  141. package/src/conformance.test.ts +136 -0
  142. package/src/conformanceVectors.ts +99 -0
  143. package/src/frameworkAdapters.test.ts +290 -0
  144. package/src/frameworkAdapters.ts +261 -0
  145. package/src/gateway.test.ts +343 -0
  146. package/src/gateway.ts +171 -0
  147. package/src/grpcBinding.test.ts +211 -0
  148. package/src/grpcBinding.ts +103 -0
  149. package/src/httpBinding.test.ts +376 -0
  150. package/src/httpBinding.ts +163 -0
  151. package/src/index.ts +32 -0
  152. package/src/keyInfra.test.ts +278 -0
  153. package/src/keyInfra.ts +228 -0
  154. package/src/keyRegistry.ts +59 -0
  155. package/src/keyRotation.test.ts +78 -0
  156. package/src/keyRotation.ts +72 -0
  157. package/src/mcpGateway.test.ts +129 -0
  158. package/src/mcpGateway.ts +250 -0
  159. package/src/mcpTransports.test.ts +92 -0
  160. package/src/mcpTransports.ts +169 -0
  161. package/src/mcpWrapper.test.ts +179 -0
  162. package/src/mcpWrapper.ts +206 -0
  163. package/src/policyEnforcer.test.ts +99 -0
  164. package/src/policyEnforcer.ts +169 -0
  165. package/src/policyTelemetryFeedback.test.ts +25 -0
  166. package/src/policyTelemetryFeedback.ts +38 -0
  167. package/src/protocol.bench.ts +37 -0
  168. package/src/protocol.test.ts +155 -0
  169. package/src/protocol.ts +413 -0
  170. package/src/protocolAgent.test.ts +105 -0
  171. package/src/protocolAgent.ts +169 -0
  172. package/src/protocolBinary.test.ts +165 -0
  173. package/src/protocolBinary.ts +312 -0
  174. package/src/protocolCapabilities.ts +70 -0
  175. package/src/protocolFuzz.advanced.test.ts +235 -0
  176. package/src/protocolFuzz.test.ts +111 -0
  177. package/src/protocolNegative.test.ts +97 -0
  178. package/src/protocolReplay.test.ts +71 -0
  179. package/src/protocolReplay.ts +194 -0
  180. package/src/protocolTransport.test.ts +556 -0
  181. package/src/protocolTransport.ts +483 -0
  182. package/src/queueBinding.test.ts +130 -0
  183. package/src/queueBinding.ts +102 -0
  184. package/src/rateLimiter.test.ts +96 -0
  185. package/src/rateLimiter.ts +46 -0
  186. package/src/redisClient.ts +140 -0
  187. package/src/redisIntegration.test.ts +134 -0
  188. package/src/replayStores.test.ts +141 -0
  189. package/src/replayStores.ts +82 -0
  190. package/src/revocation.test.ts +98 -0
  191. package/src/revocation.ts +0 -0
  192. package/src/routePolicy.test.ts +87 -0
  193. package/src/routePolicy.ts +72 -0
  194. package/src/runtimePolicy.test.ts +49 -0
  195. package/src/runtimePolicy.ts +81 -0
  196. package/src/runtimePolicyManager.test.ts +29 -0
  197. package/src/runtimePolicyManager.ts +50 -0
  198. package/src/runtimePolicyPresets.ts +43 -0
  199. package/src/signedResponse.test.ts +111 -0
  200. package/src/signedResponse.ts +83 -0
  201. package/src/webhookBinding.test.ts +144 -0
  202. package/src/webhookBinding.ts +115 -0
  203. package/src/wsBinding.test.ts +221 -0
  204. package/src/wsBinding.ts +100 -0
  205. package/tsconfig.json +15 -0
  206. package/tsconfig.lib.json +23 -0
  207. package/vite.lib.config.ts +16 -0
  208. package/agentAdapter.d.ts +0 -26
  209. package/conformanceVectors.d.ts +0 -20
  210. package/frameworkAdapters.d.ts +0 -72
  211. package/index.d.ts +0 -20
  212. package/index.js +0 -1702
  213. package/keyRotation.d.ts +0 -20
  214. package/mcpGateway.d.ts +0 -37
  215. package/mcpTransports.d.ts +0 -62
  216. package/mcpWrapper.d.ts +0 -83
  217. package/policyEnforcer.d.ts +0 -50
  218. package/policyTelemetryFeedback.d.ts +0 -11
  219. package/protocol.d.ts +0 -66
  220. package/protocolAgent.d.ts +0 -58
  221. package/protocolBinary.d.ts +0 -8
  222. package/protocolCapabilities.d.ts +0 -24
  223. package/protocolReplay.d.ts +0 -35
  224. package/protocolTransport.d.ts +0 -73
  225. package/redisClient.d.ts +0 -49
  226. package/replayStores.d.ts +0 -32
  227. package/revocation.d.ts +0 -71
  228. package/runtimePolicy.d.ts +0 -24
  229. package/runtimePolicyManager.d.ts +0 -15
  230. package/runtimePolicyPresets.d.ts +0 -11
@@ -0,0 +1,93 @@
1
+ {
2
+ "generatedAt": "2026-05-04T04:54:15.034Z",
3
+ "profile": "quick",
4
+ "iterations": 10000,
5
+ "payloadSizes": [
6
+ 256,
7
+ 1024,
8
+ 4096,
9
+ 16384
10
+ ],
11
+ "results": [
12
+ {
13
+ "profile": "quick",
14
+ "payloadBytes": 256,
15
+ "format": "compact-json",
16
+ "encodedBytes": 498,
17
+ "encodeUsPerOp": 0.71,
18
+ "decodeUsPerOp": 0.763,
19
+ "encodeOpsPerSecond": 1408024.132,
20
+ "decodeOpsPerSecond": 1309799.897
21
+ },
22
+ {
23
+ "profile": "quick",
24
+ "payloadBytes": 256,
25
+ "format": "binary",
26
+ "encodedBytes": 426,
27
+ "encodeUsPerOp": 5.978,
28
+ "decodeUsPerOp": 3.083,
29
+ "encodeOpsPerSecond": 167275.012,
30
+ "decodeOpsPerSecond": 324371.448
31
+ },
32
+ {
33
+ "profile": "quick",
34
+ "payloadBytes": 1024,
35
+ "format": "compact-json",
36
+ "encodedBytes": 1269,
37
+ "encodeUsPerOp": 1.175,
38
+ "decodeUsPerOp": 1.123,
39
+ "encodeOpsPerSecond": 850943.973,
40
+ "decodeOpsPerSecond": 890390.523
41
+ },
42
+ {
43
+ "profile": "quick",
44
+ "payloadBytes": 1024,
45
+ "format": "binary",
46
+ "encodedBytes": 1197,
47
+ "encodeUsPerOp": 7.602,
48
+ "decodeUsPerOp": 2.673,
49
+ "encodeOpsPerSecond": 131550.669,
50
+ "decodeOpsPerSecond": 374088.197
51
+ },
52
+ {
53
+ "profile": "quick",
54
+ "payloadBytes": 4096,
55
+ "format": "compact-json",
56
+ "encodedBytes": 4341,
57
+ "encodeUsPerOp": 2.145,
58
+ "decodeUsPerOp": 1.475,
59
+ "encodeOpsPerSecond": 466137.206,
60
+ "decodeOpsPerSecond": 677827.411
61
+ },
62
+ {
63
+ "profile": "quick",
64
+ "payloadBytes": 4096,
65
+ "format": "binary",
66
+ "encodedBytes": 4269,
67
+ "encodeUsPerOp": 11.99,
68
+ "decodeUsPerOp": 3.082,
69
+ "encodeOpsPerSecond": 83403.322,
70
+ "decodeOpsPerSecond": 324446.569
71
+ },
72
+ {
73
+ "profile": "quick",
74
+ "payloadBytes": 16384,
75
+ "format": "compact-json",
76
+ "encodedBytes": 16632,
77
+ "encodeUsPerOp": 6.84,
78
+ "decodeUsPerOp": 3.734,
79
+ "encodeOpsPerSecond": 146192.782,
80
+ "decodeOpsPerSecond": 267827.632
81
+ },
82
+ {
83
+ "profile": "quick",
84
+ "payloadBytes": 16384,
85
+ "format": "binary",
86
+ "encodedBytes": 16560,
87
+ "encodeUsPerOp": 13.639,
88
+ "decodeUsPerOp": 3.783,
89
+ "encodeOpsPerSecond": 73320.881,
90
+ "decodeOpsPerSecond": 264353.217
91
+ }
92
+ ]
93
+ }
@@ -0,0 +1,93 @@
1
+ {
2
+ "generatedAt": "2026-05-15T03:36:03.081Z",
3
+ "profile": "quick",
4
+ "iterations": 10000,
5
+ "payloadSizes": [
6
+ 256,
7
+ 1024,
8
+ 4096,
9
+ 16384
10
+ ],
11
+ "results": [
12
+ {
13
+ "profile": "quick",
14
+ "payloadBytes": 256,
15
+ "format": "compact-json",
16
+ "encodedBytes": 498,
17
+ "encodeUsPerOp": 0.793,
18
+ "decodeUsPerOp": 1.03,
19
+ "encodeOpsPerSecond": 1261085.096,
20
+ "decodeOpsPerSecond": 971138.351
21
+ },
22
+ {
23
+ "profile": "quick",
24
+ "payloadBytes": 256,
25
+ "format": "binary",
26
+ "encodedBytes": 426,
27
+ "encodeUsPerOp": 5.529,
28
+ "decodeUsPerOp": 2.827,
29
+ "encodeOpsPerSecond": 180848.871,
30
+ "decodeOpsPerSecond": 353762.342
31
+ },
32
+ {
33
+ "profile": "quick",
34
+ "payloadBytes": 1024,
35
+ "format": "compact-json",
36
+ "encodedBytes": 1269,
37
+ "encodeUsPerOp": 1.07,
38
+ "decodeUsPerOp": 1.066,
39
+ "encodeOpsPerSecond": 934445.822,
40
+ "decodeOpsPerSecond": 937718.078
41
+ },
42
+ {
43
+ "profile": "quick",
44
+ "payloadBytes": 1024,
45
+ "format": "binary",
46
+ "encodedBytes": 1197,
47
+ "encodeUsPerOp": 7.638,
48
+ "decodeUsPerOp": 2.989,
49
+ "encodeOpsPerSecond": 130926.791,
50
+ "decodeOpsPerSecond": 334577.225
51
+ },
52
+ {
53
+ "profile": "quick",
54
+ "payloadBytes": 4096,
55
+ "format": "compact-json",
56
+ "encodedBytes": 4341,
57
+ "encodeUsPerOp": 3.104,
58
+ "decodeUsPerOp": 1.807,
59
+ "encodeOpsPerSecond": 322159.811,
60
+ "decodeOpsPerSecond": 553432.68
61
+ },
62
+ {
63
+ "profile": "quick",
64
+ "payloadBytes": 4096,
65
+ "format": "binary",
66
+ "encodedBytes": 4269,
67
+ "encodeUsPerOp": 8.986,
68
+ "decodeUsPerOp": 3.036,
69
+ "encodeOpsPerSecond": 111280.79,
70
+ "decodeOpsPerSecond": 329346.831
71
+ },
72
+ {
73
+ "profile": "quick",
74
+ "payloadBytes": 16384,
75
+ "format": "compact-json",
76
+ "encodedBytes": 16632,
77
+ "encodeUsPerOp": 8.292,
78
+ "decodeUsPerOp": 4.561,
79
+ "encodeOpsPerSecond": 120595.796,
80
+ "decodeOpsPerSecond": 219271.207
81
+ },
82
+ {
83
+ "profile": "quick",
84
+ "payloadBytes": 16384,
85
+ "format": "binary",
86
+ "encodedBytes": 16560,
87
+ "encodeUsPerOp": 15.204,
88
+ "decodeUsPerOp": 4.587,
89
+ "encodeOpsPerSecond": 65772.192,
90
+ "decodeOpsPerSecond": 217984.825
91
+ }
92
+ ]
93
+ }
@@ -0,0 +1,93 @@
1
+ {
2
+ "generatedAt": "2026-05-15T11:18:56.130Z",
3
+ "profile": "quick",
4
+ "iterations": 10000,
5
+ "payloadSizes": [
6
+ 256,
7
+ 1024,
8
+ 4096,
9
+ 16384
10
+ ],
11
+ "results": [
12
+ {
13
+ "profile": "quick",
14
+ "payloadBytes": 256,
15
+ "format": "compact-json",
16
+ "encodedBytes": 498,
17
+ "encodeUsPerOp": 0.861,
18
+ "decodeUsPerOp": 0.93,
19
+ "encodeOpsPerSecond": 1161370.719,
20
+ "decodeOpsPerSecond": 1075066.173
21
+ },
22
+ {
23
+ "profile": "quick",
24
+ "payloadBytes": 256,
25
+ "format": "binary",
26
+ "encodedBytes": 426,
27
+ "encodeUsPerOp": 6.016,
28
+ "decodeUsPerOp": 2.499,
29
+ "encodeOpsPerSecond": 166209.883,
30
+ "decodeOpsPerSecond": 400117.683
31
+ },
32
+ {
33
+ "profile": "quick",
34
+ "payloadBytes": 1024,
35
+ "format": "compact-json",
36
+ "encodedBytes": 1269,
37
+ "encodeUsPerOp": 0.963,
38
+ "decodeUsPerOp": 0.904,
39
+ "encodeOpsPerSecond": 1038530.737,
40
+ "decodeOpsPerSecond": 1105743.219
41
+ },
42
+ {
43
+ "profile": "quick",
44
+ "payloadBytes": 1024,
45
+ "format": "binary",
46
+ "encodedBytes": 1197,
47
+ "encodeUsPerOp": 6.58,
48
+ "decodeUsPerOp": 2.551,
49
+ "encodeOpsPerSecond": 151983.004,
50
+ "decodeOpsPerSecond": 391961.804
51
+ },
52
+ {
53
+ "profile": "quick",
54
+ "payloadBytes": 4096,
55
+ "format": "compact-json",
56
+ "encodedBytes": 4341,
57
+ "encodeUsPerOp": 2.29,
58
+ "decodeUsPerOp": 1.706,
59
+ "encodeOpsPerSecond": 436690.681,
60
+ "decodeOpsPerSecond": 586132.045
61
+ },
62
+ {
63
+ "profile": "quick",
64
+ "payloadBytes": 4096,
65
+ "format": "binary",
66
+ "encodedBytes": 4269,
67
+ "encodeUsPerOp": 8.274,
68
+ "decodeUsPerOp": 2.81,
69
+ "encodeOpsPerSecond": 120859.221,
70
+ "decodeOpsPerSecond": 355922.792
71
+ },
72
+ {
73
+ "profile": "quick",
74
+ "payloadBytes": 16384,
75
+ "format": "compact-json",
76
+ "encodedBytes": 16632,
77
+ "encodeUsPerOp": 6.953,
78
+ "decodeUsPerOp": 3.802,
79
+ "encodeOpsPerSecond": 143830.843,
80
+ "decodeOpsPerSecond": 263020.978
81
+ },
82
+ {
83
+ "profile": "quick",
84
+ "payloadBytes": 16384,
85
+ "format": "binary",
86
+ "encodedBytes": 16560,
87
+ "encodeUsPerOp": 13.34,
88
+ "decodeUsPerOp": 3.786,
89
+ "encodeOpsPerSecond": 74962.159,
90
+ "decodeOpsPerSecond": 264160.181
91
+ }
92
+ ]
93
+ }
package/bin/7h3.ts ADDED
@@ -0,0 +1,385 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util'
3
+ import { createServer } from 'node:http'
4
+ import { writeFileSync } from 'node:fs'
5
+
6
+ const USAGE = `
7
+ 7h3 — Aurelion Interaction Protocol CLI (wire version 7h3/0.1)
8
+
9
+ Usage:
10
+ 7h3 keygen [--output <file>]
11
+ 7h3 sign --private-key <key> --sender <id> [--recipient <id>] [--payload <str>] [--ttl <ms>]
12
+ 7h3 verify --public-key <key> --envelope <json>
13
+ 7h3 inspect --envelope <json>
14
+ 7h3 gateway --upstream <url> [--port <n>] [--public-key <key>] [--require ed25519|none]
15
+ [--sign-responses] [--private-key <key>] [--sender <id>]
16
+ 7h3 keys serve [--public-key <key>] [--key-id <id>] [--port <n>]
17
+ 7h3 help
18
+
19
+ Commands:
20
+ keygen Generate an Ed25519 keypair (PKCS8/SPKI, base64url-encoded)
21
+ sign Create and sign an AIP envelope
22
+ verify Verify an AIP envelope signature
23
+ inspect Pretty-print an AIP envelope fields
24
+ gateway Run a verifying HTTP proxy gateway
25
+ keys serve Serve a /.well-known/7h3-keys endpoint
26
+ help Show this usage table
27
+ `
28
+
29
+ function die(msg: string): never {
30
+ process.stderr.write(`Error: ${msg}\n`)
31
+ process.exit(1)
32
+ }
33
+
34
+ async function cmdKeygen(argv: string[]): Promise<void> {
35
+ const { values } = parseArgs({
36
+ args: argv,
37
+ options: {
38
+ output: { type: 'string', short: 'o' },
39
+ },
40
+ strict: false,
41
+ })
42
+
43
+ const { generateEd25519KeypairBase64Url } = await import('../src/protocol.js')
44
+ const { publicKey, privateKey } = await generateEd25519KeypairBase64Url()
45
+
46
+ const result = {
47
+ algorithm: 'Ed25519',
48
+ wireVersion: '7h3/0.1',
49
+ publicKey,
50
+ privateKey,
51
+ createdAt: new Date().toISOString(),
52
+ warning: 'Keep privateKey secret — it grants signing authority over your agent identity.',
53
+ }
54
+
55
+ const json = JSON.stringify(result, null, 2)
56
+
57
+ if (values.output) {
58
+ writeFileSync(values.output as string, json, 'utf8')
59
+ process.stdout.write(`Keypair written to ${values.output}\n`)
60
+ process.stdout.write(` algorithm : Ed25519\n`)
61
+ process.stdout.write(` publicKey : ${publicKey}\n`)
62
+ process.stdout.write(` createdAt : ${result.createdAt}\n`)
63
+ } else {
64
+ process.stdout.write(json + '\n')
65
+ }
66
+ }
67
+
68
+ async function cmdSign(argv: string[]): Promise<void> {
69
+ const { values } = parseArgs({
70
+ args: argv,
71
+ options: {
72
+ 'private-key': { type: 'string' },
73
+ sender: { type: 'string' },
74
+ recipient: { type: 'string' },
75
+ payload: { type: 'string' },
76
+ ttl: { type: 'string' },
77
+ },
78
+ strict: false,
79
+ })
80
+
81
+ const privateKey = values['private-key'] as string | undefined
82
+ const sender = values['sender'] as string | undefined
83
+
84
+ if (!privateKey) die('--private-key is required')
85
+ if (!sender) die('--sender is required')
86
+
87
+ const { createEnvelope, signEnvelopeEd25519 } = await import('../src/protocol.js')
88
+
89
+ const envelope = createEnvelope({
90
+ sender: sender!,
91
+ recipient: values['recipient'] as string | undefined,
92
+ intent: 'PING',
93
+ content: (values['payload'] as string | undefined) ?? '',
94
+ ttlMs: values['ttl'] ? parseInt(values['ttl'] as string, 10) : 60_000,
95
+ })
96
+
97
+ const signed = await signEnvelopeEd25519(envelope, privateKey!)
98
+ process.stdout.write(JSON.stringify(signed) + '\n')
99
+ }
100
+
101
+ async function cmdVerify(argv: string[]): Promise<void> {
102
+ const { values } = parseArgs({
103
+ args: argv,
104
+ options: {
105
+ 'public-key': { type: 'string' },
106
+ envelope: { type: 'string' },
107
+ },
108
+ strict: false,
109
+ })
110
+
111
+ const publicKey = values['public-key'] as string | undefined
112
+ const envelopeJson = values['envelope'] as string | undefined
113
+
114
+ if (!publicKey) die('--public-key is required')
115
+ if (!envelopeJson) die('--envelope is required')
116
+
117
+ const { validateEnvelope, verifyEnvelopeEd25519 } = await import('../src/protocol.js')
118
+
119
+ let envelope: ReturnType<typeof JSON.parse>
120
+ try {
121
+ envelope = JSON.parse(envelopeJson!)
122
+ } catch {
123
+ die('--envelope is not valid JSON')
124
+ }
125
+
126
+ const diagnostics = validateEnvelope(envelope)
127
+ const errors = diagnostics.filter(d => d.level === 'error')
128
+ const warnings = diagnostics.filter(d => d.level === 'warning')
129
+
130
+ if (errors.length > 0) {
131
+ process.stdout.write('INVALID — envelope validation errors:\n')
132
+ for (const e of errors) process.stdout.write(` [error] ${e.message}\n`)
133
+ for (const w of warnings) process.stdout.write(` [warning] ${w.message}\n`)
134
+ process.exit(1)
135
+ }
136
+
137
+ const valid = await verifyEnvelopeEd25519(envelope, publicKey!)
138
+
139
+ if (valid) {
140
+ process.stdout.write('Signature valid\n')
141
+ if (warnings.length > 0) {
142
+ for (const w of warnings) process.stdout.write(` [warning] ${w.message}\n`)
143
+ }
144
+ } else {
145
+ process.stdout.write('INVALID — signature verification failed\n')
146
+ process.stdout.write(` alg : ${envelope.signature?.alg ?? 'none'}\n`)
147
+ process.stdout.write(` keyId : ${envelope.signature?.keyId ?? 'none'}\n`)
148
+ process.exit(1)
149
+ }
150
+ }
151
+
152
+ async function cmdInspect(argv: string[]): Promise<void> {
153
+ const { values } = parseArgs({
154
+ args: argv,
155
+ options: {
156
+ envelope: { type: 'string' },
157
+ },
158
+ strict: false,
159
+ })
160
+
161
+ const envelopeJson = values['envelope'] as string | undefined
162
+ if (!envelopeJson) die('--envelope is required')
163
+
164
+ let envelope: ReturnType<typeof JSON.parse>
165
+ try {
166
+ envelope = JSON.parse(envelopeJson!)
167
+ } catch {
168
+ die('--envelope is not valid JSON')
169
+ }
170
+
171
+ const h = envelope.header ?? {}
172
+ const b = envelope.body ?? {}
173
+ const s = envelope.signature
174
+
175
+ const nowMs = Date.now()
176
+ const expiresMs = (h.timestampMs ?? 0) + (h.ttlMs ?? 0)
177
+ const expired = expiresMs < nowMs
178
+ const expiresStatus = expired
179
+ ? `EXPIRED (${new Date(expiresMs).toISOString()})`
180
+ : `OK — expires ${new Date(expiresMs).toISOString()}`
181
+
182
+ const content: string = typeof b.content === 'string' ? b.content : ''
183
+ const contentPreview = content.length > 80 ? content.slice(0, 77) + '...' : content
184
+
185
+ process.stdout.write(`Wire Version : ${h.version ?? '(none)'}\n`)
186
+ process.stdout.write(`Message ID : ${h.messageId ?? '(none)'}\n`)
187
+ process.stdout.write(`Sender : ${h.sender ?? '(none)'}\n`)
188
+ process.stdout.write(`Recipient : ${h.recipient ?? '(none)'}\n`)
189
+ process.stdout.write(`Timestamp : ${h.timestampMs ? new Date(h.timestampMs).toISOString() : '(none)'}\n`)
190
+ process.stdout.write(`TTL : ${h.ttlMs ?? '(none)'} ms\n`)
191
+ process.stdout.write(`Expires : ${expiresStatus}\n`)
192
+ process.stdout.write(`Intent : ${b.intent ?? '(none)'}\n`)
193
+ process.stdout.write(`Content : ${contentPreview || '(empty)'}\n`)
194
+ if (s) {
195
+ process.stdout.write(`Sig alg : ${s.alg}\n`)
196
+ process.stdout.write(`Sig keyId : ${s.keyId}\n`)
197
+ } else {
198
+ process.stdout.write(`Signature : (none)\n`)
199
+ }
200
+ }
201
+
202
+ async function cmdGateway(argv: string[]): Promise<void> {
203
+ const { values } = parseArgs({
204
+ args: argv,
205
+ options: {
206
+ upstream: { type: 'string' },
207
+ port: { type: 'string' },
208
+ 'public-key': { type: 'string' },
209
+ require: { type: 'string' },
210
+ 'sign-responses': { type: 'boolean' },
211
+ 'private-key': { type: 'string' },
212
+ sender: { type: 'string' },
213
+ },
214
+ strict: false,
215
+ })
216
+
217
+ const upstream = values['upstream'] as string | undefined
218
+ if (!upstream) die('--upstream is required')
219
+
220
+ const port = parseInt((values['port'] as string | undefined) ?? '8080', 10)
221
+ const publicKey = values['public-key'] as string | undefined
222
+ const requireMode = (values['require'] as string | undefined) ?? (publicKey ? 'ed25519' : 'none')
223
+ const signResponses = !!(values['sign-responses'])
224
+ const privateKey = values['private-key'] as string | undefined
225
+ const sender = values['sender'] as string | undefined
226
+
227
+ const { createGateway } = await import('../src/gateway.js')
228
+ const { createStaticKeyRegistry } = await import('../src/keyRegistry.js')
229
+
230
+ const keys: Record<string, string> = {}
231
+ if (publicKey && sender) keys[sender] = publicKey
232
+ const keyRegistry = createStaticKeyRegistry(keys)
233
+
234
+ const gateway = createGateway({
235
+ upstream: upstream!,
236
+ keyRegistry,
237
+ signResponses: signResponses && !!privateKey,
238
+ privateKey,
239
+ sender,
240
+ defaultPolicy: requireMode === 'none' ? 'allow' : 'deny',
241
+ })
242
+
243
+ const server = createServer(async (req, res) => {
244
+ const chunks: Buffer[] = []
245
+ req.on('data', (chunk: Buffer) => chunks.push(chunk))
246
+ req.on('end', async () => {
247
+ const body = Buffer.concat(chunks)
248
+
249
+ // Flatten headers: string | string[] => string
250
+ const headers: Record<string, string> = {}
251
+ for (const [k, v] of Object.entries(req.headers)) {
252
+ if (v === undefined) continue
253
+ headers[k] = Array.isArray(v) ? v.join(', ') : v
254
+ }
255
+
256
+ try {
257
+ const result = await gateway.handle({
258
+ method: req.method ?? 'GET',
259
+ path: req.url ?? '/',
260
+ headers,
261
+ body: body.length > 0 ? body.toString('utf8') : undefined,
262
+ })
263
+ res.writeHead(result.status, result.headers)
264
+ res.end(result.body)
265
+ } catch (err) {
266
+ res.writeHead(502, { 'content-type': 'text/plain' })
267
+ res.end(`Gateway error: ${String(err)}`)
268
+ }
269
+ })
270
+ })
271
+
272
+ server.listen(port, () => {
273
+ process.stderr.write(`7h3 gateway listening on port ${port}\n`)
274
+ process.stderr.write(` upstream : ${upstream}\n`)
275
+ process.stderr.write(` verify mode : ${requireMode}\n`)
276
+ process.stderr.write(` sign-responses: ${signResponses && !!privateKey}\n`)
277
+ if (sender) process.stderr.write(` sender : ${sender}\n`)
278
+ })
279
+ }
280
+
281
+ async function cmdKeysServe(argv: string[]): Promise<void> {
282
+ const { values } = parseArgs({
283
+ args: argv,
284
+ options: {
285
+ 'public-key': { type: 'string' },
286
+ 'key-id': { type: 'string' },
287
+ port: { type: 'string' },
288
+ },
289
+ strict: false,
290
+ })
291
+
292
+ const publicKey = values['public-key'] as string | undefined
293
+ const keyId = (values['key-id'] as string | undefined) ?? 'default'
294
+ const port = parseInt((values['port'] as string | undefined) ?? '8081', 10)
295
+
296
+ const { serveWellKnownKeys } = await import('../src/keyInfra.js')
297
+
298
+ const doc = {
299
+ version: '7h3/0.1' as const,
300
+ updated: Date.now(),
301
+ keys: publicKey
302
+ ? [
303
+ {
304
+ id: keyId,
305
+ algorithm: 'Ed25519' as const,
306
+ publicKey,
307
+ created: Date.now(),
308
+ },
309
+ ]
310
+ : [],
311
+ }
312
+
313
+ const body = serveWellKnownKeys(doc)
314
+
315
+ const server = createServer((req, res) => {
316
+ if (req.url === '/.well-known/7h3-keys') {
317
+ res.writeHead(200, { 'content-type': 'application/json' })
318
+ res.end(body)
319
+ } else {
320
+ res.writeHead(404, { 'content-type': 'text/plain' })
321
+ res.end('Not Found')
322
+ }
323
+ })
324
+
325
+ server.listen(port, () => {
326
+ process.stderr.write(`7h3 key server listening on port ${port}\n`)
327
+ process.stderr.write(` GET /.well-known/7h3-keys\n`)
328
+ if (publicKey) process.stderr.write(` key-id: ${keyId}\n`)
329
+ else process.stderr.write(` (no keys configured — empty document)\n`)
330
+ })
331
+ }
332
+
333
+ async function main(): Promise<void> {
334
+ const args = process.argv.slice(2)
335
+ const command = args[0] ?? 'help'
336
+ const rest = args.slice(1)
337
+
338
+ switch (command) {
339
+ case 'keygen':
340
+ await cmdKeygen(rest)
341
+ break
342
+
343
+ case 'sign':
344
+ await cmdSign(rest)
345
+ break
346
+
347
+ case 'verify':
348
+ await cmdVerify(rest)
349
+ break
350
+
351
+ case 'inspect':
352
+ await cmdInspect(rest)
353
+ break
354
+
355
+ case 'gateway':
356
+ await cmdGateway(rest)
357
+ break
358
+
359
+ case 'keys': {
360
+ const sub = rest[0]
361
+ if (sub === 'serve') {
362
+ await cmdKeysServe(rest.slice(1))
363
+ } else {
364
+ die(`Unknown subcommand 'keys ${sub ?? ''}'. Did you mean 'keys serve'?`)
365
+ }
366
+ break
367
+ }
368
+
369
+ case 'help':
370
+ case '--help':
371
+ case '-h':
372
+ process.stdout.write(USAGE)
373
+ break
374
+
375
+ default:
376
+ process.stderr.write(`Unknown command: ${command}\n`)
377
+ process.stdout.write(USAGE)
378
+ process.exit(1)
379
+ }
380
+ }
381
+
382
+ main().catch(err => {
383
+ process.stderr.write(`Fatal: ${String(err)}\n`)
384
+ process.exit(1)
385
+ })