@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,195 @@
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 |
@@ -0,0 +1,53 @@
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.
@@ -0,0 +1,69 @@
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.
@@ -0,0 +1,159 @@
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.
@@ -0,0 +1,40 @@
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.
@@ -0,0 +1,184 @@
1
+ # GLUV Operators Guide
2
+
3
+ This guide is for SRE, platform, and performance teams operating GLUV-backed workloads in production.
4
+
5
+ ---
6
+
7
+ ## 1) Operational objectives
8
+
9
+ Primary objectives:
10
+
11
+ 1. Maintain low drop-rate under declared load profiles
12
+ 2. Maintain p99 latency SLO
13
+ 3. Preserve signature/replay guarantees under stress
14
+ 4. Detect and stop regressions before release
15
+
16
+ ---
17
+
18
+ ## 2) SLO model
19
+
20
+ Define SLOs per traffic class (example baseline):
21
+
22
+ - **Availability:** protocol receive path success ≥ 99.9%
23
+ - **Drop rate:** `dropPct <= 0.1%` (or 0% for critical lanes)
24
+ - **Latency:** p99 <= 20ms (adjust by payload and transport class)
25
+ - **Integrity:** 100% signature verification and replay checks on ingress
26
+
27
+ Important: “no drop” means **within declared operating envelope**. Any system can be overloaded if offered load exceeds capacity.
28
+
29
+ ---
30
+
31
+ ## 3) Transport selection matrix
32
+
33
+ Use this default selection policy:
34
+
35
+ | Workload | Recommended Mode | Why |
36
+ |---|---|---|
37
+ | High-throughput lane | `http-binary-batch` or `ws-binary-batch` | best throughput and lowest overhead |
38
+ | Balanced latency/compatibility | `ws-batch` | low framing overhead and stable p99 |
39
+ | Low/medium compatibility lane | `http` | simplest interoperability |
40
+ | Stress testing only | `http` + `--allow-unsafe-http` | intentional overload characterization |
41
+
42
+ Guardrail behavior in benchmark harness blocks unsafe plain HTTP at high concurrency unless explicitly overridden.
43
+
44
+ ---
45
+
46
+ ## 4) Benchmark discipline
47
+
48
+ ### Sustainable benchmarks (default)
49
+
50
+ Use adaptive benchmark commands for release confidence:
51
+
52
+ ```bash
53
+ npm run bench:openloop:quick
54
+ npm run bench:openloop:full
55
+ ```
56
+
57
+ These discover sustainable operating rates using threshold gates.
58
+
59
+ ### Stress benchmarks (explicit overload)
60
+
61
+ Use only for capacity ceiling exploration:
62
+
63
+ ```bash
64
+ npm run bench:openloop:stress:quick
65
+ npm run bench:openloop:stress:full
66
+ ```
67
+
68
+ Do not use stress runs alone as production readiness criteria.
69
+
70
+ ---
71
+
72
+ ## 5) Core runbook commands
73
+
74
+ ### Quality and release gates
75
+
76
+ ```bash
77
+ npm run lint
78
+ npm run test
79
+ npm run build
80
+ npm run release:gate
81
+ ```
82
+
83
+ ### Conformance gates
84
+
85
+ ```bash
86
+ npm run conformance:python
87
+ npm run conformance:rust
88
+ npm run conformance:binary
89
+ ```
90
+
91
+ ### Benchmark analysis
92
+
93
+ ```bash
94
+ npm run bench:diff -- --baseline <baseline.json> --candidate <candidate.json>
95
+ npm run release:dashboard
96
+ ```
97
+
98
+ ---
99
+
100
+ ## 6) Incident runbook
101
+
102
+ ### Symptom: high drop rate
103
+
104
+ 1. Confirm mode and benchmark class (adaptive vs stress)
105
+ 2. Check if plain HTTP is being used at high concurrency
106
+ 3. Switch to binary batch mode for hot lanes
107
+ 4. Inspect overload/retry behavior and queue pressure
108
+ 5. Reduce offered rate or widen worker capacity
109
+
110
+ ### Symptom: p99 latency spike
111
+
112
+ 1. Compare payload size distribution against baseline
113
+ 2. Check in-flight cap / batch-size adaptation behavior
114
+ 3. Verify no regression in signature profile or canonicalization path
115
+ 4. Re-run adaptive benchmark for sustainable envelope
116
+
117
+ ### Symptom: signature failures
118
+
119
+ 1. Validate key resolver outputs and key IDs
120
+ 2. Verify canonicalization parity across runtime boundaries
121
+ 3. Check clock skew / TTL policy window
122
+
123
+ ### Symptom: replay rejections
124
+
125
+ 1. Verify message uniqueness (`messageId`, `nonce`)
126
+ 2. Validate replay cache window sizing
127
+ 3. Confirm producer retry semantics are idempotent
128
+
129
+ ---
130
+
131
+ ## 7) Tuning matrix (starting points)
132
+
133
+ | Setting | Low Traffic | Medium Traffic | High Traffic |
134
+ |---|---:|---:|---:|
135
+ | Batch size | 4–8 | 8–32 | 32–64 |
136
+ | In-flight cap | 16–64 | 64–256 | 256–1024 |
137
+ | Retry backoff | 1–2ms | 2–8ms | 4–16ms |
138
+ | Preferred mode | `http`/`ws` | `ws-batch` | `http-binary-batch` / `ws-binary-batch` |
139
+
140
+ Treat this as a starting policy; tune from measured p99/drop under adaptive runs.
141
+
142
+ ---
143
+
144
+ ## 8) Security and reliability invariants
145
+
146
+ Never disable these in production:
147
+
148
+ - signature verification
149
+ - canonicalization checks
150
+ - replay defense
151
+ - TTL and clock-skew enforcement
152
+
153
+ Any performance optimization must preserve these invariants.
154
+
155
+ ---
156
+
157
+ ## 9) Release acceptance checklist
158
+
159
+ Release candidate is acceptable when:
160
+
161
+ 1. `release:gate` passes
162
+ 2. conformance suites pass (TS/Python/Rust/binary)
163
+ 3. adaptive benchmarks satisfy target SLO thresholds
164
+ 4. no critical regression in benchmark diff vs baseline
165
+ 5. rollback plan and threshold alerts are prepared
166
+
167
+ ---
168
+
169
+ ## 10) Recommended rollout strategy
170
+
171
+ 1. Canary 5%
172
+ 2. Observe drop/p99/signature/replay counters
173
+ 3. Expand 25% → 50% → 100%
174
+ 4. Auto-rollback if drop or p99 breaches thresholds for sustained window
175
+
176
+ ---
177
+
178
+ ## 11) Documentation map
179
+
180
+ - Protocol technical guide: `README.md`
181
+ - Executive brief: `README_EXECUTIVE.md`
182
+ - Performance policy: `PERF_REGRESSION_POLICY.md`
183
+ - Backpressure notes: `BACKPRESSURE_TUNING.md`
184
+ - Release hardening: `RELEASE_GATE.md`