@7h3/protocol 0.1.2 → 0.5.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 (276) 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 +152 -0
  8. package/CONTRIBUTING.md +82 -0
  9. package/Dockerfile +73 -0
  10. package/GOVERNANCE.md +62 -0
  11. package/README.md +1317 -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 +406 -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-github.png +0 -0
  72. package/docs/assets/banner.png +0 -0
  73. package/docs/assets/banner.svg +123 -0
  74. package/eslint.config.js +15 -0
  75. package/fuzz/ts/harness-decode.ts +136 -0
  76. package/fuzz/ts/harness-verify.ts +121 -0
  77. package/fuzz/ts/run.ts +35 -0
  78. package/mcp-server/README.md +38 -0
  79. package/mcp-server/package-lock.json +1187 -0
  80. package/mcp-server/package.json +35 -0
  81. package/mcp-server/src/index.ts +236 -0
  82. package/mcp-server/tsconfig.json +14 -0
  83. package/package.json +121 -13
  84. package/scripts/aip-framework-quickstart.ts +110 -0
  85. package/scripts/aip-mcp-gateway.ts +38 -0
  86. package/scripts/aip-mcp-wrap-demo.ts +72 -0
  87. package/scripts/aip-quickstart.ts +60 -0
  88. package/scripts/bench-diff.ts +118 -0
  89. package/scripts/bench-protocol-e2e.ts +937 -0
  90. package/scripts/bench-protocol-openloop.ts +1397 -0
  91. package/scripts/bench-replay-cache.ts +76 -0
  92. package/scripts/bench-signature-profiles.ts +180 -0
  93. package/scripts/bench-wire-codecs.ts +161 -0
  94. package/scripts/build-binary-conformance.ts +36 -0
  95. package/scripts/build-release-dashboard.ts +175 -0
  96. package/scripts/canary-rollout.ts +38 -0
  97. package/scripts/mcpGatewayCli.test.ts +116 -0
  98. package/scripts/prepare-aip-package.ts +88 -0
  99. package/scripts/regen-conformance-sigs.ts +18 -0
  100. package/scripts/release-gate.ts +19 -0
  101. package/scripts/validate-runtime-policy.ts +18 -0
  102. package/sdk/browser/index.test.ts +162 -0
  103. package/sdk/browser/index.ts +257 -0
  104. package/sdk/browser/package.json +13 -0
  105. package/sdk/go/cbor.go +551 -0
  106. package/sdk/go/cbor_test.go +232 -0
  107. package/sdk/go/encryption.go +280 -0
  108. package/sdk/go/encryption_test.go +318 -0
  109. package/sdk/go/go.mod +7 -0
  110. package/sdk/go/go.sum +4 -0
  111. package/sdk/go/http.go +135 -0
  112. package/sdk/go/protocol.go +324 -0
  113. package/sdk/go/protocol_test.go +334 -0
  114. package/sdk/go/replay.go +121 -0
  115. package/sdk/go/replay_test.go +149 -0
  116. package/sdk/go/webhook.go +136 -0
  117. package/sdk/pq/package-lock.json +1358 -0
  118. package/sdk/pq/package.json +42 -0
  119. package/sdk/pq/src/index.test.ts +143 -0
  120. package/sdk/pq/src/index.ts +166 -0
  121. package/sdk/pq/tsconfig.json +14 -0
  122. package/sdk/pq/vitest.config.ts +7 -0
  123. package/sdk/python/README.md +18 -0
  124. package/sdk/python/protocol_7h3/__init__.py +46 -0
  125. package/sdk/python/protocol_7h3/encryption.py +252 -0
  126. package/sdk/python/protocol_7h3/http.py +212 -0
  127. package/sdk/python/protocol_7h3/keys.py +149 -0
  128. package/sdk/python/protocol_7h3/pq.py +244 -0
  129. package/sdk/python/protocol_7h3/protocol.py +525 -0
  130. package/sdk/python/protocol_7h3/queue.py +118 -0
  131. package/sdk/python/protocol_7h3/replay.py +98 -0
  132. package/sdk/python/protocol_7h3/webhook.py +116 -0
  133. package/sdk/python/pyproject.toml +40 -0
  134. package/sdk/python/tests/test_conformance.py +110 -0
  135. package/sdk/python/tests/test_encryption.py +206 -0
  136. package/sdk/python/tests/test_http.py +305 -0
  137. package/sdk/python/tests/test_keys.py +417 -0
  138. package/sdk/python/tests/test_queue.py +120 -0
  139. package/sdk/python/tests/test_webhook.py +345 -0
  140. package/sdk/rust/Cargo.lock +371 -0
  141. package/sdk/rust/Cargo.toml +25 -0
  142. package/sdk/rust/README.md +31 -0
  143. package/sdk/rust/fuzz/Cargo.toml +29 -0
  144. package/sdk/rust/fuzz/fuzz_targets/fuzz_canonicalize.rs +46 -0
  145. package/sdk/rust/fuzz/fuzz_targets/fuzz_decode.rs +11 -0
  146. package/sdk/rust/src/bin/aip_mcp_gateway.rs +59 -0
  147. package/sdk/rust/src/http.rs +145 -0
  148. package/sdk/rust/src/keys.rs +161 -0
  149. package/sdk/rust/src/lib.rs +688 -0
  150. package/sdk/rust/src/queue.rs +79 -0
  151. package/sdk/rust/src/webhook.rs +86 -0
  152. package/sdk/rust/tests/conformance.rs +148 -0
  153. package/sdk/rust/tests/gateway.rs +130 -0
  154. package/sdk/rust/tests/http_webhook_queue.rs +201 -0
  155. package/sdk/rust/tests/keys.rs +189 -0
  156. package/sdk/threshold/index.d.ts +68 -0
  157. package/sdk/threshold/index.d.ts.map +1 -0
  158. package/sdk/threshold/index.js +254 -0
  159. package/sdk/threshold/package-lock.json +1361 -0
  160. package/sdk/threshold/package.json +39 -0
  161. package/sdk/threshold/src/index.d.ts +68 -0
  162. package/sdk/threshold/src/index.d.ts.map +1 -0
  163. package/sdk/threshold/src/index.js +254 -0
  164. package/sdk/threshold/src/index.test.ts +238 -0
  165. package/sdk/threshold/src/index.ts +355 -0
  166. package/sdk/threshold/tsconfig.json +19 -0
  167. package/sdk/threshold/vitest.config.ts +12 -0
  168. package/src/agentAdapter.test.ts +48 -0
  169. package/src/agentAdapter.ts +56 -0
  170. package/src/auditLog.test.ts +145 -0
  171. package/src/auditLog.ts +147 -0
  172. package/src/capability.test.ts +504 -0
  173. package/src/capability.ts +380 -0
  174. package/src/cborCodec.test.ts +263 -0
  175. package/src/cborCodec.ts +339 -0
  176. package/src/conformance.test.ts +136 -0
  177. package/src/conformanceVectors.ts +99 -0
  178. package/src/encryption.test.ts +206 -0
  179. package/src/encryption.ts +245 -0
  180. package/src/envelopeCbor.ts +140 -0
  181. package/src/frameworkAdapters.test.ts +290 -0
  182. package/src/frameworkAdapters.ts +261 -0
  183. package/src/gateway.test.ts +343 -0
  184. package/src/gateway.ts +246 -0
  185. package/src/grpcBinding.test.ts +211 -0
  186. package/src/grpcBinding.ts +103 -0
  187. package/src/httpBinding.test.ts +376 -0
  188. package/src/httpBinding.ts +189 -0
  189. package/src/index.ts +39 -0
  190. package/src/keyInfra.test.ts +278 -0
  191. package/src/keyInfra.ts +228 -0
  192. package/src/keyRegistry.ts +59 -0
  193. package/src/keyRotation.test.ts +78 -0
  194. package/src/keyRotation.ts +72 -0
  195. package/src/mcpGateway.test.ts +129 -0
  196. package/src/mcpGateway.ts +250 -0
  197. package/src/mcpTransports.test.ts +92 -0
  198. package/src/mcpTransports.ts +169 -0
  199. package/src/mcpWrapper.test.ts +179 -0
  200. package/src/mcpWrapper.ts +206 -0
  201. package/src/otel.ts +136 -0
  202. package/src/policyEnforcer.test.ts +99 -0
  203. package/src/policyEnforcer.ts +169 -0
  204. package/src/policyTelemetryFeedback.test.ts +25 -0
  205. package/src/policyTelemetryFeedback.ts +38 -0
  206. package/src/protocol.bench.ts +37 -0
  207. package/{protocol.d.ts → src/protocol.d.ts} +2 -1
  208. package/src/protocol.d.ts.map +1 -0
  209. package/src/protocol.js +294 -0
  210. package/src/protocol.test.ts +155 -0
  211. package/src/protocol.ts +414 -0
  212. package/src/protocolAgent.test.ts +105 -0
  213. package/src/protocolAgent.ts +169 -0
  214. package/src/protocolBinary.test.ts +165 -0
  215. package/src/protocolBinary.ts +312 -0
  216. package/src/protocolCapabilities.ts +70 -0
  217. package/src/protocolFuzz.advanced.test.ts +235 -0
  218. package/src/protocolFuzz.test.ts +111 -0
  219. package/src/protocolNegative.test.ts +97 -0
  220. package/src/protocolReplay.test.ts +71 -0
  221. package/src/protocolReplay.ts +194 -0
  222. package/src/protocolTransport.test.ts +556 -0
  223. package/src/protocolTransport.ts +483 -0
  224. package/src/queueBinding.test.ts +130 -0
  225. package/src/queueBinding.ts +102 -0
  226. package/src/rateLimiter.test.ts +96 -0
  227. package/src/rateLimiter.ts +46 -0
  228. package/src/redisClient.ts +140 -0
  229. package/src/redisIntegration.test.ts +134 -0
  230. package/src/replayStores.test.ts +273 -0
  231. package/src/replayStores.ts +215 -0
  232. package/src/revocation.test.ts +98 -0
  233. package/src/revocation.ts +0 -0
  234. package/src/routePolicy.test.ts +87 -0
  235. package/src/routePolicy.ts +72 -0
  236. package/src/runtimePolicy.test.ts +49 -0
  237. package/src/runtimePolicy.ts +81 -0
  238. package/src/runtimePolicyManager.test.ts +29 -0
  239. package/src/runtimePolicyManager.ts +50 -0
  240. package/src/runtimePolicyPresets.ts +43 -0
  241. package/src/signedResponse.test.ts +111 -0
  242. package/src/signedResponse.ts +83 -0
  243. package/src/stream.test.ts +254 -0
  244. package/src/stream.ts +417 -0
  245. package/src/telemetry.test.ts +251 -0
  246. package/src/telemetry.ts +299 -0
  247. package/src/webhookBinding.test.ts +144 -0
  248. package/src/webhookBinding.ts +115 -0
  249. package/src/wsBinding.test.ts +221 -0
  250. package/src/wsBinding.ts +200 -0
  251. package/tsconfig.json +15 -0
  252. package/tsconfig.lib.json +23 -0
  253. package/vite.lib.config.ts +16 -0
  254. package/vitest.config.ts +11 -0
  255. package/agentAdapter.d.ts +0 -26
  256. package/conformanceVectors.d.ts +0 -20
  257. package/frameworkAdapters.d.ts +0 -72
  258. package/index.d.ts +0 -20
  259. package/index.js +0 -1702
  260. package/keyRotation.d.ts +0 -20
  261. package/mcpGateway.d.ts +0 -37
  262. package/mcpTransports.d.ts +0 -62
  263. package/mcpWrapper.d.ts +0 -83
  264. package/policyEnforcer.d.ts +0 -50
  265. package/policyTelemetryFeedback.d.ts +0 -11
  266. package/protocolAgent.d.ts +0 -58
  267. package/protocolBinary.d.ts +0 -8
  268. package/protocolCapabilities.d.ts +0 -24
  269. package/protocolReplay.d.ts +0 -35
  270. package/protocolTransport.d.ts +0 -73
  271. package/redisClient.d.ts +0 -49
  272. package/replayStores.d.ts +0 -32
  273. package/revocation.d.ts +0 -71
  274. package/runtimePolicy.d.ts +0 -24
  275. package/runtimePolicyManager.d.ts +0 -15
  276. package/runtimePolicyPresets.d.ts +0 -11
@@ -0,0 +1,41 @@
1
+ # Telemetry Events
2
+
3
+ Transport-level telemetry can be attached to `receiveEnvelope` via `telemetry` callback.
4
+
5
+ ## Event phases
6
+
7
+ - `decoded`
8
+ - `rejected_clock_skew`
9
+ - `rejected_validation`
10
+ - `rejected_replay`
11
+ - `rejected_missing_signature`
12
+ - `rejected_missing_material`
13
+ - `rejected_bad_signature`
14
+ - `accepted`
15
+
16
+ ## Usage
17
+
18
+ ```ts
19
+ import { receiveEnvelope } from './src/protocolTransport'
20
+
21
+ await receiveEnvelope(rawEnvelope, {
22
+ nowMs: Date.now(),
23
+ replayCache,
24
+ secretResolver: async () => sharedSecret,
25
+ telemetry: async (event) => {
26
+ console.log(event.phase, event.sender, event.messageId, event.reason)
27
+ },
28
+ })
29
+ ```
30
+
31
+ ## Gateway audit events
32
+
33
+ `createAipMcpGatewayRuntime` supports `onAuditEvent` for request/policy traces.
34
+
35
+ Phases:
36
+
37
+ - `request_received`
38
+ - `policy`
39
+ - `verification_failed`
40
+ - `request_success`
41
+ - `request_error`
@@ -0,0 +1,89 @@
1
+ # 7h3 Protocol AIP Threat Model (v1.0 draft)
2
+
3
+ ## Scope
4
+
5
+ This model covers `aip/0.1` envelope transport and verification paths in:
6
+
7
+ - `src/protocol.ts`
8
+ - `src/protocolTransport.ts`
9
+ - `src/mcpGateway.ts`
10
+ - `sdk/rust/src/lib.rs`
11
+
12
+ ## Assets
13
+
14
+ - Message integrity and authenticity
15
+ - Agent identity binding (`sender`, `keyId`, signature material)
16
+ - Freshness (TTL + replay resistance)
17
+ - Gateway policy decisions (allowlist/auth/rate-limit)
18
+
19
+ ## Assumptions
20
+
21
+ - Clocks are roughly synchronized within deployment skew limits.
22
+ - Secret/private-key material is provisioned securely outside protocol logic.
23
+ - Transport channel can be observed/modified by adversaries; protocol must detect tamper.
24
+
25
+ ## Threats and controls
26
+
27
+ ## Tampering
28
+
29
+ - Threat: attacker modifies envelope body/header in transit.
30
+ - Controls:
31
+ - canonical payload signing (`HS256` / `ED25519`)
32
+ - signature verification before acceptance
33
+ - canonical key order fixed to prevent ambiguous signing forms
34
+
35
+ ## Replay
36
+
37
+ - Threat: attacker replays previously valid signed envelopes.
38
+ - Controls:
39
+ - TTL checks (`timestampMs + ttlMs`)
40
+ - replay cache consumption (`messageId` + `sender` + `nonce` semantics)
41
+
42
+ ## Sender impersonation
43
+
44
+ - Threat: attacker signs as trusted sender with incorrect key material.
45
+ - Controls:
46
+ - verifier resolves material by `(keyId, sender)` context
47
+ - `signatureResolver`/`secretResolver` mapping policy in transport layer
48
+
49
+ ## Algorithm confusion / downgrade
50
+
51
+ - Threat: attacker swaps `alg` or relies on omitted compact alg fields.
52
+ - Controls:
53
+ - explicit algorithm field in canonical signature object
54
+ - compact wire supports `sig.a`; default compatibility path only for legacy HS256
55
+ - verifier rejects material when resolved algorithm does not match envelope signature
56
+
57
+ ## Gateway abuse
58
+
59
+ - Threat: unauthorized or abusive JSON-RPC method calls.
60
+ - Controls:
61
+ - method allowlist
62
+ - authorization policy hook
63
+ - rate-limit policy hook with context-aware keys
64
+
65
+ ## Mitigated risks
66
+
67
+ - **Distributed replay** — a Redis-backed `DistributedReplayStore` is shipped
68
+ (`createRedisReplayStore`, `src/replayStores.ts`). It uses atomic `SET NX PX`,
69
+ batches via pipeline (`reserveMany`), and degrades to a local store on a Redis
70
+ outage so a node never blindly accepts replays and never hard-stops. See
71
+ `DISTRIBUTED_REPLAY.md`.
72
+ - **Fleet-wide key revocation** — a shared `RevocationStore`
73
+ (`createRedisRevocationStore`, `src/revocation.ts`) is consulted on the verify
74
+ path via `withRevocationCheck`. A key revoked on one node is rejected on all
75
+ nodes; reads are cached and fail **closed** by default. See `KEY_REVOCATION.md`.
76
+
77
+ ## Remaining risks (open)
78
+
79
+ - No formal fuzz campaign yet for parser/canonicalization boundaries.
80
+ - Revocation/replay stores rely on a correctly provisioned, available Redis (or
81
+ equivalent) control plane; operators own its HA and clock synchronization.
82
+ - No third-party cryptographic audit yet.
83
+
84
+ ## Required production mitigations
85
+
86
+ - Keep signature verification enabled by default in production paths.
87
+ - Deploy a shared replay store (`createRedisReplayStore`) for horizontally scaled gateways.
88
+ - Wire a shared revocation store (`createRedisRevocationStore` + `withRevocationCheck`)
89
+ and enforce key lifecycle policy (rotation, expiry, revocation) in the control plane.
@@ -0,0 +1,30 @@
1
+ # AIP Versioning Policy
2
+
3
+ ## Protocol versions
4
+
5
+ - Protocol version is declared in `header.version` (for example `aip/0.1`).
6
+ - New major protocol versions must not silently alter canonicalization or signature semantics.
7
+ - Backward-incompatible wire or verification changes require a new minor/major protocol tag.
8
+
9
+ ## Compatibility rules
10
+
11
+ - Patch releases: bug fixes only, no wire-format or canonicalization changes.
12
+ - Minor releases: additive changes only (new optional fields/capabilities, no required-field breakage).
13
+ - Major releases: allowed to remove or alter semantics with explicit migration documentation.
14
+
15
+ ## Signature profile policy
16
+
17
+ - Supported profiles are explicitly declared by `signature.alg`.
18
+ - Adding a new signature profile is a minor release only if existing profiles continue to verify unchanged.
19
+ - Removing a profile requires a major release.
20
+
21
+ ## SDK versioning
22
+
23
+ - TypeScript, Python, and Rust SDKs follow semver independently.
24
+ - Conformance fixture changes require SDK conformance updates in the same release train.
25
+
26
+ ## Release evidence requirements
27
+
28
+ - Conformance suites (TS/Python/Rust) pass.
29
+ - Bench regression checks attached for performance-sensitive changes.
30
+ - Migration guide updated for any behavior change that impacts consumers.
Binary file
Binary file
@@ -0,0 +1,123 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#06060f"/>
5
+ <stop offset="100%" style="stop-color:#0c0c22"/>
6
+ </linearGradient>
7
+ <linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="0%">
8
+ <stop offset="0%" style="stop-color:#4f7cff"/>
9
+ <stop offset="100%" style="stop-color:#a855f7"/>
10
+ </linearGradient>
11
+ <pattern id="hex" x="0" y="0" width="70" height="60" patternUnits="userSpaceOnUse">
12
+ <polygon points="35,3 67,21 67,57 35,60 3,57 3,21" fill="none" stroke="#ffffff07" stroke-width="1"/>
13
+ </pattern>
14
+ <filter id="softglow" x="-10%" y="-10%" width="120%" height="120%">
15
+ <feGaussianBlur stdDeviation="2" result="blur"/>
16
+ <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
17
+ </filter>
18
+ </defs>
19
+
20
+ <!-- Background -->
21
+ <rect width="1280" height="640" fill="url(#bg)"/>
22
+ <rect width="1280" height="640" fill="url(#hex)"/>
23
+
24
+ <!-- Ambient glows -->
25
+ <ellipse cx="180" cy="220" rx="300" ry="240" fill="#4f7cff" opacity="0.05"/>
26
+ <ellipse cx="1100" cy="420" rx="260" ry="200" fill="#a855f7" opacity="0.06"/>
27
+
28
+ <!-- Top accent bar -->
29
+ <rect x="0" y="0" width="1280" height="3" fill="url(#accentGrad)"/>
30
+
31
+ <!-- LEFT COLUMN -->
32
+ <text x="64" y="70" font-family="SF Mono,Consolas,monospace" font-size="12" fill="#4f7cff" letter-spacing="5" opacity="0.85">CRYPTOGRAPHIC AGENT PROTOCOL · v0.4.0</text>
33
+ <text x="58" y="162" font-family="Inter,Helvetica Neue,Arial,sans-serif" font-size="96" font-weight="800" fill="#ffffff" letter-spacing="-4">7h3 Protocol</text>
34
+ <text x="62" y="204" font-family="Inter,Helvetica Neue,Arial,sans-serif" font-size="22" font-weight="300" fill="#94a3b8" letter-spacing="0.3">Sign every message. Reject every replay.</text>
35
+ <rect x="62" y="220" width="320" height="2.5" rx="1.5" fill="url(#accentGrad)"/>
36
+
37
+ <!-- Transport pills -->
38
+ <text x="62" y="272" font-family="SF Mono,Consolas,monospace" font-size="10" fill="#475569" letter-spacing="4">SECURED TRANSPORTS</text>
39
+ <rect x="62" y="284" width="128" height="36" rx="7" fill="#0f1f3d" stroke="#4f7cff" stroke-width="1.2"/>
40
+ <text x="126" y="307" font-family="Inter,sans-serif" font-size="13" font-weight="600" fill="#93c5fd" text-anchor="middle">HTTP / REST</text>
41
+ <rect x="200" y="284" width="122" height="36" rx="7" fill="#131040" stroke="#6366f1" stroke-width="1.2"/>
42
+ <text x="261" y="307" font-family="Inter,sans-serif" font-size="13" font-weight="600" fill="#a5b4fc" text-anchor="middle">WebSocket</text>
43
+ <rect x="332" y="284" width="90" height="36" rx="7" fill="#160e3d" stroke="#8b5cf6" stroke-width="1.2"/>
44
+ <text x="377" y="307" font-family="Inter,sans-serif" font-size="13" font-weight="600" fill="#c4b5fd" text-anchor="middle">gRPC</text>
45
+ <rect x="432" y="284" width="120" height="36" rx="7" fill="#1a0f3d" stroke="#a855f7" stroke-width="1.2"/>
46
+ <text x="492" y="307" font-family="Inter,sans-serif" font-size="13" font-weight="600" fill="#d8b4fe" text-anchor="middle">Msg Queues</text>
47
+ <rect x="562" y="284" width="110" height="36" rx="7" fill="#2a0f2a" stroke="#ec4899" stroke-width="1.2"/>
48
+ <text x="617" y="307" font-family="Inter,sans-serif" font-size="13" font-weight="600" fill="#f9a8d4" text-anchor="middle">Webhooks</text>
49
+
50
+ <!-- SDK badges -->
51
+ <text x="62" y="356" font-family="SF Mono,Consolas,monospace" font-size="10" fill="#475569" letter-spacing="4">5 SDKs — ZERO RUNTIME DEPS</text>
52
+ <rect x="62" y="368" width="98" height="32" rx="6" fill="#0f1d30"/>
53
+ <text x="111" y="389" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#60a5fa" text-anchor="middle">TypeScript</text>
54
+ <rect x="170" y="368" width="84" height="32" rx="6" fill="#0a1f12"/>
55
+ <text x="212" y="389" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#4ade80" text-anchor="middle">Python</text>
56
+ <rect x="264" y="368" width="72" height="32" rx="6" fill="#1f0f05"/>
57
+ <text x="300" y="389" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#fb923c" text-anchor="middle">Rust</text>
58
+ <rect x="346" y="368" width="62" height="32" rx="6" fill="#051520"/>
59
+ <text x="377" y="389" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#38bdf8" text-anchor="middle">Go</text>
60
+ <rect x="418" y="368" width="98" height="32" rx="6" fill="#180e28"/>
61
+ <text x="467" y="389" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#c084fc" text-anchor="middle">Browser</text>
62
+
63
+ <!-- Install commands -->
64
+ <rect x="62" y="420" width="650" height="116" rx="10" fill="#080d18" stroke="#1e293b" stroke-width="1"/>
65
+ <rect x="62" y="420" width="3" height="116" rx="1.5" fill="url(#accentGrad)"/>
66
+ <text x="82" y="450" font-family="SF Mono,Consolas,Courier New,monospace" font-size="12.5" fill="#4b5563">$</text>
67
+ <text x="98" y="450" font-family="SF Mono,Consolas,Courier New,monospace" font-size="12.5" fill="#cbd5e1">npm install <tspan fill="#60a5fa">@7h3/protocol</tspan></text>
68
+ <text x="82" y="476" font-family="SF Mono,Consolas,Courier New,monospace" font-size="12.5" fill="#4b5563">$</text>
69
+ <text x="98" y="476" font-family="SF Mono,Consolas,Courier New,monospace" font-size="12.5" fill="#cbd5e1">pip install <tspan fill="#4ade80">7h3-protocol</tspan></text>
70
+ <text x="82" y="502" font-family="SF Mono,Consolas,Courier New,monospace" font-size="12.5" fill="#4b5563">$</text>
71
+ <text x="98" y="502" font-family="SF Mono,Consolas,Courier New,monospace" font-size="12.5" fill="#cbd5e1">cargo add <tspan fill="#fb923c">protocol-7h3</tspan></text>
72
+ <text x="82" y="528" font-family="SF Mono,Consolas,Courier New,monospace" font-size="12.5" fill="#4b5563">$</text>
73
+ <text x="98" y="528" font-family="SF Mono,Consolas,Courier New,monospace" font-size="12.5" fill="#cbd5e1">go get <tspan fill="#38bdf8">github.com/IceMasterT/7h3-protocol/sdk/go</tspan></text>
74
+
75
+ <!-- RIGHT COLUMN — Stat cards -->
76
+ <rect x="776" y="52" width="210" height="112" rx="12" fill="#0a0f20" stroke="#4f7cff" stroke-width="1.2"/>
77
+ <text x="881" y="94" font-family="Inter,sans-serif" font-size="30" font-weight="800" fill="#4f7cff" text-anchor="middle" filter="url(#softglow)">Ed25519</text>
78
+ <text x="881" y="118" font-family="Inter,sans-serif" font-size="12" fill="#64748b" text-anchor="middle">Asymmetric signing</text>
79
+ <text x="881" y="138" font-family="Inter,sans-serif" font-size="11" fill="#334155" text-anchor="middle">PKCS8 · SPKI · base64url</text>
80
+
81
+ <rect x="1004" y="52" width="210" height="112" rx="12" fill="#0a1a12" stroke="#10b981" stroke-width="1.2"/>
82
+ <text x="1109" y="100" font-family="Inter,sans-serif" font-size="48" font-weight="800" fill="#10b981" text-anchor="middle" filter="url(#softglow)">278</text>
83
+ <text x="1109" y="124" font-family="Inter,sans-serif" font-size="12" fill="#64748b" text-anchor="middle">tests passing</text>
84
+ <text x="1109" y="142" font-family="Inter,sans-serif" font-size="11" fill="#334155" text-anchor="middle">TS · Python · Rust · Go</text>
85
+
86
+ <rect x="776" y="180" width="210" height="112" rx="12" fill="#130a20" stroke="#a855f7" stroke-width="1.2"/>
87
+ <text x="881" y="236" font-family="Inter,sans-serif" font-size="48" font-weight="800" fill="#a855f7" text-anchor="middle" filter="url(#softglow)">0</text>
88
+ <text x="881" y="260" font-family="Inter,sans-serif" font-size="12" fill="#64748b" text-anchor="middle">runtime deps</text>
89
+ <text x="881" y="278" font-family="Inter,sans-serif" font-size="11" fill="#334155" text-anchor="middle">Pure stdlib · all 5 SDKs</text>
90
+
91
+ <rect x="1004" y="180" width="210" height="112" rx="12" fill="#1a1400" stroke="#f59e0b" stroke-width="1.2"/>
92
+ <text x="1109" y="236" font-family="Inter,sans-serif" font-size="48" font-weight="800" fill="#f59e0b" text-anchor="middle" filter="url(#softglow)">5</text>
93
+ <text x="1109" y="260" font-family="Inter,sans-serif" font-size="12" fill="#64748b" text-anchor="middle">transports</text>
94
+ <text x="1109" y="278" font-family="Inter,sans-serif" font-size="11" fill="#334155" text-anchor="middle">One wire format: 7h3/0.1</text>
95
+
96
+ <!-- Security features panel -->
97
+ <rect x="776" y="308" width="438" height="242" rx="12" fill="#0a0d1a" stroke="#1e293b" stroke-width="1"/>
98
+ <rect x="776" y="308" width="438" height="3" rx="1.5" fill="url(#accentGrad)"/>
99
+ <text x="796" y="338" font-family="SF Mono,Consolas,monospace" font-size="10" fill="#475569" letter-spacing="4">SECURITY GUARANTEES</text>
100
+
101
+ <rect x="796" y="350" width="3" height="38" rx="1.5" fill="#4f7cff"/>
102
+ <text x="810" y="368" font-family="Inter,sans-serif" font-size="14" font-weight="600" fill="#e2e8f0">Verified sender identity</text>
103
+ <text x="810" y="384" font-family="Inter,sans-serif" font-size="11.5" fill="#475569">Ed25519 — cryptographically impossible to forge</text>
104
+
105
+ <rect x="796" y="398" width="3" height="38" rx="1.5" fill="#10b981"/>
106
+ <text x="810" y="416" font-family="Inter,sans-serif" font-size="14" font-weight="600" fill="#e2e8f0">Replay attack prevention</text>
107
+ <text x="810" y="432" font-family="Inter,sans-serif" font-size="11.5" fill="#475569">TTL + nonce — captured messages expire, can't be reused</text>
108
+
109
+ <rect x="796" y="446" width="3" height="38" rx="1.5" fill="#a855f7"/>
110
+ <text x="810" y="464" font-family="Inter,sans-serif" font-size="14" font-weight="600" fill="#e2e8f0">Tamper-evident audit log</text>
111
+ <text x="810" y="480" font-family="Inter,sans-serif" font-size="11.5" fill="#475569">Ed25519-signed entries — alter one byte, break the chain</text>
112
+
113
+ <rect x="796" y="494" width="3" height="38" rx="1.5" fill="#f59e0b"/>
114
+ <text x="810" y="512" font-family="Inter,sans-serif" font-size="14" font-weight="600" fill="#e2e8f0">Bidirectional trust</text>
115
+ <text x="810" y="528" font-family="Inter,sans-serif" font-size="11.5" fill="#475569">Servers sign responses — clients verify replies too</text>
116
+
117
+ <!-- Footer -->
118
+ <rect x="0" y="608" width="1280" height="32" fill="#04040c"/>
119
+ <rect x="0" y="608" width="1280" height="1" fill="#1e293b"/>
120
+ <text x="62" y="630" font-family="SF Mono,Consolas,monospace" font-size="12" fill="#334155">github.com/IceMasterT/7h3-protocol</text>
121
+ <text x="640" y="630" font-family="SF Mono,Consolas,monospace" font-size="12" fill="#334155" text-anchor="middle">MIT License · wire: 7h3/0.1 · Zero runtime deps</text>
122
+ <text x="1218" y="630" font-family="SF Mono,Consolas,monospace" font-size="12" fill="#334155" text-anchor="end">v0.4.0</text>
123
+ </svg>
@@ -0,0 +1,15 @@
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import tseslint from 'typescript-eslint'
4
+
5
+ export default tseslint.config(
6
+ { ignores: ['dist', 'node_modules', 'sdk/rust/target'] },
7
+ {
8
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
9
+ files: ['**/*.ts'],
10
+ languageOptions: {
11
+ ecmaVersion: 2022,
12
+ globals: { ...globals.node, ...globals.browser },
13
+ },
14
+ },
15
+ )
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Fuzz harness: wire decoder resilience.
3
+ *
4
+ * Invariant: decodeEnvelope must NEVER throw on any input — arbitrary strings
5
+ * and arbitrary bytes both. On garbage it must return {ok: false}.
6
+ *
7
+ * Strategy: start from the conformance corpus, apply mutations, run N rounds.
8
+ */
9
+
10
+ import { readFileSync } from 'node:fs'
11
+ import { join, dirname } from 'node:path'
12
+ import { fileURLToPath } from 'node:url'
13
+ import { decodeEnvelope } from '../../src/index.js'
14
+
15
+ const __dir = dirname(fileURLToPath(import.meta.url))
16
+ const VECTORS_PATH = join(__dir, '../../conformance/7h3_v0_1.json')
17
+ const ROUNDS = parseInt(process.env.FUZZ_ROUNDS ?? '50000', 10)
18
+
19
+ const { vectors, ed25519Vectors = [] } = JSON.parse(readFileSync(VECTORS_PATH, 'utf-8'))
20
+
21
+ // --- corpus: valid JSON and compact encodings from conformance vectors ---
22
+ const corpus: (string | Uint8Array)[] = []
23
+ for (const v of [...vectors, ...ed25519Vectors]) {
24
+ const env = v.envelope
25
+ corpus.push(JSON.stringify(env))
26
+ // compact encoding
27
+ corpus.push(
28
+ JSON.stringify({
29
+ v: env.header?.version,
30
+ mid: env.header?.messageId,
31
+ ts: env.header?.timestampMs,
32
+ ttl: env.header?.ttlMs,
33
+ s: env.header?.sender,
34
+ n: env.header?.nonce,
35
+ i: env.body?.intent,
36
+ c: env.body?.content,
37
+ }),
38
+ )
39
+ }
40
+ // seed with known-bad corpus
41
+ corpus.push('', '{}', '[]', 'null', '"string"', '{', '{"header":{}}', '{"header":{},"body":{}}')
42
+
43
+ // --- mutators ---
44
+ type Mutator = (input: string, rand: () => number) => string
45
+
46
+ const mutators: Mutator[] = [
47
+ // bit flip a byte
48
+ (s, r) => {
49
+ const buf = Buffer.from(s, 'utf-8')
50
+ if (!buf.length) return s
51
+ buf[Math.floor(r() * buf.length)] ^= 1 << Math.floor(r() * 8)
52
+ return buf.toString('utf-8')
53
+ },
54
+ // insert random byte
55
+ (s, r) => {
56
+ const buf = Buffer.from(s, 'utf-8')
57
+ const idx = Math.floor(r() * (buf.length + 1))
58
+ const b = Math.floor(r() * 256)
59
+ return Buffer.concat([buf.subarray(0, idx), Buffer.from([b]), buf.subarray(idx)]).toString('utf-8')
60
+ },
61
+ // delete a byte
62
+ (s, r) => {
63
+ const buf = Buffer.from(s, 'utf-8')
64
+ if (!buf.length) return s
65
+ const idx = Math.floor(r() * buf.length)
66
+ return Buffer.concat([buf.subarray(0, idx), buf.subarray(idx + 1)]).toString('utf-8')
67
+ },
68
+ // truncate
69
+ (s, r) => {
70
+ const buf = Buffer.from(s, 'utf-8')
71
+ return buf.subarray(0, Math.floor(r() * buf.length)).toString('utf-8')
72
+ },
73
+ // mutate a JSON field
74
+ (s, r) => {
75
+ try {
76
+ const obj = JSON.parse(s) as Record<string, unknown>
77
+ const keys = Object.keys(obj)
78
+ if (!keys.length) return s
79
+ const key = keys[Math.floor(r() * keys.length)]
80
+ const replacement = [null, 0, 1, -1, '', [], {}, true, false][Math.floor(r() * 9)]
81
+ obj[key] = replacement
82
+ return JSON.stringify(obj)
83
+ } catch {
84
+ return s
85
+ }
86
+ },
87
+ // replace a known token with another
88
+ (s, r) => {
89
+ const tokens = ['7h3/0.1', 'TASK', 'PING', 'RESULT', 'HS256', 'ED25519', '"sender"', '"body"']
90
+ const replacements = ['aip/0.2', 'INVALID', '', '0', 'NONE', 'RSA', '"SENDER"', '"Body"']
91
+ const i = Math.floor(r() * tokens.length)
92
+ return s.split(tokens[i]).join(replacements[i])
93
+ },
94
+ ]
95
+
96
+ // --- runner ---
97
+ let crashes = 0
98
+ let okFalse = 0
99
+ let okTrue = 0
100
+
101
+ function rand(): number {
102
+ return Math.random()
103
+ }
104
+
105
+ let current = corpus[0] as string
106
+
107
+ for (let i = 0; i < ROUNDS; i++) {
108
+ // pick a mutator and apply it
109
+ const mutator = mutators[Math.floor(rand() * mutators.length)]
110
+ current = mutator(current, rand)
111
+
112
+ // occasionally reset to a fresh corpus item
113
+ if (i % 500 === 0) {
114
+ current = corpus[Math.floor(rand() * corpus.length)] as string
115
+ }
116
+
117
+ try {
118
+ const result = decodeEnvelope(current)
119
+ if (result.ok) {
120
+ okTrue++
121
+ } else {
122
+ okFalse++
123
+ }
124
+ } catch (err) {
125
+ crashes++
126
+ console.error(`\nCRASH at round ${i}:`)
127
+ console.error(' Input (first 200 chars):', JSON.stringify(current.slice(0, 200)))
128
+ console.error(' Error:', err)
129
+ }
130
+ }
131
+
132
+ console.log(`[harness-decode] rounds=${ROUNDS} ok=${okTrue} ok:false=${okFalse} crashes=${crashes}`)
133
+ if (crashes > 0) {
134
+ console.error(`FAIL: ${crashes} crash(es) detected`)
135
+ process.exit(1)
136
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Fuzz harness: signature verification with tampered envelopes.
3
+ *
4
+ * Invariants:
5
+ * 1. verifyEnvelopeHmac must NEVER throw — tampered or malformed envelopes
6
+ * must return false, not crash.
7
+ * 2. A valid envelope always verifies as true (regression check).
8
+ * 3. Single-byte tampering of any signed field must make verification fail.
9
+ *
10
+ * Strategy: start from valid signed envelopes, apply structural and byte mutations.
11
+ */
12
+
13
+ import { readFileSync } from 'node:fs'
14
+ import { join, dirname } from 'node:path'
15
+ import { fileURLToPath } from 'node:url'
16
+ import { createEnvelope, signEnvelopeHmac, verifyEnvelopeHmac } from '../../src/index.js'
17
+
18
+ const __dir = dirname(fileURLToPath(import.meta.url))
19
+ const VECTORS_PATH = join(__dir, '../../conformance/7h3_v0_1.json')
20
+ const ROUNDS = parseInt(process.env.FUZZ_ROUNDS ?? '20000', 10)
21
+
22
+ const { vectors } = JSON.parse(readFileSync(VECTORS_PATH, 'utf-8'))
23
+
24
+ const SECRET = vectors[0].secret as string
25
+
26
+ // Build and sign a fresh envelope so we have a known-valid baseline
27
+ const BASE_ENVELOPE = await signEnvelopeHmac(
28
+ createEnvelope({ sender: 'fuzzer', recipient: 'target', intent: 'TASK', content: 'baseline' }),
29
+ SECRET,
30
+ )
31
+
32
+ // Regression: valid envelope must verify
33
+ const baseOk = await verifyEnvelopeHmac(BASE_ENVELOPE, SECRET)
34
+ if (!baseOk) {
35
+ console.error('FAIL: baseline verification returned false — test setup error')
36
+ process.exit(1)
37
+ }
38
+
39
+ // --- corpus of tampered envelopes ---
40
+ function deepClone<T>(x: T): T {
41
+ return JSON.parse(JSON.stringify(x)) as T
42
+ }
43
+
44
+ type Env = typeof BASE_ENVELOPE
45
+
46
+ async function tamper(env: Env, rand: () => number): Promise<Env> {
47
+ const e = deepClone(env)
48
+ const target = rand() < 0.5 ? 'header' : 'body'
49
+ const obj = e[target] as unknown as Record<string, unknown>
50
+ const keys = Object.keys(obj)
51
+ const key = keys[Math.floor(rand() * keys.length)]
52
+ const v = obj[key]
53
+ // Mutate the value
54
+ if (typeof v === 'string') {
55
+ if (v.length === 0) {
56
+ obj[key] = 'mutated'
57
+ } else {
58
+ const buf = Buffer.from(v, 'utf-8')
59
+ const idx = Math.floor(rand() * buf.length)
60
+ buf[idx] ^= 1 << Math.floor(rand() * 8)
61
+ obj[key] = buf.toString('utf-8')
62
+ }
63
+ } else if (typeof v === 'number') {
64
+ // Ensure delta is non-zero: map [0, 999] → [-500, -1] ∪ [1, 500]
65
+ const raw = Math.floor(rand() * 1000)
66
+ const delta = raw < 500 ? raw - 500 : raw - 499 // skips 0
67
+ obj[key] = v + delta
68
+ } else {
69
+ obj[key] = null
70
+ }
71
+
72
+ // Guard: if the mutation produced no net change (e.g. bit flip round-tripped
73
+ // through invalid UTF-8 to the same replacement character), force a known change.
74
+ if (JSON.stringify(e) === JSON.stringify(env)) {
75
+ const guard = e.body as unknown as Record<string, unknown>
76
+ guard['content'] = '__forced_tamper__'
77
+ }
78
+
79
+ return e
80
+ }
81
+
82
+ // --- runner ---
83
+ let crashes = 0
84
+ let tamperFalsePositive = 0
85
+
86
+ for (let i = 0; i < ROUNDS; i++) {
87
+ const rand = () => Math.random()
88
+ const tampered = await tamper(BASE_ENVELOPE, rand)
89
+
90
+ try {
91
+ const result = await verifyEnvelopeHmac(tampered, SECRET)
92
+ if (result === true) {
93
+ tamperFalsePositive++
94
+ console.error(`\nFAIL false positive at round ${i} — tampered envelope verified as ok`)
95
+ console.error(' Tampered:', JSON.stringify(tampered).slice(0, 300))
96
+ }
97
+ } catch (err) {
98
+ crashes++
99
+ console.error(`\nCRASH at round ${i}:`)
100
+ console.error(' Input:', JSON.stringify(tampered).slice(0, 300))
101
+ console.error(' Error:', err)
102
+ }
103
+
104
+ // Occasionally test with a structurally broken envelope (missing fields)
105
+ if (i % 200 === 0) {
106
+ const broken = { header: {}, body: {}, signature: BASE_ENVELOPE.signature } as unknown as Env
107
+ try {
108
+ await verifyEnvelopeHmac(broken, SECRET)
109
+ } catch (err) {
110
+ crashes++
111
+ console.error(`\nCRASH (broken envelope) at round ${i}:`, err)
112
+ }
113
+ }
114
+ }
115
+
116
+ console.log(
117
+ `[harness-verify] rounds=${ROUNDS} tamper-false-positives=${tamperFalsePositive} crashes=${crashes}`,
118
+ )
119
+ if (crashes > 0 || tamperFalsePositive > 0) {
120
+ process.exit(1)
121
+ }
package/fuzz/ts/run.ts ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Fuzz campaign runner — executes all TypeScript harnesses in sequence.
3
+ *
4
+ * Usage:
5
+ * npm run fuzz:ts # default 50k/20k rounds
6
+ * FUZZ_ROUNDS=200000 npm run fuzz:ts
7
+ */
8
+
9
+ import { spawnSync } from 'node:child_process'
10
+ import { dirname, join } from 'node:path'
11
+ import { fileURLToPath } from 'node:url'
12
+
13
+ const __dir = dirname(fileURLToPath(import.meta.url))
14
+
15
+ const HARNESSES = ['harness-decode.ts', 'harness-verify.ts']
16
+
17
+ let failed = false
18
+ for (const harness of HARNESSES) {
19
+ process.stdout.write(`Running ${harness} ... `)
20
+ const result = spawnSync('npx', ['tsx', join(__dir, harness)], {
21
+ stdio: ['ignore', 'pipe', 'pipe'],
22
+ env: { ...process.env },
23
+ })
24
+ const out = result.stdout.toString().trim()
25
+ const err = result.stderr.toString().trim()
26
+ if (result.status !== 0) {
27
+ console.error(`FAIL\n${out}\n${err}`)
28
+ failed = true
29
+ } else {
30
+ console.log(`PASS ${out}`)
31
+ if (err) console.error(err)
32
+ }
33
+ }
34
+
35
+ process.exit(failed ? 1 : 0)
@@ -0,0 +1,38 @@
1
+ # @7h3/protocol-mcp
2
+
3
+ MCP server for [@7h3/protocol](https://github.com/IceMasterT/7h3-protocol-aip) — install into Claude to generate AIP secrets, keypairs, and server boilerplate.
4
+
5
+ ## Install into Claude Code
6
+
7
+ ```bash
8
+ claude mcp add aip -- npx @7h3/protocol-mcp
9
+ ```
10
+
11
+ ## Install into Claude Desktop
12
+
13
+ Add to your `claude_desktop_config.json`:
14
+
15
+ ```json
16
+ {
17
+ "mcpServers": {
18
+ "aip": {
19
+ "command": "npx",
20
+ "args": ["@7h3/protocol-mcp"]
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ ## Tools
27
+
28
+ | Tool | What it does |
29
+ |---|---|
30
+ | `aip_generate_secret` | Generates a 32-byte HMAC secret — store as `AIP_SECRET` |
31
+ | `aip_generate_keypair` | Generates an Ed25519 keypair — store keys as env vars |
32
+ | `aip_wrap_mcp_server` | Outputs ready-to-paste boilerplate for your MCP server |
33
+ | `aip_sign` | Signs a test envelope (debugging / fixture generation) |
34
+ | `aip_verify` | Verifies an envelope signature and shape |
35
+
36
+ ## License
37
+
38
+ MIT