@7h3/protocol 0.4.0 → 0.5.1

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 (248) hide show
  1. package/README.md +6 -323
  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 +3662 -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 -79
  23. package/policyEnforcer.d.ts +50 -0
  24. package/policyTelemetryFeedback.d.ts +11 -0
  25. package/protocol.d.ts +66 -0
  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 -92
  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 -385
  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.png +0 -0
  115. package/eslint.config.js +0 -15
  116. package/fuzz/ts/harness-decode.ts +0 -136
  117. package/fuzz/ts/harness-verify.ts +0 -121
  118. package/fuzz/ts/run.ts +0 -35
  119. package/mcp-server/README.md +0 -38
  120. package/mcp-server/package-lock.json +0 -1187
  121. package/mcp-server/package.json +0 -35
  122. package/mcp-server/src/index.ts +0 -236
  123. package/mcp-server/tsconfig.json +0 -14
  124. package/scripts/aip-framework-quickstart.ts +0 -110
  125. package/scripts/aip-mcp-gateway.ts +0 -38
  126. package/scripts/aip-mcp-wrap-demo.ts +0 -72
  127. package/scripts/aip-quickstart.ts +0 -60
  128. package/scripts/bench-diff.ts +0 -118
  129. package/scripts/bench-protocol-e2e.ts +0 -937
  130. package/scripts/bench-protocol-openloop.ts +0 -1397
  131. package/scripts/bench-replay-cache.ts +0 -76
  132. package/scripts/bench-signature-profiles.ts +0 -180
  133. package/scripts/bench-wire-codecs.ts +0 -161
  134. package/scripts/build-binary-conformance.ts +0 -36
  135. package/scripts/build-release-dashboard.ts +0 -175
  136. package/scripts/canary-rollout.ts +0 -38
  137. package/scripts/mcpGatewayCli.test.ts +0 -116
  138. package/scripts/prepare-aip-package.ts +0 -88
  139. package/scripts/regen-conformance-sigs.ts +0 -18
  140. package/scripts/release-gate.ts +0 -19
  141. package/scripts/validate-runtime-policy.ts +0 -18
  142. package/sdk/browser/index.test.ts +0 -162
  143. package/sdk/browser/index.ts +0 -257
  144. package/sdk/browser/package.json +0 -13
  145. package/sdk/go/go.mod +0 -3
  146. package/sdk/go/http.go +0 -135
  147. package/sdk/go/protocol.go +0 -324
  148. package/sdk/go/protocol_test.go +0 -334
  149. package/sdk/go/webhook.go +0 -136
  150. package/sdk/python/README.md +0 -18
  151. package/sdk/python/protocol_7h3/__init__.py +0 -46
  152. package/sdk/python/protocol_7h3/http.py +0 -212
  153. package/sdk/python/protocol_7h3/keys.py +0 -149
  154. package/sdk/python/protocol_7h3/protocol.py +0 -525
  155. package/sdk/python/protocol_7h3/queue.py +0 -118
  156. package/sdk/python/protocol_7h3/webhook.py +0 -116
  157. package/sdk/python/pyproject.toml +0 -40
  158. package/sdk/python/tests/test_conformance.py +0 -110
  159. package/sdk/python/tests/test_http.py +0 -305
  160. package/sdk/python/tests/test_keys.py +0 -417
  161. package/sdk/python/tests/test_queue.py +0 -120
  162. package/sdk/python/tests/test_webhook.py +0 -345
  163. package/sdk/rust/Cargo.lock +0 -371
  164. package/sdk/rust/Cargo.toml +0 -25
  165. package/sdk/rust/README.md +0 -31
  166. package/sdk/rust/fuzz/Cargo.toml +0 -29
  167. package/sdk/rust/fuzz/fuzz_targets/fuzz_canonicalize.rs +0 -46
  168. package/sdk/rust/fuzz/fuzz_targets/fuzz_decode.rs +0 -11
  169. package/sdk/rust/src/bin/aip_mcp_gateway.rs +0 -59
  170. package/sdk/rust/src/http.rs +0 -145
  171. package/sdk/rust/src/keys.rs +0 -161
  172. package/sdk/rust/src/lib.rs +0 -688
  173. package/sdk/rust/src/queue.rs +0 -79
  174. package/sdk/rust/src/webhook.rs +0 -86
  175. package/sdk/rust/tests/conformance.rs +0 -148
  176. package/sdk/rust/tests/gateway.rs +0 -130
  177. package/sdk/rust/tests/http_webhook_queue.rs +0 -201
  178. package/sdk/rust/tests/keys.rs +0 -189
  179. package/src/agentAdapter.test.ts +0 -48
  180. package/src/agentAdapter.ts +0 -56
  181. package/src/auditLog.test.ts +0 -145
  182. package/src/auditLog.ts +0 -147
  183. package/src/conformance.test.ts +0 -136
  184. package/src/conformanceVectors.ts +0 -99
  185. package/src/frameworkAdapters.test.ts +0 -290
  186. package/src/frameworkAdapters.ts +0 -261
  187. package/src/gateway.test.ts +0 -343
  188. package/src/gateway.ts +0 -171
  189. package/src/grpcBinding.test.ts +0 -211
  190. package/src/grpcBinding.ts +0 -103
  191. package/src/httpBinding.test.ts +0 -376
  192. package/src/httpBinding.ts +0 -163
  193. package/src/index.ts +0 -32
  194. package/src/keyInfra.test.ts +0 -278
  195. package/src/keyInfra.ts +0 -228
  196. package/src/keyRegistry.ts +0 -59
  197. package/src/keyRotation.test.ts +0 -78
  198. package/src/keyRotation.ts +0 -72
  199. package/src/mcpGateway.test.ts +0 -129
  200. package/src/mcpGateway.ts +0 -250
  201. package/src/mcpTransports.test.ts +0 -92
  202. package/src/mcpTransports.ts +0 -169
  203. package/src/mcpWrapper.test.ts +0 -179
  204. package/src/mcpWrapper.ts +0 -206
  205. package/src/policyEnforcer.test.ts +0 -99
  206. package/src/policyEnforcer.ts +0 -169
  207. package/src/policyTelemetryFeedback.test.ts +0 -25
  208. package/src/policyTelemetryFeedback.ts +0 -38
  209. package/src/protocol.bench.ts +0 -37
  210. package/src/protocol.test.ts +0 -155
  211. package/src/protocol.ts +0 -413
  212. package/src/protocolAgent.test.ts +0 -105
  213. package/src/protocolAgent.ts +0 -169
  214. package/src/protocolBinary.test.ts +0 -165
  215. package/src/protocolBinary.ts +0 -312
  216. package/src/protocolCapabilities.ts +0 -70
  217. package/src/protocolFuzz.advanced.test.ts +0 -235
  218. package/src/protocolFuzz.test.ts +0 -111
  219. package/src/protocolNegative.test.ts +0 -97
  220. package/src/protocolReplay.test.ts +0 -71
  221. package/src/protocolReplay.ts +0 -194
  222. package/src/protocolTransport.test.ts +0 -556
  223. package/src/protocolTransport.ts +0 -483
  224. package/src/queueBinding.test.ts +0 -130
  225. package/src/queueBinding.ts +0 -102
  226. package/src/rateLimiter.test.ts +0 -96
  227. package/src/rateLimiter.ts +0 -46
  228. package/src/redisIntegration.test.ts +0 -134
  229. package/src/replayStores.test.ts +0 -141
  230. package/src/replayStores.ts +0 -82
  231. package/src/revocation.test.ts +0 -98
  232. package/src/revocation.ts +0 -0
  233. package/src/routePolicy.test.ts +0 -87
  234. package/src/routePolicy.ts +0 -72
  235. package/src/runtimePolicy.test.ts +0 -49
  236. package/src/runtimePolicy.ts +0 -81
  237. package/src/runtimePolicyManager.test.ts +0 -29
  238. package/src/runtimePolicyManager.ts +0 -50
  239. package/src/runtimePolicyPresets.ts +0 -43
  240. package/src/signedResponse.test.ts +0 -111
  241. package/src/signedResponse.ts +0 -83
  242. package/src/webhookBinding.test.ts +0 -144
  243. package/src/webhookBinding.ts +0 -115
  244. package/src/wsBinding.test.ts +0 -221
  245. package/src/wsBinding.ts +0 -100
  246. package/tsconfig.json +0 -15
  247. package/tsconfig.lib.json +0 -23
  248. package/vite.lib.config.ts +0 -16
@@ -1,116 +0,0 @@
1
- import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'
2
- import { describe, expect, it } from 'vitest'
3
-
4
- interface JsonRpcEnvelope {
5
- jsonrpc: '2.0'
6
- id: string | number | null
7
- result?: unknown
8
- error?: {
9
- code: number
10
- message: string
11
- }
12
- }
13
-
14
- function waitForLine(child: ChildProcessWithoutNullStreams, timeoutMs = 8_000): Promise<string> {
15
- return new Promise((resolve, reject) => {
16
- let buffer = ''
17
- const timer = setTimeout(() => {
18
- cleanup()
19
- reject(new Error('Timed out waiting for gateway output'))
20
- }, timeoutMs)
21
-
22
- const onData = (chunk: Buffer) => {
23
- buffer += chunk.toString('utf8')
24
- const newlineIndex = buffer.indexOf('\n')
25
- if (newlineIndex >= 0) {
26
- const line = buffer.slice(0, newlineIndex).trim()
27
- cleanup()
28
- resolve(line)
29
- }
30
- }
31
-
32
- const onError = (error: Error) => {
33
- cleanup()
34
- reject(error)
35
- }
36
-
37
- const onExit = () => {
38
- cleanup()
39
- reject(new Error('Gateway process exited before producing output'))
40
- }
41
-
42
- const cleanup = () => {
43
- clearTimeout(timer)
44
- child.stdout.off('data', onData)
45
- child.off('error', onError)
46
- child.off('exit', onExit)
47
- }
48
-
49
- child.stdout.on('data', onData)
50
- child.on('error', onError)
51
- child.on('exit', onExit)
52
- })
53
- }
54
-
55
- function startGateway(extraEnv: Record<string, string> = {}): ChildProcessWithoutNullStreams {
56
- const child = spawn('npm', ['run', '-s', 'aip:mcp:gateway'], {
57
- cwd: process.cwd(),
58
- env: {
59
- ...process.env,
60
- ...extraEnv,
61
- },
62
- stdio: ['pipe', 'pipe', 'pipe'],
63
- })
64
-
65
- child.stderr.on('data', () => {
66
- // suppress stderr accumulation in test output
67
- })
68
-
69
- return child
70
- }
71
-
72
- async function stopGateway(child: ChildProcessWithoutNullStreams): Promise<void> {
73
- if (child.killed) return
74
- child.kill('SIGTERM')
75
- await new Promise<void>((resolve) => {
76
- child.once('exit', () => resolve())
77
- setTimeout(() => resolve(), 2_000)
78
- })
79
- }
80
-
81
- describe('AIP MCP gateway CLI', () => {
82
- it('returns JSON-RPC result for allowed method', async () => {
83
- const child = startGateway()
84
- try {
85
- child.stdin.write(
86
- `${JSON.stringify({ jsonrpc: '2.0', id: 99, method: 'tools/call', params: { name: 'planner' } })}\n`,
87
- )
88
- const line = await waitForLine(child)
89
- const response = JSON.parse(line) as JsonRpcEnvelope
90
-
91
- expect(response.jsonrpc).toBe('2.0')
92
- expect(response.id).toBe(99)
93
- expect(response.error).toBeUndefined()
94
- expect(response.result).toBeTruthy()
95
- } finally {
96
- await stopGateway(child)
97
- }
98
- })
99
-
100
- it('returns method-not-found for disallowed method from env policy', async () => {
101
- const child = startGateway({ AIP_ALLOWED_METHODS: 'tools/call' })
102
- try {
103
- child.stdin.write(
104
- `${JSON.stringify({ jsonrpc: '2.0', id: 'abc', method: 'resources/read', params: { uri: 'file://x' } })}\n`,
105
- )
106
- const line = await waitForLine(child)
107
- const response = JSON.parse(line) as JsonRpcEnvelope
108
-
109
- expect(response.id).toBe('abc')
110
- expect(response.result).toBeUndefined()
111
- expect(response.error?.code).toBe(-32601)
112
- } finally {
113
- await stopGateway(child)
114
- }
115
- })
116
- })
@@ -1,88 +0,0 @@
1
- import { mkdir, readFile, writeFile, copyFile, readdir } from 'node:fs/promises'
2
- import process from 'node:process'
3
-
4
- interface RootPackageJson {
5
- version?: string
6
- }
7
-
8
- function parseVersionArg(argv: string[]): string | undefined {
9
- const idx = argv.indexOf('--version')
10
- if (idx < 0) return undefined
11
- return argv[idx + 1]
12
- }
13
-
14
- async function readRootVersion(): Promise<string> {
15
- const raw = await readFile('package.json', 'utf8')
16
- const parsed = JSON.parse(raw) as RootPackageJson
17
- return parsed.version ?? '0.0.0'
18
- }
19
-
20
- async function main(): Promise<void> {
21
- const version = parseVersionArg(process.argv) ?? (await readRootVersion())
22
- const outDir = 'dist/npm-protocol'
23
-
24
- await mkdir(outDir, { recursive: true })
25
- await copyFile('dist/protocol/index.js', `${outDir}/index.js`)
26
-
27
- // Copy all .d.ts files — index.d.ts re-exports from the individual module files,
28
- // so all of them must be present for TypeScript consumers to resolve types.
29
- const distFiles = await readdir('dist/protocol')
30
- for (const file of distFiles) {
31
- if (file.endsWith('.d.ts')) {
32
- await copyFile(`dist/protocol/${file}`, `${outDir}/${file}`)
33
- }
34
- }
35
-
36
- const packageJson = {
37
- name: '@7h3/protocol',
38
- version,
39
- description: '7h3 Protocol: deterministic, signed, replay-safe AI-to-AI message envelopes (wire 7h3/0.1).',
40
- type: 'module',
41
- main: './index.js',
42
- types: './index.d.ts',
43
- exports: {
44
- '.': {
45
- import: './index.js',
46
- types: './index.d.ts',
47
- },
48
- },
49
- files: ['index.js', '*.d.ts', 'README.md'],
50
- license: 'MIT',
51
- repository: {
52
- type: 'git',
53
- url: 'https://github.com/IceMasterT/7h3-protocol-aip.git',
54
- },
55
- }
56
-
57
- const readme = [
58
- '# @7h3/protocol',
59
- '',
60
- '7h3 Protocol (wire version `7h3/0.1`):',
61
- 'deterministic, signed, replay-safe AI-to-AI message envelopes.',
62
- '',
63
- 'Install:',
64
- '',
65
- '```bash',
66
- 'npm install @7h3/protocol',
67
- '```',
68
- '',
69
- 'Import:',
70
- '',
71
- '```ts',
72
- "import { createAipAgentAdapter, receiveEnvelope } from '@7h3/protocol'",
73
- '```',
74
- '',
75
- ].join('\n')
76
-
77
- await writeFile(`${outDir}/package.json`, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8')
78
- await writeFile(`${outDir}/README.md`, readme, 'utf8')
79
-
80
- console.log(`Prepared publishable package in ${outDir}`)
81
- console.log(`Version: ${version}`)
82
- }
83
-
84
- main().catch((error: unknown) => {
85
- const message = error instanceof Error ? error.stack ?? error.message : String(error)
86
- console.error(message)
87
- process.exitCode = 1
88
- })
@@ -1,18 +0,0 @@
1
- import { signCanonicalPayloadHmac, signCanonicalPayloadEd25519 } from '../src/protocol'
2
- import { AIP_V01_CONFORMANCE_VECTORS, AIP_V01_ED25519_CONFORMANCE_VECTORS } from '../src/conformanceVectors'
3
-
4
- async function main() {
5
- console.log('=== HMAC vectors ===')
6
- for (const v of AIP_V01_CONFORMANCE_VECTORS) {
7
- const sig = await signCanonicalPayloadHmac(v.canonical, v.secret)
8
- console.log(`${v.id}: '${sig}'`)
9
- }
10
-
11
- console.log('\n=== Ed25519 vectors ===')
12
- for (const v of AIP_V01_ED25519_CONFORMANCE_VECTORS) {
13
- const sig = await signCanonicalPayloadEd25519(v.canonical, v.privateKey)
14
- console.log(`${v.id}: '${sig}'`)
15
- }
16
- }
17
-
18
- main().catch(e => { console.error(e); process.exit(1) })
@@ -1,19 +0,0 @@
1
- import { spawnSync } from 'node:child_process'
2
-
3
- const commands: Array<[string, string[]]> = [
4
- ['npm', ['run', 'policy:validate']],
5
- ['npm', ['run', 'test']],
6
- ['npm', ['run', 'build:protocol']],
7
- ['npm', ['run', 'lint']],
8
- ['npm', ['run', 'bench:wire:quick']],
9
- ['npm', ['run', 'bench:openloop:adaptive:ci']],
10
- ]
11
-
12
- for (const [command, args] of commands) {
13
- console.log(`\n$ ${command} ${args.join(' ')}`)
14
- const result = spawnSync(command, args, { stdio: 'inherit', shell: false })
15
- if (result.status !== 0) {
16
- process.exitCode = result.status ?? 1
17
- break
18
- }
19
- }
@@ -1,18 +0,0 @@
1
- import { readFile } from 'node:fs/promises'
2
- import { parseRuntimePolicyJson } from '../src/runtimePolicy'
3
-
4
- async function main(): Promise<void> {
5
- const path = process.argv[2] ?? 'AI_RUNTIME_POLICY.json'
6
- const text = await readFile(path, 'utf8')
7
- const policy = parseRuntimePolicyJson(text)
8
- if (!policy.hard_invariants || !policy.slo_defaults || !policy.tuning) {
9
- throw new Error('Runtime policy is missing one or more required sections: hard_invariants, slo_defaults, tuning')
10
- }
11
- console.log(`Runtime policy validated: ${policy.name}@${policy.version} (${policy.status})`)
12
- }
13
-
14
- main().catch((error: unknown) => {
15
- const message = error instanceof Error ? error.stack ?? error.message : String(error)
16
- console.error(message)
17
- process.exitCode = 1
18
- })
@@ -1,162 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import {
3
- generateKeypair,
4
- canonicalizeEnvelope,
5
- createEnvelope,
6
- signEnvelope,
7
- verifyEnvelope,
8
- signRequest,
9
- isEnvelopeExpired,
10
- ENVELOPE_HEADER,
11
- WIRE_VERSION,
12
- type BrowserEnvelopeHeader,
13
- type BrowserEnvelopeBody,
14
- } from './index.ts'
15
-
16
- describe('Browser SDK', () => {
17
- // Test 1: generateKeypair produces non-empty strings
18
- it('generateKeypair produces non-empty strings', async () => {
19
- const kp = await generateKeypair()
20
- expect(typeof kp.publicKey).toBe('string')
21
- expect(kp.publicKey.length).toBeGreaterThan(0)
22
- expect(typeof kp.privateKey).toBe('string')
23
- expect(kp.privateKey.length).toBeGreaterThan(0)
24
- })
25
-
26
- // Test 2: canonicalizeEnvelope matches exact conformance vector
27
- it('canonicalizeEnvelope matches conformance vector', () => {
28
- const header: BrowserEnvelopeHeader = {
29
- version: '7h3/0.1',
30
- messageId: 'vec-1',
31
- nonce: 'nonce-vec-1',
32
- sender: 'agent.alpha',
33
- recipient: 'agent.beta',
34
- timestampMs: 1712500000000,
35
- ttlMs: 60000,
36
- }
37
- const body: BrowserEnvelopeBody = {
38
- intent: 'TASK',
39
- content: 'route:alpha->beta',
40
- capability: 'task.plan',
41
- correlationId: 'corr-1',
42
- }
43
- const expected =
44
- '{"body":{"capability":"task.plan","content":"route:alpha->beta","correlationId":"corr-1","intent":"TASK"},"header":{"messageId":"vec-1","nonce":"nonce-vec-1","recipient":"agent.beta","sender":"agent.alpha","timestampMs":1712500000000,"ttlMs":60000,"version":"7h3/0.1"}}'
45
- expect(canonicalizeEnvelope({ header, body })).toBe(expected)
46
- })
47
-
48
- // Test 3: canonicalizeEnvelope with no optional fields
49
- it('canonicalizeEnvelope omits undefined optional fields', () => {
50
- const header: BrowserEnvelopeHeader = {
51
- version: '7h3/0.1',
52
- messageId: 'msg-1',
53
- nonce: 'n1',
54
- sender: 'agent.x',
55
- timestampMs: 1712500000000,
56
- ttlMs: 30000,
57
- }
58
- const body: BrowserEnvelopeBody = {
59
- intent: 'PING',
60
- content: 'hello',
61
- }
62
- const result = canonicalizeEnvelope({ header, body })
63
- const parsed = JSON.parse(result)
64
- expect(parsed.header.recipient).toBeUndefined()
65
- expect(parsed.body.capability).toBeUndefined()
66
- expect(parsed.body.correlationId).toBeUndefined()
67
- // Verify the raw string doesn't include those keys
68
- expect(result).not.toContain('"recipient"')
69
- expect(result).not.toContain('"capability"')
70
- expect(result).not.toContain('"correlationId"')
71
- })
72
-
73
- // Test 4: createEnvelope produces correct structure
74
- it('createEnvelope produces correct structure', () => {
75
- const env = createEnvelope({
76
- sender: 'agent.test',
77
- body: { intent: 'TASK', content: 'do work' },
78
- })
79
- expect(env.header.version).toBe(WIRE_VERSION)
80
- expect(typeof env.header.messageId).toBe('string')
81
- expect(env.header.messageId.length).toBeGreaterThan(0)
82
- expect(env.body.intent).toBe('TASK')
83
- expect(env.header.sender).toBe('agent.test')
84
- expect(env.header.ttlMs).toBe(60_000)
85
- })
86
-
87
- // Test 5: signEnvelope + verifyEnvelope round trip
88
- it('signEnvelope + verifyEnvelope round trip', async () => {
89
- const kp = await generateKeypair()
90
- const env = createEnvelope({
91
- sender: 'agent.a',
92
- body: { intent: 'PING', content: 'test' },
93
- })
94
- const signed = await signEnvelope(env, kp.privateKey)
95
- expect(signed.signature).toBeDefined()
96
- expect(signed.signature?.alg).toBe('ED25519')
97
- const valid = await verifyEnvelope(signed, kp.publicKey)
98
- expect(valid).toBe(true)
99
- })
100
-
101
- // Test 6: verifyEnvelope: tampered body returns false
102
- it('verifyEnvelope: tampered body returns false', async () => {
103
- const kp = await generateKeypair()
104
- const env = createEnvelope({
105
- sender: 'agent.a',
106
- body: { intent: 'PING', content: 'original' },
107
- })
108
- const signed = await signEnvelope(env, kp.privateKey)
109
- const tampered = {
110
- ...signed,
111
- body: { ...signed.body, content: 'tampered' },
112
- }
113
- const valid = await verifyEnvelope(tampered, kp.publicKey)
114
- expect(valid).toBe(false)
115
- })
116
-
117
- // Test 7: verifyEnvelope: wrong key returns false
118
- it('verifyEnvelope: wrong key returns false', async () => {
119
- const kp1 = await generateKeypair()
120
- const kp2 = await generateKeypair()
121
- const env = createEnvelope({
122
- sender: 'agent.a',
123
- body: { intent: 'PING', content: 'test' },
124
- })
125
- const signed = await signEnvelope(env, kp1.privateKey)
126
- const valid = await verifyEnvelope(signed, kp2.publicKey)
127
- expect(valid).toBe(false)
128
- })
129
-
130
- // Test 8: signRequest: produces Request with x-7h3-envelope header set
131
- it('signRequest: produces Request with x-7h3-envelope header', async () => {
132
- const kp = await generateKeypair()
133
- const req = new Request('https://example.com/api')
134
- const signed = await signRequest(req, {
135
- sender: 'agent.client',
136
- privateKey: kp.privateKey,
137
- })
138
- const headerVal = signed.headers.get(ENVELOPE_HEADER)
139
- expect(headerVal).not.toBeNull()
140
- expect(typeof headerVal).toBe('string')
141
- const parsed = JSON.parse(headerVal!)
142
- expect(parsed.signature).toBeDefined()
143
- expect(parsed.header.sender).toBe('agent.client')
144
- })
145
-
146
- // Test 9: isEnvelopeExpired: returns true for past timestamp
147
- it('isEnvelopeExpired: returns true for past timestamp', () => {
148
- const past = Date.now() - 120_000
149
- const env = {
150
- header: {
151
- version: '7h3/0.1' as const,
152
- messageId: 'x',
153
- nonce: 'y',
154
- sender: 'agent.z',
155
- timestampMs: past,
156
- ttlMs: 60_000,
157
- },
158
- body: { intent: 'PING' as const, content: 'old' },
159
- }
160
- expect(isEnvelopeExpired(env)).toBe(true)
161
- })
162
- })
@@ -1,257 +0,0 @@
1
- // 7h3 Protocol - Browser/Edge SDK
2
- // Pure Web Crypto API. Zero dependencies.
3
- // Works in browsers, Deno, Cloudflare Workers, Bun, and Node 20+.
4
-
5
- export const WIRE_VERSION = '7h3/0.1'
6
- export const ENVELOPE_HEADER = 'x-7h3-envelope'
7
- export const RESPONSE_HEADER = 'x-7h3-response'
8
-
9
- export type IntentKind = 'PING' | 'PONG' | 'CAPS' | 'TASK' | 'RESULT' | 'ERROR'
10
-
11
- export interface BrowserEnvelopeHeader {
12
- version: typeof WIRE_VERSION
13
- messageId: string
14
- timestampMs: number
15
- ttlMs: number
16
- sender: string
17
- recipient?: string
18
- nonce: string
19
- }
20
-
21
- export interface BrowserEnvelopeBody {
22
- intent: IntentKind
23
- content: string
24
- capability?: string
25
- correlationId?: string
26
- }
27
-
28
- export interface BrowserSignature {
29
- alg: 'ED25519'
30
- keyId: string
31
- value: string
32
- }
33
-
34
- export interface BrowserEnvelope {
35
- header: BrowserEnvelopeHeader
36
- body: BrowserEnvelopeBody
37
- signature?: BrowserSignature
38
- }
39
-
40
- export interface BrowserKeyPair {
41
- publicKey: string // base64url SPKI, no padding
42
- privateKey: string // base64url PKCS8, no padding
43
- }
44
-
45
- // ─── Internal helpers (NOT exported) ─────────────────────────────────────────
46
-
47
- function toBase64Url(buf: ArrayBuffer | Uint8Array): string {
48
- const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
49
- let binary = ''
50
- for (let i = 0; i < bytes.length; i++) {
51
- binary += String.fromCharCode(bytes[i])
52
- }
53
- return btoa(binary)
54
- .replace(/\+/g, '-')
55
- .replace(/\//g, '_')
56
- .replace(/=+$/g, '')
57
- }
58
-
59
- function fromBase64Url(str: string): Uint8Array {
60
- const padded = str
61
- .replace(/-/g, '+')
62
- .replace(/_/g, '/')
63
- .padEnd(Math.ceil(str.length / 4) * 4, '=')
64
- const binary = atob(padded)
65
- const bytes = new Uint8Array(binary.length)
66
- for (let i = 0; i < binary.length; i++) {
67
- bytes[i] = binary.charCodeAt(i)
68
- }
69
- return bytes
70
- }
71
-
72
- function generateNonce(): string {
73
- const bytes = new Uint8Array(16)
74
- crypto.getRandomValues(bytes)
75
- return toBase64Url(bytes)
76
- }
77
-
78
- function canonicalBody(b: BrowserEnvelopeBody): string {
79
- const parts: string[] = []
80
- if (b.capability !== undefined) {
81
- parts.push(`"capability":${JSON.stringify(b.capability)}`)
82
- }
83
- parts.push(`"content":${JSON.stringify(b.content)}`)
84
- if (b.correlationId !== undefined) {
85
- parts.push(`"correlationId":${JSON.stringify(b.correlationId)}`)
86
- }
87
- parts.push(`"intent":${JSON.stringify(b.intent)}`)
88
- return `{${parts.join(',')}}`
89
- }
90
-
91
- function canonicalHeader(h: BrowserEnvelopeHeader): string {
92
- const parts: string[] = [
93
- `"messageId":${JSON.stringify(h.messageId)}`,
94
- `"nonce":${JSON.stringify(h.nonce)}`,
95
- ]
96
- if (h.recipient !== undefined) {
97
- parts.push(`"recipient":${JSON.stringify(h.recipient)}`)
98
- }
99
- parts.push(`"sender":${JSON.stringify(h.sender)}`)
100
- parts.push(`"timestampMs":${h.timestampMs}`)
101
- parts.push(`"ttlMs":${h.ttlMs}`)
102
- parts.push(`"version":${JSON.stringify(h.version)}`)
103
- return `{${parts.join(',')}}`
104
- }
105
-
106
- // ─── Public API ───────────────────────────────────────────────────────────────
107
-
108
- export function canonicalizeEnvelope(envelope: Omit<BrowserEnvelope, 'signature'>): string {
109
- return `{"body":${canonicalBody(envelope.body)},"header":${canonicalHeader(envelope.header)}}`
110
- }
111
-
112
- export async function generateKeypair(): Promise<BrowserKeyPair> {
113
- const pair = await crypto.subtle.generateKey(
114
- { name: 'Ed25519' } as AlgorithmIdentifier,
115
- true,
116
- ['sign', 'verify'],
117
- ) as CryptoKeyPair
118
-
119
- const privateKeyRaw = await crypto.subtle.exportKey('pkcs8', pair.privateKey)
120
- const publicKeyRaw = await crypto.subtle.exportKey('spki', pair.publicKey)
121
-
122
- return {
123
- publicKey: toBase64Url(publicKeyRaw),
124
- privateKey: toBase64Url(privateKeyRaw),
125
- }
126
- }
127
-
128
- export async function signEnvelope(
129
- envelope: Omit<BrowserEnvelope, 'signature'>,
130
- privateKeyPkcs8Base64Url: string,
131
- keyId?: string,
132
- ): Promise<BrowserEnvelope> {
133
- const der = fromBase64Url(privateKeyPkcs8Base64Url)
134
- const cryptoKey = await crypto.subtle.importKey(
135
- 'pkcs8',
136
- der.buffer.slice(der.byteOffset, der.byteOffset + der.byteLength) as ArrayBuffer,
137
- { name: 'Ed25519' } as AlgorithmIdentifier,
138
- false,
139
- ['sign'],
140
- )
141
-
142
- const payload = canonicalizeEnvelope(envelope)
143
- const sigBuffer = await crypto.subtle.sign(
144
- 'Ed25519',
145
- cryptoKey,
146
- new TextEncoder().encode(payload),
147
- )
148
-
149
- return {
150
- ...envelope,
151
- signature: {
152
- alg: 'ED25519',
153
- keyId: keyId ?? generateNonce().slice(0, 16),
154
- value: toBase64Url(sigBuffer),
155
- },
156
- }
157
- }
158
-
159
- export async function verifyEnvelope(
160
- envelope: BrowserEnvelope,
161
- publicKeySpkiBase64Url: string,
162
- ): Promise<boolean> {
163
- if (!envelope.signature) return false
164
- if (envelope.signature.alg !== 'ED25519') return false
165
-
166
- const der = fromBase64Url(publicKeySpkiBase64Url)
167
- const cryptoKey = await crypto.subtle.importKey(
168
- 'spki',
169
- der.buffer.slice(der.byteOffset, der.byteOffset + der.byteLength) as ArrayBuffer,
170
- { name: 'Ed25519' } as AlgorithmIdentifier,
171
- false,
172
- ['verify'],
173
- )
174
-
175
- const unsigned: Omit<BrowserEnvelope, 'signature'> = {
176
- header: envelope.header,
177
- body: envelope.body,
178
- }
179
- const payload = canonicalizeEnvelope(unsigned)
180
- const sigBytes = fromBase64Url(envelope.signature.value)
181
-
182
- return crypto.subtle.verify(
183
- 'Ed25519',
184
- cryptoKey,
185
- sigBytes.buffer.slice(sigBytes.byteOffset, sigBytes.byteOffset + sigBytes.byteLength) as ArrayBuffer,
186
- new TextEncoder().encode(payload),
187
- )
188
- }
189
-
190
- export function createEnvelope(opts: {
191
- sender: string
192
- recipient?: string
193
- ttlMs?: number
194
- body: BrowserEnvelopeBody
195
- }): Omit<BrowserEnvelope, 'signature'> {
196
- return {
197
- header: {
198
- version: WIRE_VERSION,
199
- messageId: generateNonce(),
200
- timestampMs: Date.now(),
201
- ttlMs: opts.ttlMs ?? 60_000,
202
- sender: opts.sender,
203
- recipient: opts.recipient,
204
- nonce: generateNonce(),
205
- },
206
- body: opts.body,
207
- }
208
- }
209
-
210
- export async function signRequest(
211
- request: Request,
212
- opts: {
213
- sender: string
214
- recipient?: string
215
- ttlMs?: number
216
- privateKey: string
217
- keyId?: string
218
- },
219
- ): Promise<Request> {
220
- const envelope = createEnvelope({
221
- sender: opts.sender,
222
- recipient: opts.recipient,
223
- ttlMs: opts.ttlMs,
224
- body: {
225
- intent: 'TASK',
226
- content: request.url,
227
- },
228
- })
229
-
230
- const signed = await signEnvelope(envelope, opts.privateKey, opts.keyId)
231
-
232
- const headers = new Headers(request.headers)
233
- headers.set(ENVELOPE_HEADER, JSON.stringify(signed))
234
-
235
- return new Request(request, { headers })
236
- }
237
-
238
- export async function verifyResponseHeader(
239
- response: Response,
240
- publicKey: string,
241
- ): Promise<boolean> {
242
- const headerValue = response.headers.get(RESPONSE_HEADER)
243
- if (!headerValue) return false
244
-
245
- let parsed: BrowserEnvelope
246
- try {
247
- parsed = JSON.parse(headerValue) as BrowserEnvelope
248
- } catch {
249
- return false
250
- }
251
-
252
- return verifyEnvelope(parsed, publicKey)
253
- }
254
-
255
- export function isEnvelopeExpired(envelope: BrowserEnvelope): boolean {
256
- return envelope.header.timestampMs + envelope.header.ttlMs < Date.now()
257
- }
@@ -1,13 +0,0 @@
1
- {
2
- "name": "@7h3/protocol-browser",
3
- "version": "0.4.0",
4
- "description": "7h3 Protocol - Browser/Edge SDK. Pure Web Crypto API. Zero dependencies.",
5
- "type": "module",
6
- "main": "./index.js",
7
- "module": "./index.js",
8
- "types": "./index.d.ts",
9
- "exports": { ".": { "types": "./index.d.ts", "import": "./index.js" } },
10
- "keywords": ["7h3","protocol","browser","webcrypto","ed25519","signing","mcp"],
11
- "license": "MIT",
12
- "sideEffects": false
13
- }
package/sdk/go/go.mod DELETED
@@ -1,3 +0,0 @@
1
- module github.com/IceMasterT/7h3-protocol/sdk/go
2
-
3
- go 1.21