@7h3/protocol 0.5.0 → 0.5.2

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 (294) hide show
  1. package/README.md +6 -1317
  2. package/agentAdapter.d.ts +26 -0
  3. package/auditLog.d.ts +54 -0
  4. package/capability.d.ts +67 -0
  5. package/cborCodec.d.ts +34 -0
  6. package/conformanceVectors.d.ts +20 -0
  7. package/encryption.d.ts +85 -0
  8. package/envelopeCbor.d.ts +33 -0
  9. package/frameworkAdapters.d.ts +72 -0
  10. package/gateway.d.ts +53 -0
  11. package/grpcBinding.d.ts +29 -0
  12. package/httpBinding.d.ts +50 -0
  13. package/index.d.ts +39 -0
  14. package/index.js +3670 -0
  15. package/keyInfra.d.ts +64 -0
  16. package/keyRegistry.d.ts +9 -0
  17. package/keyRotation.d.ts +20 -0
  18. package/mcpGateway.d.ts +37 -0
  19. package/mcpTransports.d.ts +62 -0
  20. package/mcpWrapper.d.ts +83 -0
  21. package/otel.d.ts +65 -0
  22. package/package.json +13 -121
  23. package/policyEnforcer.d.ts +50 -0
  24. package/policyTelemetryFeedback.d.ts +11 -0
  25. package/{src/protocol.d.ts → protocol.d.ts} +6 -2
  26. package/protocolAgent.d.ts +58 -0
  27. package/protocolBinary.d.ts +8 -0
  28. package/protocolCapabilities.d.ts +24 -0
  29. package/protocolReplay.d.ts +35 -0
  30. package/protocolTransport.d.ts +73 -0
  31. package/queueBinding.d.ts +43 -0
  32. package/rateLimiter.d.ts +18 -0
  33. package/{src/redisClient.ts → redisClient.d.ts} +25 -75
  34. package/replayStores.d.ts +98 -0
  35. package/revocation.d.ts +71 -0
  36. package/routePolicy.d.ts +30 -0
  37. package/runtimePolicy.d.ts +24 -0
  38. package/runtimePolicyManager.d.ts +15 -0
  39. package/runtimePolicyPresets.d.ts +11 -0
  40. package/signedResponse.d.ts +21 -0
  41. package/stream.d.ts +59 -0
  42. package/telemetry.d.ts +79 -0
  43. package/webhookBinding.d.ts +30 -0
  44. package/wsBinding.d.ts +52 -0
  45. package/.dockerignore +0 -19
  46. package/.github/dependabot.yml +0 -32
  47. package/.github/workflows/ci.yml +0 -31
  48. package/.github/workflows/publish.yml +0 -59
  49. package/.github/workflows/scorecard.yml +0 -37
  50. package/7h3.example.yaml +0 -125
  51. package/CHANGELOG.md +0 -152
  52. package/CONTRIBUTING.md +0 -82
  53. package/Dockerfile +0 -73
  54. package/GOVERNANCE.md +0 -62
  55. package/SECURITY.md +0 -70
  56. package/bench-results/replay-cache-full-1777891033256.json +0 -10
  57. package/bench-results/replay-cache-full-1777896317488.json +0 -10
  58. package/bench-results/replay-cache-full-1777900993184.json +0 -10
  59. package/bench-results/replay-cache-full-1777901019285.json +0 -10
  60. package/bench-results/replay-cache-quick-1777870170126.json +0 -10
  61. package/bench-results/signature-profiles-quick-1775875160079.json +0 -85
  62. package/bench-results/signature-profiles-quick-1775983539716.json +0 -85
  63. package/bench-results/signature-profiles-quick-1776237913190.json +0 -85
  64. package/bench-results/wire-codecs-full-1777891019803.json +0 -93
  65. package/bench-results/wire-codecs-full-1777896260964.json +0 -93
  66. package/bench-results/wire-codecs-full-1777901004247.json +0 -93
  67. package/bench-results/wire-codecs-quick-1775972879056.json +0 -93
  68. package/bench-results/wire-codecs-quick-1775983541111.json +0 -93
  69. package/bench-results/wire-codecs-quick-1776237914299.json +0 -93
  70. package/bench-results/wire-codecs-quick-1777841285236.json +0 -93
  71. package/bench-results/wire-codecs-quick-1777841321772.json +0 -93
  72. package/bench-results/wire-codecs-quick-1777841330408.json +0 -93
  73. package/bench-results/wire-codecs-quick-1777852886082.json +0 -93
  74. package/bench-results/wire-codecs-quick-1777852988773.json +0 -93
  75. package/bench-results/wire-codecs-quick-1777870188095.json +0 -93
  76. package/bench-results/wire-codecs-quick-1777870263918.json +0 -93
  77. package/bench-results/wire-codecs-quick-1777870455034.json +0 -93
  78. package/bench-results/wire-codecs-quick-1778816163081.json +0 -93
  79. package/bench-results/wire-codecs-quick-1778843936130.json +0 -93
  80. package/bin/7h3.ts +0 -406
  81. package/conformance/7h3_v0_1.json +0 -77
  82. package/conformance/7h3_v0_1_binary.json +0 -20
  83. package/conformance/aip_v0_1_binary.json +0 -20
  84. package/docker-compose.yaml +0 -77
  85. package/docs/ADOPTION_PLAN.md +0 -120
  86. package/docs/AGENTS.md +0 -77
  87. package/docs/AIP_RFC_v0.1.md +0 -97
  88. package/docs/AI_DECISION_CARD.md +0 -122
  89. package/docs/AI_RUNTIME_POLICY.json +0 -126
  90. package/docs/AI_RUNTIME_POLICY.yaml +0 -110
  91. package/docs/BACKPRESSURE_TUNING.md +0 -65
  92. package/docs/BENCHMARK_CLAIM_MATRIX.md +0 -42
  93. package/docs/BENCHMARK_REPORT_TEMPLATE.md +0 -169
  94. package/docs/BINARY_CODEC_BENCH.md +0 -23
  95. package/docs/CLEAN_CLONE_RUNBOOK.md +0 -36
  96. package/docs/CLOCK_SKEW_POLICY.md +0 -30
  97. package/docs/DISTRIBUTED_REPLAY.md +0 -142
  98. package/docs/FUZZ_CAMPAIGN.md +0 -121
  99. package/docs/GATEWAY.md +0 -195
  100. package/docs/KEY_MANAGEMENT_POLICY.md +0 -53
  101. package/docs/KEY_REVOCATION.md +0 -69
  102. package/docs/MCP_WRAPPER.md +0 -159
  103. package/docs/MIGRATION_GUIDE.md +0 -40
  104. package/docs/OPERATORS.md +0 -184
  105. package/docs/PERF_REGRESSION_POLICY.md +0 -34
  106. package/docs/PROJECT_EXAMINATION_2026-05-31.md +0 -219
  107. package/docs/RELEASE_BENCHMARK_REPORT_2026-05-15.md +0 -135
  108. package/docs/RELEASE_GATE.md +0 -25
  109. package/docs/RELEASE_NOTES_v0.1.0.md +0 -54
  110. package/docs/SECURITY_REVIEW_2026-06-05.md +0 -165
  111. package/docs/TELEMETRY.md +0 -41
  112. package/docs/THREAT_MODEL.md +0 -89
  113. package/docs/VERSIONING_POLICY.md +0 -30
  114. package/docs/assets/banner-github.png +0 -0
  115. package/docs/assets/banner.png +0 -0
  116. package/docs/assets/banner.svg +0 -123
  117. package/eslint.config.js +0 -15
  118. package/fuzz/ts/harness-decode.ts +0 -136
  119. package/fuzz/ts/harness-verify.ts +0 -121
  120. package/fuzz/ts/run.ts +0 -35
  121. package/mcp-server/README.md +0 -38
  122. package/mcp-server/package-lock.json +0 -1187
  123. package/mcp-server/package.json +0 -35
  124. package/mcp-server/src/index.ts +0 -236
  125. package/mcp-server/tsconfig.json +0 -14
  126. package/scripts/aip-framework-quickstart.ts +0 -110
  127. package/scripts/aip-mcp-gateway.ts +0 -38
  128. package/scripts/aip-mcp-wrap-demo.ts +0 -72
  129. package/scripts/aip-quickstart.ts +0 -60
  130. package/scripts/bench-diff.ts +0 -118
  131. package/scripts/bench-protocol-e2e.ts +0 -937
  132. package/scripts/bench-protocol-openloop.ts +0 -1397
  133. package/scripts/bench-replay-cache.ts +0 -76
  134. package/scripts/bench-signature-profiles.ts +0 -180
  135. package/scripts/bench-wire-codecs.ts +0 -161
  136. package/scripts/build-binary-conformance.ts +0 -36
  137. package/scripts/build-release-dashboard.ts +0 -175
  138. package/scripts/canary-rollout.ts +0 -38
  139. package/scripts/mcpGatewayCli.test.ts +0 -116
  140. package/scripts/prepare-aip-package.ts +0 -88
  141. package/scripts/regen-conformance-sigs.ts +0 -18
  142. package/scripts/release-gate.ts +0 -19
  143. package/scripts/validate-runtime-policy.ts +0 -18
  144. package/sdk/browser/index.test.ts +0 -162
  145. package/sdk/browser/index.ts +0 -257
  146. package/sdk/browser/package.json +0 -13
  147. package/sdk/go/cbor.go +0 -551
  148. package/sdk/go/cbor_test.go +0 -232
  149. package/sdk/go/encryption.go +0 -280
  150. package/sdk/go/encryption_test.go +0 -318
  151. package/sdk/go/go.mod +0 -7
  152. package/sdk/go/go.sum +0 -4
  153. package/sdk/go/http.go +0 -135
  154. package/sdk/go/protocol.go +0 -324
  155. package/sdk/go/protocol_test.go +0 -334
  156. package/sdk/go/replay.go +0 -121
  157. package/sdk/go/replay_test.go +0 -149
  158. package/sdk/go/webhook.go +0 -136
  159. package/sdk/pq/package-lock.json +0 -1358
  160. package/sdk/pq/package.json +0 -42
  161. package/sdk/pq/src/index.test.ts +0 -143
  162. package/sdk/pq/src/index.ts +0 -166
  163. package/sdk/pq/tsconfig.json +0 -14
  164. package/sdk/pq/vitest.config.ts +0 -7
  165. package/sdk/python/README.md +0 -18
  166. package/sdk/python/protocol_7h3/__init__.py +0 -46
  167. package/sdk/python/protocol_7h3/encryption.py +0 -252
  168. package/sdk/python/protocol_7h3/http.py +0 -212
  169. package/sdk/python/protocol_7h3/keys.py +0 -149
  170. package/sdk/python/protocol_7h3/pq.py +0 -244
  171. package/sdk/python/protocol_7h3/protocol.py +0 -525
  172. package/sdk/python/protocol_7h3/queue.py +0 -118
  173. package/sdk/python/protocol_7h3/replay.py +0 -98
  174. package/sdk/python/protocol_7h3/webhook.py +0 -116
  175. package/sdk/python/pyproject.toml +0 -40
  176. package/sdk/python/tests/test_conformance.py +0 -110
  177. package/sdk/python/tests/test_encryption.py +0 -206
  178. package/sdk/python/tests/test_http.py +0 -305
  179. package/sdk/python/tests/test_keys.py +0 -417
  180. package/sdk/python/tests/test_queue.py +0 -120
  181. package/sdk/python/tests/test_webhook.py +0 -345
  182. package/sdk/rust/Cargo.lock +0 -371
  183. package/sdk/rust/Cargo.toml +0 -25
  184. package/sdk/rust/README.md +0 -31
  185. package/sdk/rust/fuzz/Cargo.toml +0 -29
  186. package/sdk/rust/fuzz/fuzz_targets/fuzz_canonicalize.rs +0 -46
  187. package/sdk/rust/fuzz/fuzz_targets/fuzz_decode.rs +0 -11
  188. package/sdk/rust/src/bin/aip_mcp_gateway.rs +0 -59
  189. package/sdk/rust/src/http.rs +0 -145
  190. package/sdk/rust/src/keys.rs +0 -161
  191. package/sdk/rust/src/lib.rs +0 -688
  192. package/sdk/rust/src/queue.rs +0 -79
  193. package/sdk/rust/src/webhook.rs +0 -86
  194. package/sdk/rust/tests/conformance.rs +0 -148
  195. package/sdk/rust/tests/gateway.rs +0 -130
  196. package/sdk/rust/tests/http_webhook_queue.rs +0 -201
  197. package/sdk/rust/tests/keys.rs +0 -189
  198. package/sdk/threshold/index.d.ts +0 -68
  199. package/sdk/threshold/index.d.ts.map +0 -1
  200. package/sdk/threshold/index.js +0 -254
  201. package/sdk/threshold/package-lock.json +0 -1361
  202. package/sdk/threshold/package.json +0 -39
  203. package/sdk/threshold/src/index.d.ts +0 -68
  204. package/sdk/threshold/src/index.d.ts.map +0 -1
  205. package/sdk/threshold/src/index.js +0 -254
  206. package/sdk/threshold/src/index.test.ts +0 -238
  207. package/sdk/threshold/src/index.ts +0 -355
  208. package/sdk/threshold/tsconfig.json +0 -19
  209. package/sdk/threshold/vitest.config.ts +0 -12
  210. package/src/agentAdapter.test.ts +0 -48
  211. package/src/agentAdapter.ts +0 -56
  212. package/src/auditLog.test.ts +0 -145
  213. package/src/auditLog.ts +0 -147
  214. package/src/capability.test.ts +0 -504
  215. package/src/capability.ts +0 -380
  216. package/src/cborCodec.test.ts +0 -263
  217. package/src/cborCodec.ts +0 -339
  218. package/src/conformance.test.ts +0 -136
  219. package/src/conformanceVectors.ts +0 -99
  220. package/src/encryption.test.ts +0 -206
  221. package/src/encryption.ts +0 -245
  222. package/src/envelopeCbor.ts +0 -140
  223. package/src/frameworkAdapters.test.ts +0 -290
  224. package/src/frameworkAdapters.ts +0 -261
  225. package/src/gateway.test.ts +0 -343
  226. package/src/gateway.ts +0 -246
  227. package/src/grpcBinding.test.ts +0 -211
  228. package/src/grpcBinding.ts +0 -103
  229. package/src/httpBinding.test.ts +0 -376
  230. package/src/httpBinding.ts +0 -189
  231. package/src/index.ts +0 -39
  232. package/src/keyInfra.test.ts +0 -278
  233. package/src/keyInfra.ts +0 -228
  234. package/src/keyRegistry.ts +0 -59
  235. package/src/keyRotation.test.ts +0 -78
  236. package/src/keyRotation.ts +0 -72
  237. package/src/mcpGateway.test.ts +0 -129
  238. package/src/mcpGateway.ts +0 -250
  239. package/src/mcpTransports.test.ts +0 -92
  240. package/src/mcpTransports.ts +0 -169
  241. package/src/mcpWrapper.test.ts +0 -179
  242. package/src/mcpWrapper.ts +0 -206
  243. package/src/otel.ts +0 -136
  244. package/src/policyEnforcer.test.ts +0 -99
  245. package/src/policyEnforcer.ts +0 -169
  246. package/src/policyTelemetryFeedback.test.ts +0 -25
  247. package/src/policyTelemetryFeedback.ts +0 -38
  248. package/src/protocol.bench.ts +0 -37
  249. package/src/protocol.d.ts.map +0 -1
  250. package/src/protocol.js +0 -294
  251. package/src/protocol.test.ts +0 -155
  252. package/src/protocol.ts +0 -414
  253. package/src/protocolAgent.test.ts +0 -105
  254. package/src/protocolAgent.ts +0 -169
  255. package/src/protocolBinary.test.ts +0 -165
  256. package/src/protocolBinary.ts +0 -312
  257. package/src/protocolCapabilities.ts +0 -70
  258. package/src/protocolFuzz.advanced.test.ts +0 -235
  259. package/src/protocolFuzz.test.ts +0 -111
  260. package/src/protocolNegative.test.ts +0 -97
  261. package/src/protocolReplay.test.ts +0 -71
  262. package/src/protocolReplay.ts +0 -194
  263. package/src/protocolTransport.test.ts +0 -556
  264. package/src/protocolTransport.ts +0 -483
  265. package/src/queueBinding.test.ts +0 -130
  266. package/src/queueBinding.ts +0 -102
  267. package/src/rateLimiter.test.ts +0 -96
  268. package/src/rateLimiter.ts +0 -46
  269. package/src/redisIntegration.test.ts +0 -134
  270. package/src/replayStores.test.ts +0 -273
  271. package/src/replayStores.ts +0 -215
  272. package/src/revocation.test.ts +0 -98
  273. package/src/revocation.ts +0 -0
  274. package/src/routePolicy.test.ts +0 -87
  275. package/src/routePolicy.ts +0 -72
  276. package/src/runtimePolicy.test.ts +0 -49
  277. package/src/runtimePolicy.ts +0 -81
  278. package/src/runtimePolicyManager.test.ts +0 -29
  279. package/src/runtimePolicyManager.ts +0 -50
  280. package/src/runtimePolicyPresets.ts +0 -43
  281. package/src/signedResponse.test.ts +0 -111
  282. package/src/signedResponse.ts +0 -83
  283. package/src/stream.test.ts +0 -254
  284. package/src/stream.ts +0 -417
  285. package/src/telemetry.test.ts +0 -251
  286. package/src/telemetry.ts +0 -299
  287. package/src/webhookBinding.test.ts +0 -144
  288. package/src/webhookBinding.ts +0 -115
  289. package/src/wsBinding.test.ts +0 -221
  290. package/src/wsBinding.ts +0 -200
  291. package/tsconfig.json +0 -15
  292. package/tsconfig.lib.json +0 -23
  293. package/vite.lib.config.ts +0 -16
  294. package/vitest.config.ts +0 -11
@@ -1,121 +0,0 @@
1
- # Fuzz Campaign
2
-
3
- AIP employs two complementary fuzzing strategies: mutation-based harnesses for TypeScript and coverage-guided libFuzzer targets for Rust.
4
-
5
- ---
6
-
7
- ## TypeScript — mutation-based harnesses
8
-
9
- **Location:** `fuzz/ts/`
10
-
11
- **Run:**
12
- ```bash
13
- npm run fuzz:ts # both harnesses, default rounds
14
- FUZZ_ROUNDS=200000 npm run fuzz:ts # heavier run
15
- npm run fuzz:ts:decode # decoder only
16
- npm run fuzz:ts:verify # verifier only
17
- ```
18
-
19
- ### Harness: `harness-decode.ts`
20
-
21
- **Target:** `decodeEnvelope`
22
- **Invariant:** Must never throw on any input — garbage returns `{ok: false}`, not a crash.
23
-
24
- **Strategy:**
25
- 1. Seed corpus: conformance vectors (JSON full form + compact form) + known-bad inputs (`{}`, `[]`, `null`, `"string"`, `{`, etc.)
26
- 2. Each round: pick a mutator, apply it to the current input
27
- 3. Reset to a fresh corpus item every 500 rounds
28
-
29
- **Mutators:**
30
- - Bit flip at a random byte position
31
- - Random byte insertion
32
- - Random byte deletion
33
- - Truncation to a random prefix length
34
- - JSON field mutation (null, type change)
35
- - Token substitution (e.g. `"aip/0.1"` → `"aip/0.2"`)
36
-
37
- **Initial run result (2026-06-05):**
38
- ```
39
- [harness-decode] rounds=50000 ok=98 ok:false=49902 crashes=0
40
- ```
41
- No crashes. Decoder tolerates all 50,000 mutated inputs.
42
-
43
- ---
44
-
45
- ### Harness: `harness-verify.ts`
46
-
47
- **Target:** `verifyEnvelopeHmac`
48
- **Invariants:**
49
- 1. Must never throw on a tampered envelope.
50
- 2. Any tampered envelope must verify as `false` (no false positives).
51
-
52
- **Strategy:**
53
- 1. Sign a fresh baseline envelope
54
- 2. Each round: deep-clone and randomly mutate one field in `header` or `body`
55
- 3. Guard: if mutation produced no net change, force `body.content` to a sentinel value
56
- 4. Every 200 rounds: test with a fully broken envelope (`{}` headers/body)
57
-
58
- **Mutation operators:**
59
- - String fields: bit flip a random byte, UTF-8 round-tripped (invalid sequences → replacement char)
60
- - Numeric fields: add a non-zero delta (−500 to −1 or +1 to +500)
61
- - Optional fields: set to `null`
62
-
63
- **Initial run result (2026-06-05):**
64
- ```
65
- [harness-verify] rounds=20000 tamper-false-positives=0 crashes=0
66
- ```
67
- No crashes, no false positives. No tampered envelope verified as valid.
68
-
69
- ---
70
-
71
- ## Rust — cargo-fuzz (libFuzzer)
72
-
73
- **Location:** `sdk/rust/fuzz/`
74
-
75
- **Prerequisites:**
76
- ```bash
77
- rustup toolchain install nightly
78
- cargo install cargo-fuzz
79
- ```
80
-
81
- **Run:**
82
- ```bash
83
- npm run fuzz:rust:decode # 60-second decode fuzzing session
84
- npm run fuzz:rust:canonicalize # 60-second canonicalize fuzzing session
85
-
86
- # Or run directly:
87
- cargo +nightly fuzz run fuzz_decode --manifest-path sdk/rust/fuzz/Cargo.toml
88
- cargo +nightly fuzz run fuzz_canonicalize --manifest-path sdk/rust/fuzz/Cargo.toml
89
- ```
90
-
91
- ### Target: `fuzz_decode`
92
-
93
- **Target:** `aip7h3::decode_envelope`
94
- **Invariant:** Must never panic on any UTF-8-valid input — errors are `Err(...)`, not panics.
95
-
96
- Input: arbitrary byte sequences converted to UTF-8 via `std::str::from_utf8`.
97
-
98
- ### Target: `fuzz_canonicalize`
99
-
100
- **Target:** `aip7h3::canonicalize_envelope`
101
- **Invariant:** Calling it twice on the same envelope returns the same string (idempotence / purity).
102
-
103
- Input: fuzz bytes carved into envelope fields (`messageId`, `content`) to exercise the canonicalization path under arbitrary field content.
104
-
105
- ---
106
-
107
- ## Coverage gaps and known limitations
108
-
109
- | Gap | Notes |
110
- |---|---|
111
- | TypeScript fuzzing is mutation-based, not coverage-guided | Coverage-guided fuzzing (e.g. jazzer.js) requires Java; out of scope for CI. The mutation harnesses provide meaningful parser boundary coverage without the dependency. |
112
- | Binary decoder (`decodeEnvelope` with `Uint8Array`) | The existing property-based tests (fast-check) cover this path (`src/protocolFuzz.advanced.test.ts`). A dedicated fuzz harness is a future addition. |
113
- | Ed25519 verification paths | These depend on WebCrypto internals; crash surface is on the envelope parsing/canonicalization layer (covered). |
114
- | No crash corpus retained | Extend the harnesses with a `fuzz/corpus/` directory to persist and replay interesting inputs. |
115
- | Python | No standalone fuzz harness — Python correctness is covered by conformance vectors and the property-based TS tests. |
116
-
117
- ---
118
-
119
- ## Pre-release fuzz policy
120
-
121
- Per `docs/RELEASE_GATE.md`, at minimum the TypeScript harnesses run at default round counts as part of every release gate check. Rust fuzzing is optional for patch releases and required for minor/major bumps.
package/docs/GATEWAY.md DELETED
@@ -1,195 +0,0 @@
1
- # 7h3 Protocol — Gateway Quick-Start
2
-
3
- The 7h3 gateway is a verifying HTTP reverse proxy. It sits in front of your
4
- service, intercepts every request, checks that the AIP envelope header carries
5
- a valid Ed25519 (or HMAC) signature from a known sender, enforces per-route
6
- policies and rate limits, optionally signs upstream responses, then forwards
7
- clean HTTP to your application.
8
-
9
- ---
10
-
11
- ## 1. Install
12
-
13
- ```bash
14
- npm install @7h3/protocol
15
- ```
16
-
17
- `tsx` is required to run the CLI from source. It is listed as a dev
18
- dependency and is also installed globally in the Docker image.
19
-
20
- ```bash
21
- npm install -g tsx # if running outside Docker
22
- ```
23
-
24
- ---
25
-
26
- ## 2. In-Process Middleware
27
-
28
- Drop the gateway into an existing Express (or any Node.js `http`) server in
29
- three lines:
30
-
31
- ```ts
32
- import express from 'express'
33
- import { createGateway } from '@7h3/protocol/gateway'
34
- import { createStaticKeyRegistry } from '@7h3/protocol/key-registry'
35
-
36
- const app = express()
37
-
38
- const registry = createStaticKeyRegistry({
39
- 'agent@example.com': process.env.AGENT_PUBLIC_KEY!,
40
- })
41
-
42
- const gateway = createGateway({
43
- upstream: 'http://internal-api:3000',
44
- keyRegistry: registry,
45
- defaultPolicy: 'deny',
46
- })
47
-
48
- // Verify every request before it reaches your routes
49
- app.use(async (req, res, next) => {
50
- const headers: Record<string, string> = {}
51
- for (const [k, v] of Object.entries(req.headers)) {
52
- if (typeof v === 'string') headers[k] = v
53
- else if (Array.isArray(v)) headers[k] = v[0]
54
- }
55
- const outcome = await gateway.verify({ method: req.method, path: req.path, headers })
56
- if (!outcome.ok) return res.status(outcome.status).json({ error: outcome.reason })
57
- res.locals.sender = outcome.sender
58
- next()
59
- })
60
- ```
61
-
62
- ---
63
-
64
- ## 3. Gateway Mode (CLI / Standalone Proxy)
65
-
66
- ### 3a. Generate a keypair
67
-
68
- ```bash
69
- 7h3 keygen --output keys.json
70
- ```
71
-
72
- This writes a JSON file with `publicKey` and `privateKey` (both base64url-encoded
73
- Ed25519 SPKI/PKCS8). Store `privateKey` in a secret; share `publicKey` with
74
- the agents that will call your service.
75
-
76
- ### 3b. Start the gateway
77
-
78
- ```bash
79
- 7h3 gateway \
80
- --upstream http://localhost:3000 \
81
- --public-key <base64url-SPKI> \
82
- --port 8080 \
83
- --require ed25519
84
- ```
85
-
86
- Full options:
87
-
88
- | Flag | Default | Description |
89
- |---|---|---|
90
- | `--upstream <url>` | _(required)_ | Upstream service URL |
91
- | `--port <n>` | `8080` | Port to listen on |
92
- | `--require ed25519\|none` | `ed25519` if `--public-key` is set, else `none` | Signature mode |
93
- | `--public-key <key>` | — | Ed25519 SPKI public key (base64url) |
94
- | `--sign-responses` | `false` | Sign every proxied response |
95
- | `--private-key <key>` | — | Private key for response signing |
96
- | `--sender <id>` | — | Sender ID attached to signed responses |
97
-
98
- ---
99
-
100
- ## 4. Docker
101
-
102
- ```bash
103
- # Bring up the gateway + example upstream
104
- docker compose up --build
105
-
106
- # Test with a health probe (no signature required)
107
- curl http://localhost:8080/health
108
-
109
- # Generate keys, then call a signed route
110
- 7h3 keygen --output keys.json
111
- PRIV=$(jq -r .privateKey keys.json)
112
- PUB=$(jq -r .publicKey keys.json)
113
-
114
- ENVELOPE=$(7h3 sign --private-key "$PRIV" --sender agent@example.com --payload '{"q":"hello"}')
115
- curl -H "x-7h3-envelope: $ENVELOPE" http://localhost:8080/api/hello
116
- ```
117
-
118
- To enable response signing, pass environment variables to the gateway service
119
- in `docker-compose.yaml`:
120
-
121
- ```yaml
122
- environment:
123
- GATEWAY_PRIVATE_KEY: <your-private-key>
124
- GATEWAY_SENDER: gateway@my-service.example.com
125
- ```
126
-
127
- ---
128
-
129
- ## 5. Config Reference (YAML)
130
-
131
- See `7h3.example.yaml` for a fully-annotated configuration file.
132
-
133
- | Field | Type | Default | Description |
134
- |---|---|---|---|
135
- | `upstream` | string | _(required)_ | Upstream base URL |
136
- | `port` | number | `8080` | Listening port |
137
- | `sender` | string | — | Gateway sender identity for signed responses |
138
- | `sign_responses` | boolean | `false` | Sign every proxied response |
139
- | `default_policy` | `allow` \| `deny` | `allow` | Behaviour when no route policy matches |
140
- | `keys.private_key` | string | — | Gateway Ed25519 private key (base64url PKCS8) |
141
- | `keys.registry` | map | `{}` | `senderID → publicKey` (base64url SPKI) |
142
- | `policies[].path` | glob string | _(required)_ | Route glob (`**` crosses slashes) |
143
- | `policies[].require` | `ed25519` \| `hmac` \| `any` \| `none` | _(required)_ | Signature algorithm |
144
- | `policies[].allowed_senders` | string[] | _(all)_ | Allowlist of sender IDs |
145
- | `policies[].rate_limit.requests` | number | — | Max requests in window |
146
- | `policies[].rate_limit.window_ms` | number | — | Sliding window in ms |
147
- | `policies[].sign_response` | boolean | — | Per-route response signing override |
148
-
149
- ---
150
-
151
- ## 6. Client-Side Signing
152
-
153
- Agents calling a 7h3-protected gateway must attach a signed AIP envelope to
154
- every request. Use the TypeScript SDK:
155
-
156
- ```ts
157
- import { createEnvelope, signEnvelopeEd25519 } from '@7h3/protocol'
158
- import { attachEnvelopeToHeaders } from '@7h3/protocol/http'
159
-
160
- async function signedFetch(url: string, body: string, privateKey: string) {
161
- const envelope = createEnvelope({
162
- sender: 'agent@example.com',
163
- recipient: 'gateway@my-service.example.com',
164
- intent: 'REQUEST',
165
- content: body,
166
- ttlMs: 60_000,
167
- })
168
-
169
- const signed = await signEnvelopeEd25519(envelope, privateKey)
170
- const headers = attachEnvelopeToHeaders(signed, {}) // adds x-7h3-envelope
171
-
172
- return fetch(url, {
173
- method: 'POST',
174
- headers: { 'content-type': 'application/json', ...headers },
175
- body,
176
- })
177
- }
178
- ```
179
-
180
- The gateway validates the envelope header, verifies the Ed25519 signature
181
- against the sender's registered public key, checks replay protection, and only
182
- then forwards the request to the upstream service.
183
-
184
- ---
185
-
186
- ## 7. Policy Examples
187
-
188
- | Use Case | `path` | `require` | `allowed_senders` | `rate_limit` |
189
- |---|---|---|---|---|
190
- | Public health probe | `/health` | `none` | — | — |
191
- | Open public API | `/public/**` | `none` | — | — |
192
- | Any authenticated agent | `/api/**` | `ed25519` | — | 1 000 req / 60 s |
193
- | Named admin agents only | `/api/admin/**` | `ed25519` | `admin-agent@…`, `orchestrator@…` | — |
194
- | Partner integration | `/api/partner/**` | `ed25519` | `partner-bot@partner.example.com` | 200 req / 60 s |
195
- | Webhook receiver | `/webhooks/**` | `hmac` | — | 500 req / 60 s |
@@ -1,53 +0,0 @@
1
- # 7h3 Protocol AIP Key Management Policy (v1.0 draft)
2
-
3
- ## Supported signature profiles
4
-
5
- - `HS256` (shared secret)
6
- - `ED25519` (asymmetric keypair)
7
-
8
- ## Key ID rules
9
-
10
- - Every signing key must have a unique `keyId`.
11
- - Verifiers must resolve verification material by both `keyId` and `sender`.
12
- - Reusing the same `keyId` for different senders is prohibited.
13
-
14
- ## Rotation policy
15
-
16
- - Rotation interval target:
17
- - `HS256`: every 30 days
18
- - `ED25519`: every 90 days
19
- - Rotation process:
20
- 1. Provision new key as active for signing.
21
- 2. Keep previous key in verify-only mode during overlap window.
22
- 3. End overlap and revoke old key.
23
-
24
- ## Revocation policy
25
-
26
- - Immediate revocation triggers:
27
- - suspected credential leak
28
- - host compromise
29
- - failed integrity investigation
30
- - Revoked keys must fail verification in resolver control plane within SLA.
31
- - For multi-node deployments, back revocation with a shared `RevocationStore`
32
- (`createRedisRevocationStore`) and wrap the verification resolver with
33
- `withRevocationCheck` so a revoke on one node is enforced fleet-wide. The
34
- store fails **closed** by default: if the revocation list is unreachable, the
35
- key is treated as revoked. See `KEY_REVOCATION.md`.
36
-
37
- ## Storage requirements
38
-
39
- - Do not commit secrets/private keys to source control.
40
- - Store key material in managed secrets/KMS.
41
- - Restrict read access to signing services and verification control plane.
42
-
43
- ## Runtime policy
44
-
45
- - Production endpoints require signatures by default.
46
- - `HS256` is recommended for tightly controlled private clusters.
47
- - `ED25519` is recommended for cross-domain or multi-tenant federation.
48
- - Use `RollingKeyring` (`src/keyRotation.ts`) for overlap windows, verify-only periods, and revocation enforcement.
49
-
50
- ## Audit requirements
51
-
52
- - Log key activation, rotation, revocation, and failed verification events.
53
- - Retain audit logs for incident response and compliance windows.
@@ -1,69 +0,0 @@
1
- # Fleet-Wide Key Revocation
2
-
3
- `RollingKeyring` (`src/keyRotation.ts`) enforces revocation and `notBefore`/
4
- `notAfter` expiry **in-process**. For horizontally scaled deployments, a shared
5
- `RevocationStore` makes a revocation effective across every node: revoke a
6
- `(sender, keyId)` on one gateway and all gateways that consult the same store
7
- reject it.
8
-
9
- Source: `src/revocation.ts`.
10
-
11
- ## Interfaces
12
-
13
- - `RevocationStore` — `isRevoked(sender, keyId, nowMs?)` and `revoke(sender, keyId, { untilMs? })`.
14
- - `InMemoryRevocationStore` — single-process implementation (tests, local dev, single node).
15
- - `createRedisRevocationStore` — Redis-backed shared store with cached reads.
16
- - `withRevocationCheck(resolver, store)` — wraps a signature resolver so a revoked key resolves to **no material**, which makes verification fail and the envelope is rejected.
17
-
18
- ## Usage
19
-
20
- ```ts
21
- import {
22
- RollingKeyring,
23
- createKeyringSignatureResolver,
24
- createRedisRevocationStore,
25
- withRevocationCheck,
26
- receiveEnvelope,
27
- } from '@7h3/protocol'
28
-
29
- const keyring = new RollingKeyring(records)
30
- const revocations = createRedisRevocationStore(redisLikeClient, {
31
- keyPrefix: 'aip:revoked:',
32
- errorBehavior: 'reject', // fail closed (default)
33
- cacheTtlMs: 5000,
34
- onDegraded: (err, ctx) => metrics.increment('aip.revocation.degraded'),
35
- })
36
-
37
- const signatureResolver = withRevocationCheck(
38
- createKeyringSignatureResolver(keyring),
39
- revocations,
40
- )
41
-
42
- await receiveEnvelope(rawEnvelope, { signatureResolver })
43
-
44
- // Operationally, on a suspected compromise:
45
- await revocations.revoke('agent.worker', 'agent.worker-k1') // permanent
46
- await revocations.revoke('agent.worker', 'agent.worker-k2', { untilMs: Date.now() + 86_400_000 }) // 24h
47
- ```
48
-
49
- ## Behavior on Redis outage (`errorBehavior`)
50
-
51
- | Value | On client error | Rationale |
52
- |---|---|---|
53
- | `reject` (default) | Treat the key as **revoked** (fail closed) | A revoked key is a compromised key — never accept on uncertainty |
54
- | `allow` | Treat the key as **not revoked** | Favor availability where the risk is acceptable |
55
-
56
- Reads are served from a short-lived local cache (`cacheTtlMs`), so:
57
-
58
- - the verify hot path does not hit Redis on every message (scalability), and
59
- - known revocations keep enforcing through a brief Redis outage (a stale cached
60
- `revoked: true` is still honored).
61
-
62
- Every degraded decision fires `onDegraded` — degradation is never silent.
63
-
64
- ## Operational guidance
65
-
66
- - Wire `withRevocationCheck` into every verifying node's `signatureResolver`.
67
- - Treat the `onDegraded` rate as a security signal; sustained degradation means
68
- revocations may not be propagating.
69
- - Keep `cacheTtlMs` short enough that a revocation propagates within your SLA.
@@ -1,159 +0,0 @@
1
- # MCP Hardening Wrapper
2
-
3
- MCP messages are plain JSON-RPC 2.0 with no signature and no replay protection.
4
- This wrapper puts a signed AIP envelope around every MCP message on the wire —
5
- authenticating it, TTL-bounding it, and replay-checking it — **without changing
6
- your MCP handler**. Your handler still receives plain JSON-RPC `{ method, params }`
7
- and returns plain results; AIP lives entirely on the wire.
8
-
9
- Source: `src/mcpWrapper.ts`. Runnable demo: `npm run aip:mcp:wrap`.
10
-
11
- ## Threat coverage
12
-
13
- | Threat | Covered by |
14
- |---|---|
15
- | Message tampering (params/method altered in flight) | Canonical signature over the envelope (HS256 / Ed25519) |
16
- | Replay / duplicate execution | `(sender, messageId, nonce)` replay cache + TTL; **on by default** (`InMemoryReplayCache` if none supplied) |
17
- | Cross-server relay (valid envelope for server A replayed to server B) | **Recipient binding** — the server runs the handler only when `recipient === selfAgentId` |
18
- | Response spoofing (another valid signer answers) | **Sender binding** — the client accepts a response only when `sender === peerAgentId` (peerAgentId is required) |
19
- | Response substitution (a valid response to request A returned for request B) | **Correlation binding** — the client requires `correlationId === the request's messageId` |
20
- | Compromised key still in use | Compose `withRevocationCheck` into `receive.signatureResolver` |
21
-
22
- `encodeRequest` returns `{ raw, messageId }`; pass `messageId` to `decodeResponse(raw, { expectCorrelationId })` to enforce the correlation binding (`wrapMcpClient` does this for you).
23
-
24
- ## Server side — wrap an existing handler
25
-
26
- ```ts
27
- import { wrapMcpServer, signEnvelopeHmac } from '@7h3/protocol'
28
- import { DistributedReplayCache, createRedisReplayStore } from '@7h3/protocol'
29
-
30
- // Your existing MCP handler — unchanged, AIP-unaware.
31
- async function myMcpServer(request) {
32
- if (request.method === 'tools/call') return { jsonrpc: '2.0', id: request.id, result: runTool(request.params) }
33
- return { jsonrpc: '2.0', id: request.id, error: { code: -32601, message: 'Method not found' } }
34
- }
35
-
36
- const secured = wrapMcpServer(myMcpServer, {
37
- selfAgentId: 'agent.mcp-server',
38
- sign: (e) => signEnvelopeHmac(e, process.env.AIP_SECRET!, 'server-k1'),
39
- receive: {
40
- secretResolver: async () => process.env.AIP_SECRET!,
41
- replayCache: new DistributedReplayCache(createRedisReplayStore(redisLikeClient)), // fleet-wide replay
42
- },
43
- })
44
-
45
- // `secured(rawWireEnvelope) => Promise<rawWireEnvelope>` — drop it into your stdio/HTTP transport read loop.
46
- ```
47
-
48
- ## HMAC vs Ed25519
49
-
50
- | | HMAC (HS256) | Ed25519 |
51
- |---|---|---|
52
- | Secret model | Shared secret — both sides can sign and verify | Keypair — only you can sign; peers verify with your public key |
53
- | Peer compromise | A compromised peer can forge messages | A compromised peer cannot forge your messages |
54
- | Key distribution | One secret distributed to all trusted peers | Public key is safe to publish; private key never leaves the signer |
55
- | When to use | Dev, testing, fully trusted internal infra | Production, multi-tenant, or any boundary where you do not control both sides |
56
-
57
- **Use HMAC when** you own both sides of the wire (same team, same trusted cluster) and want the simplest possible setup.
58
-
59
- **Use Ed25519 in production.** If a worker/peer node is compromised, the attacker gains verify capability only — they cannot impersonate the signer.
60
-
61
- Ed25519 server example (replace the HMAC `sign`/`receive` block above):
62
-
63
- ```ts
64
- import { wrapMcpServer, signEnvelopeEd25519 } from '@7h3/protocol'
65
-
66
- // Generate keys once: npx @7h3/protocol-mcp → aip_generate_keypair
67
- // Set AIP_PRIVATE_KEY (server) and distribute AIP_CLIENT_PUBLIC_KEY (client's pubkey)
68
-
69
- const secured = wrapMcpServer(myMcpServer, {
70
- selfAgentId: 'agent.mcp-server',
71
- sign: (e) => signEnvelopeEd25519(e, process.env.AIP_PRIVATE_KEY!, 'k1'),
72
- receive: {
73
- signatureResolver: async (sig) =>
74
- sig.alg === 'ED25519'
75
- ? { alg: 'ED25519' as const, publicKey: process.env.AIP_CLIENT_PUBLIC_KEY! }
76
- : undefined,
77
- },
78
- })
79
- ```
80
-
81
- ## Client side — sign requests, verify responses
82
-
83
- ```ts
84
- import { wrapMcpClient, signEnvelopeHmac } from '@7h3/protocol'
85
-
86
- const call = wrapMcpClient(transport.send /* (raw) => Promise<raw> */, {
87
- selfAgentId: 'agent.mcp-client',
88
- peerAgentId: 'agent.mcp-server',
89
- sign: (e) => signEnvelopeHmac(e, process.env.AIP_SECRET!, 'client-k1'),
90
- receive: { secretResolver: async () => process.env.AIP_SECRET! },
91
- })
92
-
93
- const result = await call({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'planner' } })
94
- // throws if the server response signature does not verify
95
- ```
96
-
97
- `createMcpClientCodec(options)` exposes `encodeRequest` / `decodeResponse` directly
98
- when you manage the transport send yourself. `encodeRequest` returns
99
- `{ raw, messageId }`; pass that `messageId` as `decodeResponse(raw, { expectCorrelationId })`
100
- to bind each response to its request. `peerAgentId` is required so responses can be
101
- bound to the expected sender.
102
-
103
- ## Composes with the rest of the stack
104
-
105
- The `receive` field is a full `ReceiveEnvelopeOptions`, so the wrapper inherits
106
- everything the transport pipeline offers:
107
-
108
- - **Fleet-wide replay** — pass a `DistributedReplayCache(createRedisReplayStore(...))`.
109
- - **Key revocation** — set `receive.signatureResolver = withRevocationCheck(resolver, revocationStore)`.
110
- - **Ed25519** — sign with `signEnvelopeEd25519` and resolve material via `signatureResolver`.
111
- - **Clock skew / TTL / telemetry** — `maxClockSkewMs`, `telemetry`, etc.
112
-
113
- ## Transports
114
-
115
- The wrapper is transport-agnostic (it operates on wire envelopes). Two adapters
116
- connect it to real transports; both keep the handler on plain JSON-RPC.
117
-
118
- ### stdio (newline-delimited)
119
-
120
- ```ts
121
- import { wrapMcpServer, serveMcpOverStdio, createStdioMcpClient, wrapMcpClient } from '@7h3/protocol'
122
-
123
- // server process: read envelopes from stdin, write replies to stdout
124
- serveMcpOverStdio(wrapMcpServer(myMcpServer, serverOpts)) // defaults to process.stdin/stdout
125
-
126
- // client process: spawn the server and talk to it
127
- const stdio = createStdioMcpClient({ input: child.stdout, output: child.stdin })
128
- const call = wrapMcpClient(stdio.send, clientOpts)
129
- const result = await call({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'planner' } })
130
- ```
131
-
132
- Stdio frames one envelope per line, so it supports the `json` and `compact` wire
133
- formats. For `binary`, use HTTP.
134
-
135
- ### HTTP
136
-
137
- ```ts
138
- import http from 'node:http'
139
- import { wrapMcpServer, createHttpMcpHandler, createHttpMcpClient, wrapMcpClient } from '@7h3/protocol'
140
-
141
- // server: any node:http-compatible host
142
- http.createServer(createHttpMcpHandler(wrapMcpServer(myMcpServer, serverOpts))).listen(8787)
143
-
144
- // client: POSTs each request envelope, returns the reply envelope (global fetch)
145
- const httpClient = createHttpMcpClient({ url: 'http://localhost:8787' })
146
- const call = wrapMcpClient(httpClient.send, clientOpts)
147
- const result = await call({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'planner' } })
148
- ```
149
-
150
- Pass `{ binary: true }` to both `createHttpMcpHandler` and `createHttpMcpClient`
151
- to use the `binary` wire format over HTTP.
152
-
153
- ## Notes
154
-
155
- - Both peers must be wrapped: the on-wire message is an AIP envelope, not plain
156
- JSON-RPC. For interop with un-wrapped MCP peers, terminate AIP at a gateway.
157
- - `wireFormat` defaults to `compact`; use `binary` for the highest-throughput lanes.
158
- - Verification failures and replays come back as signed JSON-RPC errors
159
- (`-32600`), so the client still gets an authenticated, well-formed response.
@@ -1,40 +0,0 @@
1
- # AIP Migration Guide
2
-
3
- ## Import path migration (JS/TS)
4
-
5
- Use the stable package subpath export:
6
-
7
- ```ts
8
- import { createAipAgentAdapter, receiveEnvelope } from '@7h3/protocol'
9
- ```
10
-
11
- Avoid repo-relative imports in consumer apps.
12
-
13
- ## Signature profile migration
14
-
15
- ### HS256 to ED25519
16
-
17
- 1. Generate/provision ED25519 key pairs.
18
- 2. Keep HS256 verification active during overlap window.
19
- 3. Start signing outbound envelopes with ED25519.
20
- 4. Monitor verify failures by algorithm.
21
- 5. Revoke/decommission HS256 keys after migration window.
22
-
23
- ## Replay cache migration
24
-
25
- ### Single-node to distributed
26
-
27
- 1. Replace `InMemoryReplayCache` with `DistributedReplayCache`.
28
- 2. Implement atomic reserve in shared store (for example Redis `SET NX PX`).
29
- 3. Roll out gradually and monitor replay reject metrics.
30
-
31
- ## Clock skew migration
32
-
33
- 1. Start with default `maxClockSkewMs` (30s).
34
- 2. Tighten to lower values in synchronized environments.
35
- 3. Monitor `rejected_clock_skew` telemetry for false-positive tuning.
36
-
37
- ## Rollback guidance
38
-
39
- - Keep prior verification profiles and key material during rollout.
40
- - Do not remove old verifiers until telemetry confirms stable traffic.