@labacacia/nps-sdk 1.0.0-alpha.6 → 1.0.0-alpha.7

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 (365) hide show
  1. package/CHANGELOG.cn.md +115 -0
  2. package/CHANGELOG.md +124 -0
  3. package/README.cn.md +3 -1
  4. package/README.md +3 -1
  5. package/dist/core/anchor-cache.d.ts +42 -0
  6. package/dist/core/anchor-cache.d.ts.map +1 -0
  7. package/dist/core/anchor-cache.js +104 -0
  8. package/dist/core/anchor-cache.js.map +1 -0
  9. package/dist/core/cache.d.ts +14 -0
  10. package/dist/core/cache.d.ts.map +1 -0
  11. package/dist/core/cache.js +80 -0
  12. package/dist/core/cache.js.map +1 -0
  13. package/dist/core/canonical-json.d.ts +12 -0
  14. package/dist/core/canonical-json.d.ts.map +1 -0
  15. package/dist/core/canonical-json.js +44 -0
  16. package/dist/core/canonical-json.js.map +1 -0
  17. package/dist/core/codec.d.ts +32 -0
  18. package/dist/core/codec.d.ts.map +1 -0
  19. package/dist/core/codec.js +119 -0
  20. package/dist/core/codec.js.map +1 -0
  21. package/dist/core/codecs/index.d.ts +4 -0
  22. package/dist/core/codecs/index.d.ts.map +1 -0
  23. package/{src/core/codecs/index.ts → dist/core/codecs/index.js} +1 -0
  24. package/dist/core/codecs/index.js.map +1 -0
  25. package/dist/core/codecs/ncp-codec.d.ts +39 -0
  26. package/dist/core/codecs/ncp-codec.d.ts.map +1 -0
  27. package/dist/core/codecs/ncp-codec.js +93 -0
  28. package/dist/core/codecs/ncp-codec.js.map +1 -0
  29. package/dist/core/codecs/tier1-json-codec.d.ts +10 -0
  30. package/dist/core/codecs/tier1-json-codec.d.ts.map +1 -0
  31. package/{src/core/codecs/tier1-json-codec.ts → dist/core/codecs/tier1-json-codec.js} +11 -16
  32. package/dist/core/codecs/tier1-json-codec.js.map +1 -0
  33. package/dist/core/codecs/tier2-msgpack-codec.d.ts +10 -0
  34. package/dist/core/codecs/tier2-msgpack-codec.d.ts.map +1 -0
  35. package/{src/core/codecs/tier2-msgpack-codec.ts → dist/core/codecs/tier2-msgpack-codec.js} +10 -14
  36. package/dist/core/codecs/tier2-msgpack-codec.js.map +1 -0
  37. package/dist/core/crypto-provider.d.ts +31 -0
  38. package/dist/core/crypto-provider.d.ts.map +1 -0
  39. package/dist/core/crypto-provider.js +10 -0
  40. package/dist/core/crypto-provider.js.map +1 -0
  41. package/dist/core/exceptions.d.ts +27 -0
  42. package/dist/core/exceptions.d.ts.map +1 -0
  43. package/dist/core/exceptions.js +52 -0
  44. package/dist/core/exceptions.js.map +1 -0
  45. package/dist/core/frame-header.d.ts +87 -0
  46. package/dist/core/frame-header.d.ts.map +1 -0
  47. package/dist/core/frame-header.js +185 -0
  48. package/dist/core/frame-header.js.map +1 -0
  49. package/dist/core/frame-registry.d.ts +35 -0
  50. package/dist/core/frame-registry.d.ts.map +1 -0
  51. package/dist/core/frame-registry.js +63 -0
  52. package/dist/core/frame-registry.js.map +1 -0
  53. package/dist/core/frames.d.ts +81 -0
  54. package/dist/core/frames.d.ts.map +1 -0
  55. package/dist/core/frames.js +154 -0
  56. package/dist/core/frames.js.map +1 -0
  57. package/dist/core/index.d.ts +11 -0
  58. package/dist/core/index.d.ts.map +1 -0
  59. package/{src/core/index.ts → dist/core/index.js} +3 -23
  60. package/dist/core/index.js.map +1 -0
  61. package/dist/core/registry.d.ts +11 -0
  62. package/dist/core/registry.d.ts.map +1 -0
  63. package/dist/core/registry.js +17 -0
  64. package/dist/core/registry.js.map +1 -0
  65. package/dist/core/status-codes.d.ts +29 -0
  66. package/dist/core/status-codes.d.ts.map +1 -0
  67. package/dist/core/status-codes.js +39 -0
  68. package/dist/core/status-codes.js.map +1 -0
  69. package/dist/index.d.ts +2 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/{src/index.ts → dist/index.js} +1 -1
  72. package/dist/index.js.map +1 -0
  73. package/dist/ncp/frames/anchor-frame.d.ts +29 -0
  74. package/dist/ncp/frames/anchor-frame.d.ts.map +1 -0
  75. package/dist/ncp/frames/anchor-frame.js +54 -0
  76. package/dist/ncp/frames/anchor-frame.js.map +1 -0
  77. package/dist/ncp/frames/caps-frame.d.ts +29 -0
  78. package/dist/ncp/frames/caps-frame.d.ts.map +1 -0
  79. package/dist/ncp/frames/caps-frame.js +29 -0
  80. package/dist/ncp/frames/caps-frame.js.map +1 -0
  81. package/dist/ncp/frames/diff-frame.d.ts +32 -0
  82. package/dist/ncp/frames/diff-frame.d.ts.map +1 -0
  83. package/dist/ncp/frames/diff-frame.js +37 -0
  84. package/dist/ncp/frames/diff-frame.js.map +1 -0
  85. package/dist/ncp/frames/error-frame.d.ts +16 -0
  86. package/dist/ncp/frames/error-frame.d.ts.map +1 -0
  87. package/dist/ncp/frames/error-frame.js +13 -0
  88. package/dist/ncp/frames/error-frame.js.map +1 -0
  89. package/dist/ncp/frames/hello-frame.d.ts +21 -0
  90. package/dist/ncp/frames/hello-frame.d.ts.map +1 -0
  91. package/dist/ncp/frames/hello-frame.js +25 -0
  92. package/dist/ncp/frames/hello-frame.js.map +1 -0
  93. package/dist/ncp/frames/stream-frame.d.ts +16 -0
  94. package/dist/ncp/frames/stream-frame.d.ts.map +1 -0
  95. package/dist/ncp/frames/stream-frame.js +18 -0
  96. package/dist/ncp/frames/stream-frame.js.map +1 -0
  97. package/dist/ncp/frames.d.ts +94 -0
  98. package/dist/ncp/frames.d.ts.map +1 -0
  99. package/dist/ncp/frames.js +192 -0
  100. package/dist/ncp/frames.js.map +1 -0
  101. package/dist/ncp/handshake.d.ts +30 -0
  102. package/dist/ncp/handshake.d.ts.map +1 -0
  103. package/dist/ncp/handshake.js +80 -0
  104. package/dist/ncp/handshake.js.map +1 -0
  105. package/dist/ncp/index.d.ts +12 -0
  106. package/dist/ncp/index.d.ts.map +1 -0
  107. package/{src/ncp/index.ts → dist/ncp/index.js} +1 -0
  108. package/dist/ncp/index.js.map +1 -0
  109. package/dist/ncp/ncp-error-codes.d.ts +23 -0
  110. package/dist/ncp/ncp-error-codes.d.ts.map +1 -0
  111. package/dist/ncp/ncp-error-codes.js +34 -0
  112. package/dist/ncp/ncp-error-codes.js.map +1 -0
  113. package/dist/ncp/ncp-patch-format.d.ts +7 -0
  114. package/dist/ncp/ncp-patch-format.d.ts.map +1 -0
  115. package/dist/ncp/ncp-patch-format.js +13 -0
  116. package/dist/ncp/ncp-patch-format.js.map +1 -0
  117. package/dist/ncp/preamble.d.ts +47 -0
  118. package/dist/ncp/preamble.d.ts.map +1 -0
  119. package/dist/ncp/preamble.js +74 -0
  120. package/dist/ncp/preamble.js.map +1 -0
  121. package/dist/ncp/registry.d.ts +3 -0
  122. package/dist/ncp/registry.d.ts.map +1 -0
  123. package/dist/ncp/registry.js +13 -0
  124. package/dist/ncp/registry.js.map +1 -0
  125. package/dist/ncp/stream-manager.d.ts +57 -0
  126. package/dist/ncp/stream-manager.d.ts.map +1 -0
  127. package/dist/ncp/stream-manager.js +163 -0
  128. package/dist/ncp/stream-manager.js.map +1 -0
  129. package/dist/ndp/dns-txt.d.ts +35 -0
  130. package/dist/ndp/dns-txt.d.ts.map +1 -0
  131. package/dist/ndp/dns-txt.js +67 -0
  132. package/dist/ndp/dns-txt.js.map +1 -0
  133. package/dist/ndp/frames.d.ts +56 -0
  134. package/dist/ndp/frames.d.ts.map +1 -0
  135. package/dist/ndp/frames.js +87 -0
  136. package/dist/ndp/frames.js.map +1 -0
  137. package/dist/ndp/index.d.ts +6 -0
  138. package/dist/ndp/index.d.ts.map +1 -0
  139. package/{src/ndp/index.ts → dist/ndp/index.js} +1 -1
  140. package/dist/ndp/index.js.map +1 -0
  141. package/dist/ndp/ndp-registry.d.ts +13 -0
  142. package/dist/ndp/ndp-registry.d.ts.map +1 -0
  143. package/dist/ndp/ndp-registry.js +104 -0
  144. package/dist/ndp/ndp-registry.js.map +1 -0
  145. package/dist/ndp/registry.d.ts +3 -0
  146. package/dist/ndp/registry.d.ts.map +1 -0
  147. package/dist/ndp/registry.js +10 -0
  148. package/dist/ndp/registry.js.map +1 -0
  149. package/dist/ndp/validator.d.ts +18 -0
  150. package/dist/ndp/validator.d.ts.map +1 -0
  151. package/dist/ndp/validator.js +48 -0
  152. package/dist/ndp/validator.js.map +1 -0
  153. package/dist/nip/acme/client.d.ts +31 -0
  154. package/dist/nip/acme/client.d.ts.map +1 -0
  155. package/dist/nip/acme/client.js +136 -0
  156. package/dist/nip/acme/client.js.map +1 -0
  157. package/dist/nip/acme/index.d.ts +6 -0
  158. package/dist/nip/acme/index.d.ts.map +1 -0
  159. package/{src/nip/acme/index.ts → dist/nip/acme/index.js} +1 -1
  160. package/dist/nip/acme/index.js.map +1 -0
  161. package/dist/nip/acme/jws.d.ts +31 -0
  162. package/dist/nip/acme/jws.d.ts.map +1 -0
  163. package/dist/nip/acme/jws.js +76 -0
  164. package/dist/nip/acme/jws.js.map +1 -0
  165. package/dist/nip/acme/messages.d.ts +71 -0
  166. package/dist/nip/acme/messages.d.ts.map +1 -0
  167. package/dist/nip/acme/messages.js +4 -0
  168. package/dist/nip/acme/messages.js.map +1 -0
  169. package/dist/nip/acme/server.d.ts +41 -0
  170. package/dist/nip/acme/server.d.ts.map +1 -0
  171. package/dist/nip/acme/server.js +458 -0
  172. package/dist/nip/acme/server.js.map +1 -0
  173. package/dist/nip/acme/wire.d.ts +19 -0
  174. package/dist/nip/acme/wire.d.ts.map +1 -0
  175. package/dist/nip/acme/wire.js +21 -0
  176. package/dist/nip/acme/wire.js.map +1 -0
  177. package/dist/nip/assurance-level.d.ts +19 -0
  178. package/dist/nip/assurance-level.d.ts.map +1 -0
  179. package/dist/nip/assurance-level.js +38 -0
  180. package/dist/nip/assurance-level.js.map +1 -0
  181. package/dist/nip/cert-format.d.ts +5 -0
  182. package/dist/nip/cert-format.d.ts.map +1 -0
  183. package/dist/nip/cert-format.js +6 -0
  184. package/dist/nip/cert-format.js.map +1 -0
  185. package/dist/nip/error-codes.d.ts +25 -0
  186. package/dist/nip/error-codes.d.ts.map +1 -0
  187. package/{src/nip/error-codes.ts → dist/nip/error-codes.js} +19 -25
  188. package/dist/nip/error-codes.js.map +1 -0
  189. package/dist/nip/frames.d.ts +53 -0
  190. package/dist/nip/frames.d.ts.map +1 -0
  191. package/dist/nip/frames.js +106 -0
  192. package/dist/nip/frames.js.map +1 -0
  193. package/dist/nip/identity.d.ts +18 -0
  194. package/dist/nip/identity.d.ts.map +1 -0
  195. package/dist/nip/identity.js +94 -0
  196. package/dist/nip/identity.js.map +1 -0
  197. package/dist/nip/index.d.ts +11 -0
  198. package/dist/nip/index.d.ts.map +1 -0
  199. package/{src/nip/index.ts → dist/nip/index.js} +3 -2
  200. package/dist/nip/index.js.map +1 -0
  201. package/dist/nip/registry.d.ts +3 -0
  202. package/dist/nip/registry.d.ts.map +1 -0
  203. package/dist/nip/registry.js +10 -0
  204. package/dist/nip/registry.js.map +1 -0
  205. package/dist/nip/reputation-client.d.ts +116 -0
  206. package/dist/nip/reputation-client.d.ts.map +1 -0
  207. package/dist/nip/reputation-client.js +261 -0
  208. package/dist/nip/reputation-client.js.map +1 -0
  209. package/dist/nip/verifier.d.ts +23 -0
  210. package/dist/nip/verifier.d.ts.map +1 -0
  211. package/dist/nip/verifier.js +90 -0
  212. package/dist/nip/verifier.js.map +1 -0
  213. package/dist/nip/x509/builder.d.ts +35 -0
  214. package/dist/nip/x509/builder.d.ts.map +1 -0
  215. package/dist/nip/x509/builder.js +59 -0
  216. package/dist/nip/x509/builder.js.map +1 -0
  217. package/dist/nip/x509/index.d.ts +4 -0
  218. package/dist/nip/x509/index.d.ts.map +1 -0
  219. package/{src/nip/x509/index.ts → dist/nip/x509/index.js} +1 -1
  220. package/dist/nip/x509/index.js.map +1 -0
  221. package/dist/nip/x509/oids.d.ts +16 -0
  222. package/dist/nip/x509/oids.d.ts.map +1 -0
  223. package/{src/nip/x509/oids.ts → dist/nip/x509/oids.js} +5 -10
  224. package/dist/nip/x509/oids.js.map +1 -0
  225. package/dist/nip/x509/verifier.d.ts +26 -0
  226. package/dist/nip/x509/verifier.d.ts.map +1 -0
  227. package/dist/nip/x509/verifier.js +171 -0
  228. package/dist/nip/x509/verifier.js.map +1 -0
  229. package/dist/nop/client.d.ts +34 -0
  230. package/dist/nop/client.d.ts.map +1 -0
  231. package/dist/nop/client.js +90 -0
  232. package/dist/nop/client.js.map +1 -0
  233. package/dist/nop/frames.d.ts +65 -0
  234. package/dist/nop/frames.d.ts.map +1 -0
  235. package/dist/nop/frames.js +148 -0
  236. package/dist/nop/frames.js.map +1 -0
  237. package/dist/nop/index.d.ts +5 -0
  238. package/dist/nop/index.d.ts.map +1 -0
  239. package/{src/nop/index.ts → dist/nop/index.js} +1 -1
  240. package/dist/nop/index.js.map +1 -0
  241. package/dist/nop/models.d.ts +58 -0
  242. package/dist/nop/models.d.ts.map +1 -0
  243. package/dist/nop/models.js +50 -0
  244. package/dist/nop/models.js.map +1 -0
  245. package/dist/nop/nop-types.d.ts +136 -0
  246. package/dist/nop/nop-types.d.ts.map +1 -0
  247. package/dist/nop/nop-types.js +44 -0
  248. package/dist/nop/nop-types.js.map +1 -0
  249. package/dist/nop/registry.d.ts +3 -0
  250. package/dist/nop/registry.d.ts.map +1 -0
  251. package/dist/nop/registry.js +11 -0
  252. package/dist/nop/registry.js.map +1 -0
  253. package/dist/nwp/anchor-client.d.ts +109 -0
  254. package/dist/nwp/anchor-client.d.ts.map +1 -0
  255. package/dist/nwp/anchor-client.js +279 -0
  256. package/dist/nwp/anchor-client.js.map +1 -0
  257. package/dist/nwp/client.d.ts +22 -0
  258. package/dist/nwp/client.d.ts.map +1 -0
  259. package/dist/nwp/client.js +101 -0
  260. package/dist/nwp/client.js.map +1 -0
  261. package/dist/nwp/frames.d.ts +46 -0
  262. package/dist/nwp/frames.d.ts.map +1 -0
  263. package/dist/nwp/frames.js +81 -0
  264. package/dist/nwp/frames.js.map +1 -0
  265. package/dist/nwp/index.d.ts +5 -0
  266. package/dist/nwp/index.d.ts.map +1 -0
  267. package/{src/nwp/index.ts → dist/nwp/index.js} +2 -1
  268. package/dist/nwp/index.js.map +1 -0
  269. package/dist/nwp/registry.d.ts +3 -0
  270. package/dist/nwp/registry.d.ts.map +1 -0
  271. package/dist/nwp/registry.js +9 -0
  272. package/dist/nwp/registry.js.map +1 -0
  273. package/dist/setup.d.ts +10 -0
  274. package/dist/setup.d.ts.map +1 -0
  275. package/{src/setup.ts → dist/setup.js} +13 -16
  276. package/dist/setup.js.map +1 -0
  277. package/package.json +12 -1
  278. package/CONTRIBUTING.cn.md +0 -35
  279. package/CONTRIBUTING.md +0 -35
  280. package/nip-ca-server/Dockerfile +0 -27
  281. package/nip-ca-server/README.md +0 -45
  282. package/nip-ca-server/db/001_init.sql +0 -25
  283. package/nip-ca-server/docker-compose.yml +0 -29
  284. package/nip-ca-server/package.json +0 -23
  285. package/nip-ca-server/src/ca.ts +0 -155
  286. package/nip-ca-server/src/db.ts +0 -104
  287. package/nip-ca-server/src/index.ts +0 -157
  288. package/nip-ca-server/tsconfig.json +0 -13
  289. package/src/core/anchor-cache.ts +0 -129
  290. package/src/core/cache.ts +0 -93
  291. package/src/core/canonical-json.ts +0 -50
  292. package/src/core/codec.ts +0 -158
  293. package/src/core/codecs/ncp-codec.ts +0 -170
  294. package/src/core/crypto-provider.ts +0 -47
  295. package/src/core/exceptions.ts +0 -57
  296. package/src/core/frame-header.ts +0 -282
  297. package/src/core/frame-registry.ts +0 -91
  298. package/src/core/frames.ts +0 -184
  299. package/src/core/registry.ts +0 -28
  300. package/src/core/status-codes.ts +0 -47
  301. package/src/ncp/frames/anchor-frame.ts +0 -87
  302. package/src/ncp/frames/caps-frame.ts +0 -59
  303. package/src/ncp/frames/diff-frame.ts +0 -69
  304. package/src/ncp/frames/error-frame.ts +0 -26
  305. package/src/ncp/frames/hello-frame.ts +0 -50
  306. package/src/ncp/frames/stream-frame.ts +0 -35
  307. package/src/ncp/frames.ts +0 -251
  308. package/src/ncp/handshake.ts +0 -95
  309. package/src/ncp/ncp-error-codes.ts +0 -36
  310. package/src/ncp/ncp-patch-format.ts +0 -16
  311. package/src/ncp/preamble.ts +0 -79
  312. package/src/ncp/registry.ts +0 -15
  313. package/src/ncp/stream-manager.ts +0 -212
  314. package/src/ndp/dns-txt.ts +0 -86
  315. package/src/ndp/frames.ts +0 -124
  316. package/src/ndp/ndp-registry.ts +0 -116
  317. package/src/ndp/registry.ts +0 -12
  318. package/src/ndp/validator.ts +0 -64
  319. package/src/nip/acme/client.ts +0 -185
  320. package/src/nip/acme/jws.ts +0 -109
  321. package/src/nip/acme/messages.ts +0 -85
  322. package/src/nip/acme/server.ts +0 -480
  323. package/src/nip/acme/wire.ts +0 -24
  324. package/src/nip/assurance-level.ts +0 -40
  325. package/src/nip/cert-format.ts +0 -9
  326. package/src/nip/frames.ts +0 -138
  327. package/src/nip/identity.ts +0 -113
  328. package/src/nip/registry.ts +0 -12
  329. package/src/nip/verifier.ts +0 -122
  330. package/src/nip/x509/builder.ts +0 -91
  331. package/src/nip/x509/verifier.ts +0 -214
  332. package/src/nop/client.ts +0 -103
  333. package/src/nop/frames.ts +0 -181
  334. package/src/nop/models.ts +0 -79
  335. package/src/nop/nop-types.ts +0 -208
  336. package/src/nop/registry.ts +0 -13
  337. package/src/nwp/client.ts +0 -114
  338. package/src/nwp/frames.ts +0 -116
  339. package/src/nwp/registry.ts +0 -11
  340. package/tests/_rfc0002-keys.ts +0 -57
  341. package/tests/core/anchor-cache.test.ts +0 -242
  342. package/tests/core/codec.test.ts +0 -205
  343. package/tests/core/frame-registry.test.ts +0 -46
  344. package/tests/core.test.ts +0 -327
  345. package/tests/ncp/diff-binary-bitset.test.ts +0 -107
  346. package/tests/ncp/e2e-enc-reject.test.ts +0 -93
  347. package/tests/ncp/err-error-frame.test.ts +0 -152
  348. package/tests/ncp/frames.test.ts +0 -359
  349. package/tests/ncp/framing.test.ts +0 -233
  350. package/tests/ncp/hello-frame.test.ts +0 -122
  351. package/tests/ncp/inline-anchor.test.ts +0 -88
  352. package/tests/ncp/preamble.test.ts +0 -93
  353. package/tests/ncp/security.test.ts +0 -184
  354. package/tests/ncp/stream-window.test.ts +0 -167
  355. package/tests/ncp/stream.test.ts +0 -242
  356. package/tests/ncp/version-negotiation.test.ts +0 -123
  357. package/tests/ndp.test.ts +0 -377
  358. package/tests/nip-acme-agent01.test.ts +0 -192
  359. package/tests/nip-x509.test.ts +0 -280
  360. package/tests/nip.test.ts +0 -184
  361. package/tests/nop.test.ts +0 -344
  362. package/tests/nwp.test.ts +0 -237
  363. package/tsconfig.json +0 -20
  364. package/tsup.config.ts +0 -20
  365. package/vitest.config.ts +0 -10
package/tests/ndp.test.ts DELETED
@@ -1,377 +0,0 @@
1
- // Copyright 2026 INNO LOTUS PTY LTD
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- import { describe, expect, it } from "vitest";
5
- import { AnnounceFrame, ResolveFrame, GraphFrame } from "../src/ndp/frames.js";
6
- import { InMemoryNdpRegistry } from "../src/ndp/ndp-registry.js";
7
- import { NdpAnnounceValidator, NdpAnnounceResult } from "../src/ndp/validator.js";
8
- import { parseNpsTxtRecord, extractHostFromTarget, type DnsTxtLookup } from "../src/ndp/dns-txt.js";
9
- import { NipIdentity } from "../src/nip/identity.js";
10
- import { createFullRegistry } from "../src/setup.js";
11
- import { NpsFrameCodec } from "../src/core/index.js";
12
-
13
- // ── Helpers ───────────────────────────────────────────────────────────────────
14
-
15
- const NID = "urn:nps:node:example.com:data";
16
- const ADDRS = [{ host: "example.com", port: 17433, protocol: "nwp" }];
17
- const CAPS = ["nwp/query", "nwp/stream"];
18
-
19
- function makeAnnounce(nid = NID, ttl = 300, id?: NipIdentity): AnnounceFrame {
20
- const ident = id ?? NipIdentity.generate();
21
- const timestamp = "2026-01-01T00:00:00Z";
22
- const unsigned = {
23
- nid, addresses: ADDRS, capabilities: CAPS, ttl, timestamp, node_type: null,
24
- };
25
- const sig = ident.sign(unsigned);
26
- return new AnnounceFrame(nid, ADDRS, CAPS, ttl, timestamp, sig);
27
- }
28
-
29
- // ── AnnounceFrame round-trip ──────────────────────────────────────────────────
30
-
31
- describe("AnnounceFrame", () => {
32
- it("toDict / fromDict roundtrip", () => {
33
- const f = makeAnnounce();
34
- const back = AnnounceFrame.fromDict(f.toDict());
35
- expect(back.nid).toBe(NID);
36
- expect(back.ttl).toBe(300);
37
- expect(back.addresses[0]?.port).toBe(17433);
38
- expect(back.capabilities).toContain("nwp/query");
39
- });
40
-
41
- it("unsignedDict omits signature", () => {
42
- const f = makeAnnounce();
43
- const d = f.unsignedDict();
44
- expect(d["signature"]).toBeUndefined();
45
- expect(d["nid"]).toBe(NID);
46
- });
47
-
48
- it("codec roundtrip (MsgPack)", () => {
49
- const registry = createFullRegistry();
50
- const codec = new NpsFrameCodec(registry);
51
- const f = makeAnnounce();
52
- const back = codec.decode(codec.encode(f)) as AnnounceFrame;
53
- expect(back).toBeInstanceOf(AnnounceFrame);
54
- expect(back.nid).toBe(NID);
55
- });
56
- });
57
-
58
- // ── ResolveFrame round-trip ───────────────────────────────────────────────────
59
-
60
- describe("ResolveFrame", () => {
61
- it("toDict / fromDict with resolved", () => {
62
- const f = new ResolveFrame("nwp://example.com/data", "urn:nps:node:a:b", { host: "example.com", port: 17433, ttl: 300 });
63
- const back = ResolveFrame.fromDict(f.toDict());
64
- expect(back.target).toBe("nwp://example.com/data");
65
- expect(back.requesterNid).toBe("urn:nps:node:a:b");
66
- expect(back.resolved?.port).toBe(17433);
67
- });
68
-
69
- it("toDict / fromDict without optional fields", () => {
70
- const f = new ResolveFrame("nwp://example.com/data");
71
- const back = ResolveFrame.fromDict(f.toDict());
72
- expect(back.requesterNid).toBeUndefined();
73
- expect(back.resolved).toBeUndefined();
74
- });
75
- });
76
-
77
- // ── GraphFrame round-trip ─────────────────────────────────────────────────────
78
-
79
- describe("GraphFrame", () => {
80
- it("toDict / fromDict with nodes", () => {
81
- const nodes = [{ nid: NID, addresses: ADDRS, capabilities: CAPS }];
82
- const f = new GraphFrame(1, true, nodes);
83
- const back = GraphFrame.fromDict(f.toDict());
84
- expect(back.seq).toBe(1);
85
- expect(back.initialSync).toBe(true);
86
- expect(back.nodes?.[0]?.nid).toBe(NID);
87
- expect(back.patch).toBeUndefined();
88
- });
89
- });
90
-
91
- // ── InMemoryNdpRegistry ───────────────────────────────────────────────────────
92
-
93
- describe("InMemoryNdpRegistry", () => {
94
- it("announce + getByNid", () => {
95
- const reg = new InMemoryNdpRegistry();
96
- const f = makeAnnounce();
97
- reg.announce(f);
98
- expect(reg.getByNid(NID)).toBe(f);
99
- });
100
-
101
- it("getByNid returns undefined for unknown NID", () => {
102
- const reg = new InMemoryNdpRegistry();
103
- expect(reg.getByNid("urn:nps:node:unknown:x")).toBeUndefined();
104
- });
105
-
106
- it("announce with ttl=0 removes entry", () => {
107
- const reg = new InMemoryNdpRegistry();
108
- reg.announce(makeAnnounce(NID, 300));
109
- expect(reg.getByNid(NID)).toBeDefined();
110
- reg.announce(makeAnnounce(NID, 0));
111
- expect(reg.getByNid(NID)).toBeUndefined();
112
- });
113
-
114
- it("TTL expiry — getByNid returns undefined after expiry", () => {
115
- const reg = new InMemoryNdpRegistry();
116
- let now = 0;
117
- reg.clock = () => now;
118
- reg.announce(makeAnnounce(NID, 10));
119
- now = 11_000;
120
- expect(reg.getByNid(NID)).toBeUndefined();
121
- });
122
-
123
- it("resolve returns host/port for matching target", () => {
124
- const reg = new InMemoryNdpRegistry();
125
- reg.announce(makeAnnounce());
126
- const r = reg.resolve("nwp://example.com/data/sub");
127
- expect(r).toBeDefined();
128
- expect(r?.host).toBe("example.com");
129
- expect(r?.port).toBe(17433);
130
- });
131
-
132
- it("resolve returns undefined for non-matching target", () => {
133
- const reg = new InMemoryNdpRegistry();
134
- reg.announce(makeAnnounce());
135
- expect(reg.resolve("nwp://other.com/data")).toBeUndefined();
136
- });
137
-
138
- it("getAll returns active entries", () => {
139
- const reg = new InMemoryNdpRegistry();
140
- let now = 0;
141
- reg.clock = () => now;
142
- reg.announce(makeAnnounce("urn:nps:node:a.com:x", 100));
143
- reg.announce(makeAnnounce("urn:nps:node:b.com:y", 1));
144
- now = 2_000; // b expired
145
- const all = reg.getAll();
146
- expect(all).toHaveLength(1);
147
- expect(all[0]?.nid).toBe("urn:nps:node:a.com:x");
148
- });
149
-
150
- it("resolve skips expired entries", () => {
151
- const reg = new InMemoryNdpRegistry();
152
- let now = 0;
153
- reg.clock = () => now;
154
- reg.announce(makeAnnounce(NID, 5));
155
- now = 10_000;
156
- expect(reg.resolve("nwp://example.com/data")).toBeUndefined();
157
- });
158
- });
159
-
160
- // ── nwpTargetMatchesNid ───────────────────────────────────────────────────────
161
-
162
- describe("InMemoryNdpRegistry.nwpTargetMatchesNid", () => {
163
- const match = InMemoryNdpRegistry.nwpTargetMatchesNid;
164
-
165
- it("exact match", () => {
166
- expect(match("urn:nps:node:example.com:data", "nwp://example.com/data")).toBe(true);
167
- });
168
-
169
- it("sub-path match", () => {
170
- expect(match("urn:nps:node:example.com:data", "nwp://example.com/data/sub")).toBe(true);
171
- });
172
-
173
- it("different authority does not match", () => {
174
- expect(match("urn:nps:node:other.com:data", "nwp://example.com/data")).toBe(false);
175
- });
176
-
177
- it("sibling path does not match", () => {
178
- expect(match("urn:nps:node:example.com:data", "nwp://example.com/dataset")).toBe(false);
179
- });
180
-
181
- it("invalid NID format returns false", () => {
182
- expect(match("invalid-nid", "nwp://example.com/data")).toBe(false);
183
- });
184
-
185
- it("non-nwp:// target returns false", () => {
186
- expect(match("urn:nps:node:example.com:data", "http://example.com/data")).toBe(false);
187
- });
188
-
189
- it("target without path slash returns false", () => {
190
- expect(match("urn:nps:node:example.com:data", "nwp://example.com")).toBe(false);
191
- });
192
- });
193
-
194
- // ── NdpAnnounceResult ─────────────────────────────────────────────────────────
195
-
196
- describe("NdpAnnounceResult", () => {
197
- it("ok() returns isValid=true", () => {
198
- const r = NdpAnnounceResult.ok();
199
- expect(r.isValid).toBe(true);
200
- expect(r.errorCode).toBeUndefined();
201
- });
202
-
203
- it("fail() returns isValid=false with code + message", () => {
204
- const r = NdpAnnounceResult.fail("NDP-ERR", "bad sig");
205
- expect(r.isValid).toBe(false);
206
- expect(r.errorCode).toBe("NDP-ERR");
207
- expect(r.message).toBe("bad sig");
208
- });
209
- });
210
-
211
- // ── NdpAnnounceValidator ──────────────────────────────────────────────────────
212
-
213
- describe("NdpAnnounceValidator", () => {
214
- it("fails when no key registered", () => {
215
- const v = new NdpAnnounceValidator();
216
- const r = v.validate(makeAnnounce());
217
- expect(r.isValid).toBe(false);
218
- expect(r.errorCode).toBe("NDP-ANNOUNCE-NID-MISMATCH");
219
- });
220
-
221
- it("validates a correctly signed frame", () => {
222
- const ident = NipIdentity.generate();
223
- const v = new NdpAnnounceValidator();
224
- v.registerPublicKey(NID, ident.pubKeyString);
225
- const f = makeAnnounce(NID, 300, ident);
226
- expect(v.validate(f).isValid).toBe(true);
227
- });
228
-
229
- it("rejects tampered frame (wrong signature)", () => {
230
- const ident = NipIdentity.generate();
231
- const v = new NdpAnnounceValidator();
232
- v.registerPublicKey(NID, ident.pubKeyString);
233
- // Build frame signed by a different key
234
- const other = NipIdentity.generate();
235
- const f = makeAnnounce(NID, 300, other);
236
- expect(v.validate(f).isValid).toBe(false);
237
- });
238
-
239
- it("rejects signature with wrong prefix", () => {
240
- const ident = NipIdentity.generate();
241
- const v = new NdpAnnounceValidator();
242
- v.registerPublicKey(NID, ident.pubKeyString);
243
- const f = new AnnounceFrame(NID, ADDRS, CAPS, 300, "2026-01-01T00:00:00Z", "rsa:invalid");
244
- const r = v.validate(f);
245
- expect(r.isValid).toBe(false);
246
- expect(r.errorCode).toBe("NDP-ANNOUNCE-SIG-INVALID");
247
- });
248
-
249
- it("rejects corrupted base64 signature", () => {
250
- const ident = NipIdentity.generate();
251
- const v = new NdpAnnounceValidator();
252
- v.registerPublicKey(NID, ident.pubKeyString);
253
- const f = new AnnounceFrame(NID, ADDRS, CAPS, 300, "2026-01-01T00:00:00Z", "ed25519:!!!garbage!!!");
254
- const r = v.validate(f);
255
- expect(r.isValid).toBe(false);
256
- });
257
-
258
- it("removePublicKey removes registration", () => {
259
- const ident = NipIdentity.generate();
260
- const v = new NdpAnnounceValidator();
261
- v.registerPublicKey(NID, ident.pubKeyString);
262
- v.removePublicKey(NID);
263
- expect(v.knownPublicKeys.has(NID)).toBe(false);
264
- expect(v.validate(makeAnnounce(NID, 300, ident)).isValid).toBe(false);
265
- });
266
-
267
- it("knownPublicKeys is readonly view", () => {
268
- const v = new NdpAnnounceValidator();
269
- v.registerPublicKey("urn:nps:node:a:1", "ed25519:aabb");
270
- expect(v.knownPublicKeys.size).toBe(1);
271
- });
272
- });
273
-
274
- // ── DnsTxtResolution ──────────────────────────────────────────────────────────
275
-
276
- describe("DnsTxtResolution", () => {
277
- // ── parseNpsTxtRecord ───────────────────────────────────────────────────────
278
-
279
- it("parseNpsTxtRecord - valid full record", () => {
280
- const parts = ["v=nps1 type=memory port=17434 nid=urn:nps:node:api.example.com:products fp=sha256:a3f9"];
281
- const result = parseNpsTxtRecord(parts, "api.example.com");
282
- expect(result).toBeDefined();
283
- expect(result?.host).toBe("api.example.com");
284
- expect(result?.port).toBe(17434);
285
- expect(result?.ttl).toBe(300);
286
- expect(result?.certFingerprint).toBe("sha256:a3f9");
287
- });
288
-
289
- it("parseNpsTxtRecord - missing v returns undefined", () => {
290
- const parts = ["type=memory port=17434 nid=urn:nps:node:api.example.com:products"];
291
- expect(parseNpsTxtRecord(parts, "api.example.com")).toBeUndefined();
292
- });
293
-
294
- it("parseNpsTxtRecord - wrong v returns undefined", () => {
295
- const parts = ["v=nps2 nid=urn:nps:node:api.example.com:products"];
296
- expect(parseNpsTxtRecord(parts, "api.example.com")).toBeUndefined();
297
- });
298
-
299
- it("parseNpsTxtRecord - missing nid returns undefined", () => {
300
- const parts = ["v=nps1 type=memory port=17434"];
301
- expect(parseNpsTxtRecord(parts, "api.example.com")).toBeUndefined();
302
- });
303
-
304
- it("parseNpsTxtRecord - default port", () => {
305
- const parts = ["v=nps1 nid=urn:nps:node:api.example.com:products"];
306
- const result = parseNpsTxtRecord(parts, "api.example.com");
307
- expect(result).toBeDefined();
308
- expect(result?.port).toBe(17433);
309
- });
310
-
311
- it("parseNpsTxtRecord - with fingerprint", () => {
312
- const parts = ["v=nps1 nid=urn:nps:node:api.example.com:products fp=sha256:deadbeef"];
313
- const result = parseNpsTxtRecord(parts, "api.example.com");
314
- expect(result?.certFingerprint).toBe("sha256:deadbeef");
315
- });
316
-
317
- // ── resolveWithDns ──────────────────────────────────────────────────────────
318
-
319
- it("resolveWithDns - uses registry first (dns not called)", async () => {
320
- const reg = new InMemoryNdpRegistry();
321
- reg.announce(makeAnnounce("urn:nps:node:example.com:data", 300));
322
-
323
- let dnsCalled = false;
324
- const mockDns: DnsTxtLookup = {
325
- resolveTxt: async (_hostname: string) => {
326
- dnsCalled = true;
327
- return [];
328
- },
329
- };
330
-
331
- const result = await reg.resolveWithDns("nwp://example.com/data", mockDns);
332
- expect(result).toBeDefined();
333
- expect(result?.host).toBe("example.com");
334
- expect(dnsCalled).toBe(false);
335
- });
336
-
337
- it("resolveWithDns - falls back to dns when registry empty", async () => {
338
- const reg = new InMemoryNdpRegistry();
339
-
340
- const mockDns: DnsTxtLookup = {
341
- resolveTxt: async (hostname: string) => {
342
- expect(hostname).toBe("_nps-node.api.example.com");
343
- return [["v=nps1 nid=urn:nps:node:api.example.com:products port=17434"]];
344
- },
345
- };
346
-
347
- const result = await reg.resolveWithDns("nwp://api.example.com/products", mockDns);
348
- expect(result).toBeDefined();
349
- expect(result?.host).toBe("api.example.com");
350
- expect(result?.port).toBe(17434);
351
- });
352
-
353
- it("resolveWithDns - invalid txt returns undefined", async () => {
354
- const reg = new InMemoryNdpRegistry();
355
-
356
- const mockDns: DnsTxtLookup = {
357
- resolveTxt: async (_hostname: string) => {
358
- // Missing v=nps1 and nid — invalid record
359
- return [["type=memory port=17434"]];
360
- },
361
- };
362
-
363
- const result = await reg.resolveWithDns("nwp://api.example.com/products", mockDns);
364
- expect(result).toBeUndefined();
365
- });
366
-
367
- it("resolveWithDns - empty records returns undefined", async () => {
368
- const reg = new InMemoryNdpRegistry();
369
-
370
- const mockDns: DnsTxtLookup = {
371
- resolveTxt: async (_hostname: string) => [],
372
- };
373
-
374
- const result = await reg.resolveWithDns("nwp://api.example.com/products", mockDns);
375
- expect(result).toBeUndefined();
376
- });
377
- });
@@ -1,192 +0,0 @@
1
- // Copyright 2026 INNO LOTUS PTY LTD
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- // TypeScript parallel of Java AcmeAgent01Tests / .NET AcmeAgent01Tests
5
- // per NPS-RFC-0002 §4.4. End-to-end agent-01 round-trip plus tampered-signature
6
- // negative path.
7
-
8
- import { describe, expect, it } from "vitest";
9
- import * as ed25519 from "@noble/ed25519";
10
- import { sha512 } from "@noble/hashes/sha512";
11
- import * as x509 from "@peculiar/x509";
12
-
13
- import { AssuranceLevel } from "../src/nip/assurance-level.js";
14
- import * as ec from "../src/nip/error-codes.js";
15
- import { issueRoot } from "../src/nip/x509/builder.js";
16
- import { verify as verifyX509 } from "../src/nip/x509/verifier.js";
17
- import { AcmeClient } from "../src/nip/acme/client.js";
18
- import { AcmeServer } from "../src/nip/acme/server.js";
19
- import * as Jws from "../src/nip/acme/jws.js";
20
- import * as wire from "../src/nip/acme/wire.js";
21
- import type {
22
- Authorization, ChallengeRespondPayload, Directory, NewAccountPayload,
23
- NewOrderPayload, Order, ProblemDetail,
24
- } from "../src/nip/acme/messages.js";
25
- import { generateDualKeyPair, randomHexSerial } from "./_rfc0002-keys.js";
26
-
27
- ed25519.etc.sha512Sync = (...m) => sha512(ed25519.etc.concatBytes(...m));
28
- x509.cryptoProvider.set(globalThis.crypto);
29
-
30
- interface Fixture {
31
- caNid: string;
32
- agentNid: string;
33
- caRoot: x509.X509Certificate;
34
- agentKeys: Awaited<ReturnType<typeof generateDualKeyPair>>;
35
- server: AcmeServer;
36
- }
37
-
38
- async function createFixture(): Promise<Fixture> {
39
- const caNid = "urn:nps:ca:acme-test";
40
- const agentNid = "urn:nps:agent:acme-test:1";
41
-
42
- const caKeys = await generateDualKeyPair();
43
- const caRoot = await issueRoot({
44
- caNid, caKeys: caKeys.webCrypto,
45
- notBefore: new Date(Date.now() - 60_000),
46
- notAfter: new Date(Date.now() + 365 * 24 * 3600_000),
47
- serialNumber: "01",
48
- });
49
-
50
- const agentKeys = await generateDualKeyPair();
51
-
52
- const server = new AcmeServer({
53
- caNid, caKeys: caKeys.webCrypto, caRootCert: caRoot,
54
- certValidityMs: 30 * 24 * 3600_000,
55
- });
56
- await server.start();
57
-
58
- return { caNid, agentNid, caRoot, agentKeys, server };
59
- }
60
-
61
- describe("ACME agent-01 — RFC-0002 §4.4 round-trip", () => {
62
-
63
- it("issueAgentCert round-trip returns a PEM chain that verifies against the CA root", async () => {
64
- const fx = await createFixture();
65
- try {
66
- const client = new AcmeClient({
67
- directoryUrl: fx.server.directoryUrl,
68
- privateKey: fx.agentKeys.privRaw,
69
- publicKey: fx.agentKeys.pubRaw,
70
- webCryptoKeys: fx.agentKeys.webCrypto,
71
- });
72
-
73
- const pem = await client.issueAgentCert(fx.agentNid);
74
- expect(pem).toContain("BEGIN CERTIFICATE");
75
-
76
- // Parse PEM chain and re-encode as base64url DER for the X.509 verifier.
77
- const certs = x509.PemConverter.decode(pem)
78
- .map((buf) => new x509.X509Certificate(buf));
79
- expect(certs.length).toBeGreaterThan(0);
80
- const chainB64 = certs.map((c) => b64uEncode(new Uint8Array(c.rawData)));
81
-
82
- const result = await verifyX509({
83
- certChainBase64UrlDer: chainB64,
84
- assertedNid: fx.agentNid,
85
- assertedAssuranceLevel: AssuranceLevel.ANONYMOUS,
86
- trustedRootCerts: [fx.caRoot],
87
- });
88
- expect(result.valid).toBe(true);
89
- expect(extractCn(result.leaf!.subject)).toBe(fx.agentNid);
90
- } finally {
91
- await fx.server.close();
92
- }
93
- });
94
-
95
- it("respondAgent01 with tampered agent_signature → server returns NIP-ACME-CHALLENGE-FAILED", async () => {
96
- const fx = await createFixture();
97
- try {
98
- // Drive the flow manually so we can splice in a forged challenge response.
99
- const dirResp = await fetch(fx.server.directoryUrl);
100
- expect(dirResp.ok).toBe(true);
101
- const dir = await dirResp.json() as Directory;
102
-
103
- const nonceResp = await fetch(dir.newNonce, { method: "HEAD" });
104
- let nonce = nonceResp.headers.get("Replay-Nonce")!;
105
- expect(nonce).not.toBeNull();
106
-
107
- // newAccount.
108
- const jwk = Jws.jwkFromPublicKey(fx.agentKeys.pubRaw);
109
- const acctEnv = Jws.sign(
110
- { alg: Jws.ALG_EDDSA, nonce, url: dir.newAccount, jwk },
111
- { termsOfServiceAgreed: true } as NewAccountPayload,
112
- fx.agentKeys.privRaw);
113
- const acctResp = await postJose(dir.newAccount, acctEnv);
114
- expect(acctResp.status).toBe(201);
115
- const accountUrl = acctResp.headers.get("Location")!;
116
- nonce = acctResp.headers.get("Replay-Nonce")!;
117
-
118
- // newOrder.
119
- const orderEnv = Jws.sign(
120
- { alg: Jws.ALG_EDDSA, nonce, url: dir.newOrder, kid: accountUrl },
121
- {
122
- identifiers: [{ type: wire.IDENTIFIER_TYPE_NID, value: fx.agentNid }],
123
- } as NewOrderPayload,
124
- fx.agentKeys.privRaw);
125
- const orderResp = await postJose(dir.newOrder, orderEnv);
126
- expect(orderResp.status).toBe(201);
127
- const order = await orderResp.json() as Order;
128
- nonce = orderResp.headers.get("Replay-Nonce")!;
129
-
130
- // POST-as-GET on authz to discover the challenge URL + token.
131
- const authzEnv = Jws.sign(
132
- { alg: Jws.ALG_EDDSA, nonce, url: order.authorizations[0], kid: accountUrl },
133
- null, fx.agentKeys.privRaw);
134
- const authzResp = await postJose(order.authorizations[0], authzEnv);
135
- const authz = await authzResp.json() as Authorization;
136
- nonce = authzResp.headers.get("Replay-Nonce")!;
137
-
138
- const challenge = authz.challenges.find((c) => c.type === wire.CHALLENGE_AGENT_01);
139
- expect(challenge).toBeDefined();
140
-
141
- // ★ Tampered: sign challenge token with a *different* keypair, but submit
142
- // the JWS envelope under the registered account's key — server verifies
143
- // the JWS sig (passes with account key) and then verifies the agent
144
- // signature against the account key (fails).
145
- const forger = await generateDualKeyPair();
146
- const tokenBytes = new TextEncoder().encode(challenge!.token);
147
- const forgedSig = ed25519.sign(tokenBytes, forger.privRaw);
148
-
149
- const chEnv = Jws.sign(
150
- { alg: Jws.ALG_EDDSA, nonce, url: challenge!.url, kid: accountUrl },
151
- { agent_signature: Jws.b64uEncode(forgedSig) } as ChallengeRespondPayload,
152
- fx.agentKeys.privRaw);
153
- const chResp = await postJose(challenge!.url, chEnv);
154
-
155
- expect(chResp.status).toBe(400);
156
- const problem = await chResp.json() as ProblemDetail;
157
- expect(problem.type).toBe(ec.ACME_CHALLENGE_FAILED);
158
- } finally {
159
- await fx.server.close();
160
- }
161
- });
162
- });
163
-
164
- // ── helpers ──────────────────────────────────────────────────────────────────
165
-
166
- async function postJose(url: string, env: Jws.Envelope): Promise<Response> {
167
- return await fetch(url, {
168
- method: "POST",
169
- headers: { "Content-Type": wire.CONTENT_TYPE_JOSE_JSON },
170
- body: JSON.stringify(env),
171
- });
172
- }
173
-
174
- function b64uEncode(bytes: Uint8Array): string {
175
- return Buffer.from(bytes).toString("base64").replace(/=+$/, "")
176
- .replace(/\+/g, "-").replace(/\//g, "_");
177
- }
178
-
179
- function extractCn(dn: string): string | null {
180
- for (const rdn of dn.split(",")) {
181
- const t = rdn.trim();
182
- if (t.startsWith("CN=")) {
183
- let v = t.slice(3);
184
- if (v.startsWith("\"") && v.endsWith("\"")) v = v.slice(1, -1);
185
- return v.replace(/\\([",+;<>\\])/g, "$1");
186
- }
187
- }
188
- return null;
189
- }
190
-
191
- // Touch the import so unused-symbol lint doesn't trip.
192
- void randomHexSerial;