@7h3/protocol 0.1.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/.dockerignore +19 -0
  2. package/.github/dependabot.yml +32 -0
  3. package/.github/workflows/ci.yml +31 -0
  4. package/.github/workflows/publish.yml +59 -0
  5. package/.github/workflows/scorecard.yml +37 -0
  6. package/7h3.example.yaml +125 -0
  7. package/CHANGELOG.md +92 -0
  8. package/CONTRIBUTING.md +82 -0
  9. package/Dockerfile +73 -0
  10. package/GOVERNANCE.md +62 -0
  11. package/README.md +323 -6
  12. package/SECURITY.md +70 -0
  13. package/bench-results/replay-cache-full-1777891033256.json +10 -0
  14. package/bench-results/replay-cache-full-1777896317488.json +10 -0
  15. package/bench-results/replay-cache-full-1777900993184.json +10 -0
  16. package/bench-results/replay-cache-full-1777901019285.json +10 -0
  17. package/bench-results/replay-cache-quick-1777870170126.json +10 -0
  18. package/bench-results/signature-profiles-quick-1775875160079.json +85 -0
  19. package/bench-results/signature-profiles-quick-1775983539716.json +85 -0
  20. package/bench-results/signature-profiles-quick-1776237913190.json +85 -0
  21. package/bench-results/wire-codecs-full-1777891019803.json +93 -0
  22. package/bench-results/wire-codecs-full-1777896260964.json +93 -0
  23. package/bench-results/wire-codecs-full-1777901004247.json +93 -0
  24. package/bench-results/wire-codecs-quick-1775972879056.json +93 -0
  25. package/bench-results/wire-codecs-quick-1775983541111.json +93 -0
  26. package/bench-results/wire-codecs-quick-1776237914299.json +93 -0
  27. package/bench-results/wire-codecs-quick-1777841285236.json +93 -0
  28. package/bench-results/wire-codecs-quick-1777841321772.json +93 -0
  29. package/bench-results/wire-codecs-quick-1777841330408.json +93 -0
  30. package/bench-results/wire-codecs-quick-1777852886082.json +93 -0
  31. package/bench-results/wire-codecs-quick-1777852988773.json +93 -0
  32. package/bench-results/wire-codecs-quick-1777870188095.json +93 -0
  33. package/bench-results/wire-codecs-quick-1777870263918.json +93 -0
  34. package/bench-results/wire-codecs-quick-1777870455034.json +93 -0
  35. package/bench-results/wire-codecs-quick-1778816163081.json +93 -0
  36. package/bench-results/wire-codecs-quick-1778843936130.json +93 -0
  37. package/bin/7h3.ts +385 -0
  38. package/conformance/7h3_v0_1.json +77 -0
  39. package/conformance/7h3_v0_1_binary.json +20 -0
  40. package/conformance/aip_v0_1_binary.json +20 -0
  41. package/docker-compose.yaml +77 -0
  42. package/docs/ADOPTION_PLAN.md +120 -0
  43. package/docs/AGENTS.md +77 -0
  44. package/docs/AIP_RFC_v0.1.md +97 -0
  45. package/docs/AI_DECISION_CARD.md +122 -0
  46. package/docs/AI_RUNTIME_POLICY.json +126 -0
  47. package/docs/AI_RUNTIME_POLICY.yaml +110 -0
  48. package/docs/BACKPRESSURE_TUNING.md +65 -0
  49. package/docs/BENCHMARK_CLAIM_MATRIX.md +42 -0
  50. package/docs/BENCHMARK_REPORT_TEMPLATE.md +169 -0
  51. package/docs/BINARY_CODEC_BENCH.md +23 -0
  52. package/docs/CLEAN_CLONE_RUNBOOK.md +36 -0
  53. package/docs/CLOCK_SKEW_POLICY.md +30 -0
  54. package/docs/DISTRIBUTED_REPLAY.md +142 -0
  55. package/docs/FUZZ_CAMPAIGN.md +121 -0
  56. package/docs/GATEWAY.md +195 -0
  57. package/docs/KEY_MANAGEMENT_POLICY.md +53 -0
  58. package/docs/KEY_REVOCATION.md +69 -0
  59. package/docs/MCP_WRAPPER.md +159 -0
  60. package/docs/MIGRATION_GUIDE.md +40 -0
  61. package/docs/OPERATORS.md +184 -0
  62. package/docs/PERF_REGRESSION_POLICY.md +34 -0
  63. package/docs/PROJECT_EXAMINATION_2026-05-31.md +219 -0
  64. package/docs/RELEASE_BENCHMARK_REPORT_2026-05-15.md +135 -0
  65. package/docs/RELEASE_GATE.md +25 -0
  66. package/docs/RELEASE_NOTES_v0.1.0.md +54 -0
  67. package/docs/SECURITY_REVIEW_2026-06-05.md +165 -0
  68. package/docs/TELEMETRY.md +41 -0
  69. package/docs/THREAT_MODEL.md +89 -0
  70. package/docs/VERSIONING_POLICY.md +30 -0
  71. package/docs/assets/banner.png +0 -0
  72. package/eslint.config.js +15 -0
  73. package/fuzz/ts/harness-decode.ts +136 -0
  74. package/fuzz/ts/harness-verify.ts +121 -0
  75. package/fuzz/ts/run.ts +35 -0
  76. package/mcp-server/README.md +38 -0
  77. package/mcp-server/package-lock.json +1187 -0
  78. package/mcp-server/package.json +35 -0
  79. package/mcp-server/src/index.ts +236 -0
  80. package/mcp-server/tsconfig.json +14 -0
  81. package/package.json +79 -13
  82. package/scripts/aip-framework-quickstart.ts +110 -0
  83. package/scripts/aip-mcp-gateway.ts +38 -0
  84. package/scripts/aip-mcp-wrap-demo.ts +72 -0
  85. package/scripts/aip-quickstart.ts +60 -0
  86. package/scripts/bench-diff.ts +118 -0
  87. package/scripts/bench-protocol-e2e.ts +937 -0
  88. package/scripts/bench-protocol-openloop.ts +1397 -0
  89. package/scripts/bench-replay-cache.ts +76 -0
  90. package/scripts/bench-signature-profiles.ts +180 -0
  91. package/scripts/bench-wire-codecs.ts +161 -0
  92. package/scripts/build-binary-conformance.ts +36 -0
  93. package/scripts/build-release-dashboard.ts +175 -0
  94. package/scripts/canary-rollout.ts +38 -0
  95. package/scripts/mcpGatewayCli.test.ts +116 -0
  96. package/scripts/prepare-aip-package.ts +88 -0
  97. package/scripts/regen-conformance-sigs.ts +18 -0
  98. package/scripts/release-gate.ts +19 -0
  99. package/scripts/validate-runtime-policy.ts +18 -0
  100. package/sdk/browser/index.test.ts +162 -0
  101. package/sdk/browser/index.ts +257 -0
  102. package/sdk/browser/package.json +13 -0
  103. package/sdk/go/go.mod +3 -0
  104. package/sdk/go/http.go +135 -0
  105. package/sdk/go/protocol.go +324 -0
  106. package/sdk/go/protocol_test.go +334 -0
  107. package/sdk/go/webhook.go +136 -0
  108. package/sdk/python/README.md +18 -0
  109. package/sdk/python/protocol_7h3/__init__.py +46 -0
  110. package/sdk/python/protocol_7h3/http.py +212 -0
  111. package/sdk/python/protocol_7h3/keys.py +149 -0
  112. package/sdk/python/protocol_7h3/protocol.py +525 -0
  113. package/sdk/python/protocol_7h3/queue.py +118 -0
  114. package/sdk/python/protocol_7h3/webhook.py +116 -0
  115. package/sdk/python/pyproject.toml +40 -0
  116. package/sdk/python/tests/test_conformance.py +110 -0
  117. package/sdk/python/tests/test_http.py +305 -0
  118. package/sdk/python/tests/test_keys.py +417 -0
  119. package/sdk/python/tests/test_queue.py +120 -0
  120. package/sdk/python/tests/test_webhook.py +345 -0
  121. package/sdk/rust/Cargo.lock +371 -0
  122. package/sdk/rust/Cargo.toml +25 -0
  123. package/sdk/rust/README.md +31 -0
  124. package/sdk/rust/fuzz/Cargo.toml +29 -0
  125. package/sdk/rust/fuzz/fuzz_targets/fuzz_canonicalize.rs +46 -0
  126. package/sdk/rust/fuzz/fuzz_targets/fuzz_decode.rs +11 -0
  127. package/sdk/rust/src/bin/aip_mcp_gateway.rs +59 -0
  128. package/sdk/rust/src/http.rs +145 -0
  129. package/sdk/rust/src/keys.rs +161 -0
  130. package/sdk/rust/src/lib.rs +688 -0
  131. package/sdk/rust/src/queue.rs +79 -0
  132. package/sdk/rust/src/webhook.rs +86 -0
  133. package/sdk/rust/tests/conformance.rs +148 -0
  134. package/sdk/rust/tests/gateway.rs +130 -0
  135. package/sdk/rust/tests/http_webhook_queue.rs +201 -0
  136. package/sdk/rust/tests/keys.rs +189 -0
  137. package/src/agentAdapter.test.ts +48 -0
  138. package/src/agentAdapter.ts +56 -0
  139. package/src/auditLog.test.ts +145 -0
  140. package/src/auditLog.ts +147 -0
  141. package/src/conformance.test.ts +136 -0
  142. package/src/conformanceVectors.ts +99 -0
  143. package/src/frameworkAdapters.test.ts +290 -0
  144. package/src/frameworkAdapters.ts +261 -0
  145. package/src/gateway.test.ts +343 -0
  146. package/src/gateway.ts +171 -0
  147. package/src/grpcBinding.test.ts +211 -0
  148. package/src/grpcBinding.ts +103 -0
  149. package/src/httpBinding.test.ts +376 -0
  150. package/src/httpBinding.ts +163 -0
  151. package/src/index.ts +32 -0
  152. package/src/keyInfra.test.ts +278 -0
  153. package/src/keyInfra.ts +228 -0
  154. package/src/keyRegistry.ts +59 -0
  155. package/src/keyRotation.test.ts +78 -0
  156. package/src/keyRotation.ts +72 -0
  157. package/src/mcpGateway.test.ts +129 -0
  158. package/src/mcpGateway.ts +250 -0
  159. package/src/mcpTransports.test.ts +92 -0
  160. package/src/mcpTransports.ts +169 -0
  161. package/src/mcpWrapper.test.ts +179 -0
  162. package/src/mcpWrapper.ts +206 -0
  163. package/src/policyEnforcer.test.ts +99 -0
  164. package/src/policyEnforcer.ts +169 -0
  165. package/src/policyTelemetryFeedback.test.ts +25 -0
  166. package/src/policyTelemetryFeedback.ts +38 -0
  167. package/src/protocol.bench.ts +37 -0
  168. package/src/protocol.test.ts +155 -0
  169. package/src/protocol.ts +413 -0
  170. package/src/protocolAgent.test.ts +105 -0
  171. package/src/protocolAgent.ts +169 -0
  172. package/src/protocolBinary.test.ts +165 -0
  173. package/src/protocolBinary.ts +312 -0
  174. package/src/protocolCapabilities.ts +70 -0
  175. package/src/protocolFuzz.advanced.test.ts +235 -0
  176. package/src/protocolFuzz.test.ts +111 -0
  177. package/src/protocolNegative.test.ts +97 -0
  178. package/src/protocolReplay.test.ts +71 -0
  179. package/src/protocolReplay.ts +194 -0
  180. package/src/protocolTransport.test.ts +556 -0
  181. package/src/protocolTransport.ts +483 -0
  182. package/src/queueBinding.test.ts +130 -0
  183. package/src/queueBinding.ts +102 -0
  184. package/src/rateLimiter.test.ts +96 -0
  185. package/src/rateLimiter.ts +46 -0
  186. package/src/redisClient.ts +140 -0
  187. package/src/redisIntegration.test.ts +134 -0
  188. package/src/replayStores.test.ts +141 -0
  189. package/src/replayStores.ts +82 -0
  190. package/src/revocation.test.ts +98 -0
  191. package/src/revocation.ts +0 -0
  192. package/src/routePolicy.test.ts +87 -0
  193. package/src/routePolicy.ts +72 -0
  194. package/src/runtimePolicy.test.ts +49 -0
  195. package/src/runtimePolicy.ts +81 -0
  196. package/src/runtimePolicyManager.test.ts +29 -0
  197. package/src/runtimePolicyManager.ts +50 -0
  198. package/src/runtimePolicyPresets.ts +43 -0
  199. package/src/signedResponse.test.ts +111 -0
  200. package/src/signedResponse.ts +83 -0
  201. package/src/webhookBinding.test.ts +144 -0
  202. package/src/webhookBinding.ts +115 -0
  203. package/src/wsBinding.test.ts +221 -0
  204. package/src/wsBinding.ts +100 -0
  205. package/tsconfig.json +15 -0
  206. package/tsconfig.lib.json +23 -0
  207. package/vite.lib.config.ts +16 -0
  208. package/agentAdapter.d.ts +0 -26
  209. package/conformanceVectors.d.ts +0 -20
  210. package/frameworkAdapters.d.ts +0 -72
  211. package/index.d.ts +0 -20
  212. package/index.js +0 -1702
  213. package/keyRotation.d.ts +0 -20
  214. package/mcpGateway.d.ts +0 -37
  215. package/mcpTransports.d.ts +0 -62
  216. package/mcpWrapper.d.ts +0 -83
  217. package/policyEnforcer.d.ts +0 -50
  218. package/policyTelemetryFeedback.d.ts +0 -11
  219. package/protocol.d.ts +0 -66
  220. package/protocolAgent.d.ts +0 -58
  221. package/protocolBinary.d.ts +0 -8
  222. package/protocolCapabilities.d.ts +0 -24
  223. package/protocolReplay.d.ts +0 -35
  224. package/protocolTransport.d.ts +0 -73
  225. package/redisClient.d.ts +0 -49
  226. package/replayStores.d.ts +0 -32
  227. package/revocation.d.ts +0 -71
  228. package/runtimePolicy.d.ts +0 -24
  229. package/runtimePolicyManager.d.ts +0 -15
  230. package/runtimePolicyPresets.d.ts +0 -11
@@ -0,0 +1,194 @@
1
+ import type { ProtocolEnvelope } from './protocol'
2
+
3
+ export interface ReplayCheckResult {
4
+ ok: boolean
5
+ reason?: string
6
+ }
7
+
8
+ export interface ReplayCache {
9
+ consume(envelope: ProtocolEnvelope, nowMs?: number): ReplayCheckResult | Promise<ReplayCheckResult>
10
+ consumeMany?(envelopes: ProtocolEnvelope[], nowMs?: number): ReplayCheckResult[] | Promise<ReplayCheckResult[]>
11
+ }
12
+
13
+ export interface DistributedReplayStore {
14
+ reserve(key: string, expiresAtMs: number, nowMs: number): boolean | Promise<boolean>
15
+ /** Optional batch reserve; enables single round-trip checks for batched envelopes. */
16
+ reserveMany?(
17
+ entries: Array<{ key: string; expiresAtMs: number }>,
18
+ nowMs: number,
19
+ ): boolean[] | Promise<boolean[]>
20
+ }
21
+
22
+ interface ExpiryEntry {
23
+ key: string
24
+ expiresAt: number
25
+ }
26
+
27
+ class ExpiryMinHeap {
28
+ private readonly items: ExpiryEntry[] = []
29
+
30
+ peek(): ExpiryEntry | undefined {
31
+ return this.items[0]
32
+ }
33
+
34
+ push(entry: ExpiryEntry): void {
35
+ this.items.push(entry)
36
+ this.bubbleUp(this.items.length - 1)
37
+ }
38
+
39
+ pop(): ExpiryEntry | undefined {
40
+ if (this.items.length === 0) return undefined
41
+ const first = this.items[0]
42
+ const last = this.items.pop()
43
+ if (this.items.length > 0 && last) {
44
+ this.items[0] = last
45
+ this.bubbleDown(0)
46
+ }
47
+ return first
48
+ }
49
+
50
+ private bubbleUp(index: number): void {
51
+ let current = index
52
+ while (current > 0) {
53
+ const parent = Math.floor((current - 1) / 2)
54
+ const parentItem = this.items[parent]
55
+ const currentItem = this.items[current]
56
+ if (!parentItem || !currentItem || parentItem.expiresAt <= currentItem.expiresAt) break
57
+ this.items[parent] = currentItem
58
+ this.items[current] = parentItem
59
+ current = parent
60
+ }
61
+ }
62
+
63
+ private bubbleDown(index: number): void {
64
+ let current = index
65
+ while (true) {
66
+ const left = current * 2 + 1
67
+ const right = current * 2 + 2
68
+ let smallest = current
69
+
70
+ const currentItem = this.items[current]
71
+ const leftItem = this.items[left]
72
+ const rightItem = this.items[right]
73
+
74
+ if (leftItem && currentItem && leftItem.expiresAt < currentItem.expiresAt) {
75
+ smallest = left
76
+ }
77
+ const smallestItem = this.items[smallest]
78
+ if (rightItem && smallestItem && rightItem.expiresAt < smallestItem.expiresAt) {
79
+ smallest = right
80
+ }
81
+
82
+ if (smallest === current) break
83
+ const swapA = this.items[current]
84
+ const swapB = this.items[smallest]
85
+ if (!swapA || !swapB) break
86
+ this.items[current] = swapB
87
+ this.items[smallest] = swapA
88
+ current = smallest
89
+ }
90
+ }
91
+ }
92
+
93
+ export class InMemoryReplayCache implements ReplayCache {
94
+ private readonly entries = new Map<string, number>()
95
+ private readonly maxEntries: number
96
+ private readonly expiries = new ExpiryMinHeap()
97
+
98
+ constructor(maxEntries = 10000) {
99
+ this.maxEntries = maxEntries
100
+ }
101
+
102
+ private makeKey(envelope: ProtocolEnvelope): string {
103
+ // encodeURIComponent escapes '|' so sender/messageId/nonce can never collide
104
+ // across field boundaries (e.g. sender="a|b" vs messageId="a|b").
105
+ return `${encodeURIComponent(envelope.header.sender)}|${encodeURIComponent(envelope.header.messageId)}|${encodeURIComponent(envelope.header.nonce)}`
106
+ }
107
+
108
+ prune(nowMs = Date.now()): void {
109
+ while (true) {
110
+ const next = this.expiries.peek()
111
+ if (!next || next.expiresAt > nowMs) break
112
+
113
+ this.expiries.pop()
114
+ const currentExpiry = this.entries.get(next.key)
115
+ if (currentExpiry !== undefined && currentExpiry === next.expiresAt && currentExpiry <= nowMs) {
116
+ this.entries.delete(next.key)
117
+ }
118
+ }
119
+ }
120
+
121
+ private evictOne(): void {
122
+ while (this.entries.size >= this.maxEntries) {
123
+ const next = this.expiries.pop()
124
+ if (!next) break
125
+ const currentExpiry = this.entries.get(next.key)
126
+ if (currentExpiry !== undefined && currentExpiry === next.expiresAt) {
127
+ this.entries.delete(next.key)
128
+ return
129
+ }
130
+ }
131
+
132
+ if (this.entries.size >= this.maxEntries) {
133
+ const fallback = this.entries.keys().next().value as string | undefined
134
+ if (fallback) this.entries.delete(fallback)
135
+ }
136
+ }
137
+
138
+ consume(envelope: ProtocolEnvelope, nowMs = Date.now()): ReplayCheckResult {
139
+ this.prune(nowMs)
140
+
141
+ const key = this.makeKey(envelope)
142
+ const existingExpiry = this.entries.get(key)
143
+ if (existingExpiry && existingExpiry > nowMs) {
144
+ return { ok: false, reason: 'Replay detected for sender/messageId/nonce' }
145
+ }
146
+
147
+ if (this.entries.size >= this.maxEntries) this.evictOne()
148
+
149
+ const expiresAt = envelope.header.timestampMs + envelope.header.ttlMs
150
+ this.entries.set(key, expiresAt)
151
+ this.expiries.push({ key, expiresAt })
152
+ return { ok: true }
153
+ }
154
+
155
+ consumeMany(envelopes: ProtocolEnvelope[], nowMs = Date.now()): ReplayCheckResult[] {
156
+ return envelopes.map((envelope) => this.consume(envelope, nowMs))
157
+ }
158
+ }
159
+
160
+ export class DistributedReplayCache implements ReplayCache {
161
+ private readonly store: DistributedReplayStore
162
+
163
+ constructor(store: DistributedReplayStore) {
164
+ this.store = store
165
+ }
166
+
167
+ private makeKey(envelope: ProtocolEnvelope): string {
168
+ return `${encodeURIComponent(envelope.header.sender)}|${encodeURIComponent(envelope.header.messageId)}|${encodeURIComponent(envelope.header.nonce)}`
169
+ }
170
+
171
+ async consume(envelope: ProtocolEnvelope, nowMs = Date.now()): Promise<ReplayCheckResult> {
172
+ const key = this.makeKey(envelope)
173
+ const expiresAtMs = envelope.header.timestampMs + envelope.header.ttlMs
174
+ const reserved = await this.store.reserve(key, expiresAtMs, nowMs)
175
+ if (!reserved) {
176
+ return { ok: false, reason: 'Replay detected for sender/messageId/nonce' }
177
+ }
178
+ return { ok: true }
179
+ }
180
+
181
+ async consumeMany(envelopes: ProtocolEnvelope[], nowMs = Date.now()): Promise<ReplayCheckResult[]> {
182
+ if (this.store.reserveMany) {
183
+ const entries = envelopes.map((envelope) => ({
184
+ key: this.makeKey(envelope),
185
+ expiresAtMs: envelope.header.timestampMs + envelope.header.ttlMs,
186
+ }))
187
+ const reserved = await this.store.reserveMany(entries, nowMs)
188
+ return reserved.map((ok) =>
189
+ ok ? { ok: true } : { ok: false, reason: 'Replay detected for sender/messageId/nonce' },
190
+ )
191
+ }
192
+ return Promise.all(envelopes.map((envelope) => this.consume(envelope, nowMs)))
193
+ }
194
+ }