@labacacia/nps-sdk 1.0.0-alpha.3 → 1.0.0-alpha.5

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 (241) hide show
  1. package/CHANGELOG.cn.md +73 -0
  2. package/CHANGELOG.md +82 -0
  3. package/README.cn.md +17 -4
  4. package/README.md +17 -4
  5. package/dist/core/anchor-cache.js +104 -0
  6. package/dist/core/anchor-cache.js.map +1 -0
  7. package/dist/core/cache.js +80 -0
  8. package/dist/core/cache.js.map +1 -0
  9. package/dist/core/canonical-json.js +44 -0
  10. package/dist/core/canonical-json.js.map +1 -0
  11. package/dist/core/codec.js +119 -0
  12. package/dist/core/codec.js.map +1 -0
  13. package/dist/core/codecs/index.js +6 -0
  14. package/dist/core/codecs/index.js.map +1 -0
  15. package/dist/core/codecs/ncp-codec.js +93 -0
  16. package/dist/core/codecs/ncp-codec.js.map +1 -0
  17. package/dist/core/codecs/tier1-json-codec.js +28 -0
  18. package/dist/core/codecs/tier1-json-codec.js.map +1 -0
  19. package/dist/core/codecs/tier2-msgpack-codec.js +26 -0
  20. package/dist/core/codecs/tier2-msgpack-codec.js.map +1 -0
  21. package/dist/core/crypto-provider.js +10 -0
  22. package/dist/core/crypto-provider.js.map +1 -0
  23. package/dist/core/exceptions.js +52 -0
  24. package/dist/core/exceptions.js.map +1 -0
  25. package/dist/core/frame-header.js +185 -0
  26. package/dist/core/frame-header.js.map +1 -0
  27. package/dist/core/frame-registry.js +63 -0
  28. package/dist/core/frame-registry.js.map +1 -0
  29. package/dist/core/frames.js +154 -0
  30. package/dist/core/frames.js.map +1 -0
  31. package/dist/core/index.js +21 -405
  32. package/dist/core/index.js.map +1 -1
  33. package/dist/core/registry.js +17 -0
  34. package/dist/core/registry.js.map +1 -0
  35. package/dist/core/status-codes.d.ts +1 -0
  36. package/dist/core/status-codes.d.ts.map +1 -1
  37. package/dist/core/status-codes.js +39 -0
  38. package/dist/core/status-codes.js.map +1 -0
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +9 -5
  41. package/dist/index.js.map +1 -1
  42. package/dist/ncp/frames/anchor-frame.js +54 -0
  43. package/dist/ncp/frames/anchor-frame.js.map +1 -0
  44. package/dist/ncp/frames/caps-frame.js +29 -0
  45. package/dist/ncp/frames/caps-frame.js.map +1 -0
  46. package/dist/ncp/frames/diff-frame.js +37 -0
  47. package/dist/ncp/frames/diff-frame.js.map +1 -0
  48. package/dist/ncp/frames/error-frame.js +13 -0
  49. package/dist/ncp/frames/error-frame.js.map +1 -0
  50. package/dist/ncp/frames/hello-frame.js +25 -0
  51. package/dist/ncp/frames/hello-frame.js.map +1 -0
  52. package/dist/ncp/frames/stream-frame.js +18 -0
  53. package/dist/ncp/frames/stream-frame.js.map +1 -0
  54. package/dist/ncp/frames.js +192 -0
  55. package/dist/ncp/frames.js.map +1 -0
  56. package/dist/ncp/handshake.js +80 -0
  57. package/dist/ncp/handshake.js.map +1 -0
  58. package/dist/ncp/index.d.ts +1 -0
  59. package/dist/ncp/index.d.ts.map +1 -1
  60. package/dist/ncp/index.js +13 -368
  61. package/dist/ncp/index.js.map +1 -1
  62. package/dist/ncp/ncp-error-codes.d.ts +1 -0
  63. package/dist/ncp/ncp-error-codes.d.ts.map +1 -1
  64. package/dist/ncp/ncp-error-codes.js +34 -0
  65. package/dist/ncp/ncp-error-codes.js.map +1 -0
  66. package/dist/ncp/ncp-patch-format.js +13 -0
  67. package/dist/ncp/ncp-patch-format.js.map +1 -0
  68. package/dist/ncp/preamble.d.ts +47 -0
  69. package/dist/ncp/preamble.d.ts.map +1 -0
  70. package/dist/ncp/preamble.js +74 -0
  71. package/dist/ncp/preamble.js.map +1 -0
  72. package/dist/ncp/registry.js +13 -0
  73. package/dist/ncp/registry.js.map +1 -0
  74. package/dist/ncp/stream-manager.js +163 -0
  75. package/dist/ncp/stream-manager.js.map +1 -0
  76. package/dist/ndp/dns-txt.d.ts +35 -0
  77. package/dist/ndp/dns-txt.d.ts.map +1 -0
  78. package/dist/ndp/dns-txt.js +67 -0
  79. package/dist/ndp/dns-txt.js.map +1 -0
  80. package/dist/ndp/frames.js +87 -0
  81. package/dist/ndp/frames.js.map +1 -0
  82. package/dist/ndp/index.d.ts +1 -0
  83. package/dist/ndp/index.d.ts.map +1 -1
  84. package/dist/ndp/index.js +7 -223
  85. package/dist/ndp/index.js.map +1 -1
  86. package/dist/ndp/ndp-registry.d.ts +2 -0
  87. package/dist/ndp/ndp-registry.d.ts.map +1 -1
  88. package/dist/ndp/ndp-registry.js +104 -0
  89. package/dist/ndp/ndp-registry.js.map +1 -0
  90. package/dist/ndp/registry.js +10 -0
  91. package/dist/ndp/registry.js.map +1 -0
  92. package/dist/ndp/validator.js +48 -0
  93. package/dist/ndp/validator.js.map +1 -0
  94. package/dist/nip/acme/client.d.ts +31 -0
  95. package/dist/nip/acme/client.d.ts.map +1 -0
  96. package/dist/nip/acme/client.js +136 -0
  97. package/dist/nip/acme/client.js.map +1 -0
  98. package/dist/nip/acme/index.d.ts +6 -0
  99. package/dist/nip/acme/index.d.ts.map +1 -0
  100. package/dist/nip/acme/index.js +8 -0
  101. package/dist/nip/acme/index.js.map +1 -0
  102. package/dist/nip/acme/jws.d.ts +31 -0
  103. package/dist/nip/acme/jws.d.ts.map +1 -0
  104. package/dist/nip/acme/jws.js +76 -0
  105. package/dist/nip/acme/jws.js.map +1 -0
  106. package/dist/nip/acme/messages.d.ts +71 -0
  107. package/dist/nip/acme/messages.d.ts.map +1 -0
  108. package/dist/nip/acme/messages.js +4 -0
  109. package/dist/nip/acme/messages.js.map +1 -0
  110. package/dist/nip/acme/server.d.ts +41 -0
  111. package/dist/nip/acme/server.d.ts.map +1 -0
  112. package/dist/nip/acme/server.js +458 -0
  113. package/dist/nip/acme/server.js.map +1 -0
  114. package/dist/nip/acme/wire.d.ts +19 -0
  115. package/dist/nip/acme/wire.d.ts.map +1 -0
  116. package/dist/nip/acme/wire.js +21 -0
  117. package/dist/nip/acme/wire.js.map +1 -0
  118. package/dist/nip/assurance-level.d.ts +19 -0
  119. package/dist/nip/assurance-level.d.ts.map +1 -0
  120. package/dist/nip/assurance-level.js +38 -0
  121. package/dist/nip/assurance-level.js.map +1 -0
  122. package/dist/nip/cert-format.d.ts +5 -0
  123. package/dist/nip/cert-format.d.ts.map +1 -0
  124. package/dist/nip/cert-format.js +6 -0
  125. package/dist/nip/cert-format.js.map +1 -0
  126. package/dist/nip/error-codes.d.ts +25 -0
  127. package/dist/nip/error-codes.d.ts.map +1 -0
  128. package/dist/nip/error-codes.js +32 -0
  129. package/dist/nip/error-codes.js.map +1 -0
  130. package/dist/nip/frames.d.ts +10 -1
  131. package/dist/nip/frames.d.ts.map +1 -1
  132. package/dist/nip/frames.js +106 -0
  133. package/dist/nip/frames.js.map +1 -0
  134. package/dist/nip/identity.js +94 -0
  135. package/dist/nip/identity.js.map +1 -0
  136. package/dist/nip/index.d.ts +6 -0
  137. package/dist/nip/index.d.ts.map +1 -1
  138. package/dist/nip/index.js +12 -187
  139. package/dist/nip/index.js.map +1 -1
  140. package/dist/nip/registry.js +10 -0
  141. package/dist/nip/registry.js.map +1 -0
  142. package/dist/nip/verifier.d.ts +23 -0
  143. package/dist/nip/verifier.d.ts.map +1 -0
  144. package/dist/nip/verifier.js +90 -0
  145. package/dist/nip/verifier.js.map +1 -0
  146. package/dist/nip/x509/builder.d.ts +35 -0
  147. package/dist/nip/x509/builder.d.ts.map +1 -0
  148. package/dist/nip/x509/builder.js +59 -0
  149. package/dist/nip/x509/builder.js.map +1 -0
  150. package/dist/nip/x509/index.d.ts +4 -0
  151. package/dist/nip/x509/index.d.ts.map +1 -0
  152. package/dist/nip/x509/index.js +6 -0
  153. package/dist/nip/x509/index.js.map +1 -0
  154. package/dist/nip/x509/oids.d.ts +17 -0
  155. package/dist/nip/x509/oids.d.ts.map +1 -0
  156. package/dist/nip/x509/oids.js +23 -0
  157. package/dist/nip/x509/oids.js.map +1 -0
  158. package/dist/nip/x509/verifier.d.ts +26 -0
  159. package/dist/nip/x509/verifier.d.ts.map +1 -0
  160. package/dist/nip/x509/verifier.js +171 -0
  161. package/dist/nip/x509/verifier.js.map +1 -0
  162. package/dist/nop/client.js +90 -0
  163. package/dist/nop/client.js.map +1 -0
  164. package/dist/nop/frames.js +148 -0
  165. package/dist/nop/frames.js.map +1 -0
  166. package/dist/nop/index.js +6 -789
  167. package/dist/nop/index.js.map +1 -1
  168. package/dist/nop/models.js +50 -0
  169. package/dist/nop/models.js.map +1 -0
  170. package/dist/nop/nop-types.js +44 -0
  171. package/dist/nop/nop-types.js.map +1 -0
  172. package/dist/nop/registry.js +11 -0
  173. package/dist/nop/registry.js.map +1 -0
  174. package/dist/nwp/client.js +101 -0
  175. package/dist/nwp/client.js.map +1 -0
  176. package/dist/nwp/error-codes.d.ts +42 -0
  177. package/dist/nwp/error-codes.d.ts.map +1 -0
  178. package/dist/nwp/error-codes.js +53 -0
  179. package/dist/nwp/error-codes.js.map +1 -0
  180. package/dist/nwp/frames.js +81 -0
  181. package/dist/nwp/frames.js.map +1 -0
  182. package/dist/nwp/index.d.ts +1 -0
  183. package/dist/nwp/index.d.ts.map +1 -1
  184. package/dist/nwp/index.js +6 -693
  185. package/dist/nwp/index.js.map +1 -1
  186. package/dist/nwp/registry.js +9 -0
  187. package/dist/nwp/registry.js.map +1 -0
  188. package/dist/setup.js +29 -0
  189. package/dist/setup.js.map +1 -0
  190. package/doc/nps-sdk.nip.cn.md +30 -0
  191. package/doc/nps-sdk.nip.md +30 -0
  192. package/doc/nps-sdk.nwp.cn.md +71 -0
  193. package/doc/nps-sdk.nwp.md +71 -0
  194. package/package.json +2 -1
  195. package/src/core/status-codes.ts +1 -0
  196. package/src/index.ts +1 -1
  197. package/src/ncp/index.ts +1 -0
  198. package/src/ncp/ncp-error-codes.ts +2 -0
  199. package/src/ncp/preamble.ts +79 -0
  200. package/src/ndp/dns-txt.ts +86 -0
  201. package/src/ndp/index.ts +1 -0
  202. package/src/ndp/ndp-registry.ts +34 -0
  203. package/src/nip/acme/client.ts +185 -0
  204. package/src/nip/acme/index.ts +8 -0
  205. package/src/nip/acme/jws.ts +109 -0
  206. package/src/nip/acme/messages.ts +85 -0
  207. package/src/nip/acme/server.ts +480 -0
  208. package/src/nip/acme/wire.ts +24 -0
  209. package/src/nip/assurance-level.ts +40 -0
  210. package/src/nip/cert-format.ts +9 -0
  211. package/src/nip/error-codes.ts +38 -0
  212. package/src/nip/frames.ts +35 -3
  213. package/src/nip/index.ts +8 -0
  214. package/src/nip/verifier.ts +122 -0
  215. package/src/nip/x509/builder.ts +91 -0
  216. package/src/nip/x509/index.ts +6 -0
  217. package/src/nip/x509/oids.ts +28 -0
  218. package/src/nip/x509/verifier.ts +214 -0
  219. package/src/nop/client.ts +1 -1
  220. package/src/nwp/client.ts +4 -4
  221. package/src/nwp/error-codes.ts +62 -0
  222. package/src/nwp/index.ts +1 -0
  223. package/tests/_rfc0002-keys.ts +57 -0
  224. package/tests/ncp/preamble.test.ts +93 -0
  225. package/tests/ndp.test.ts +106 -0
  226. package/tests/nip-acme-agent01.test.ts +192 -0
  227. package/tests/nip-x509.test.ts +280 -0
  228. package/dist/core/index.cjs +0 -452
  229. package/dist/core/index.cjs.map +0 -1
  230. package/dist/index.cjs +0 -8
  231. package/dist/index.cjs.map +0 -1
  232. package/dist/ncp/index.cjs +0 -388
  233. package/dist/ncp/index.cjs.map +0 -1
  234. package/dist/ndp/index.cjs +0 -252
  235. package/dist/ndp/index.cjs.map +0 -1
  236. package/dist/nip/index.cjs +0 -214
  237. package/dist/nip/index.cjs.map +0 -1
  238. package/dist/nop/index.cjs +0 -823
  239. package/dist/nop/index.cjs.map +0 -1
  240. package/dist/nwp/index.cjs +0 -720
  241. package/dist/nwp/index.cjs.map +0 -1
package/dist/ndp/index.js CHANGED
@@ -1,224 +1,8 @@
1
- import * as ed25519 from '@noble/ed25519';
2
- import { sha512 } from '@noble/hashes/sha512';
3
-
4
- // src/ndp/frames.ts
5
- var AnnounceFrame = class _AnnounceFrame {
6
- constructor(nid, addresses, capabilities, ttl, timestamp, signature, nodeType) {
7
- this.nid = nid;
8
- this.addresses = addresses;
9
- this.capabilities = capabilities;
10
- this.ttl = ttl;
11
- this.timestamp = timestamp;
12
- this.signature = signature;
13
- this.nodeType = nodeType;
14
- }
15
- nid;
16
- addresses;
17
- capabilities;
18
- ttl;
19
- timestamp;
20
- signature;
21
- nodeType;
22
- frameType = 48 /* ANNOUNCE */;
23
- preferredTier = 1 /* MSGPACK */;
24
- unsignedDict() {
25
- return {
26
- nid: this.nid,
27
- addresses: this.addresses,
28
- capabilities: this.capabilities,
29
- ttl: this.ttl,
30
- timestamp: this.timestamp,
31
- node_type: this.nodeType ?? null
32
- };
33
- }
34
- toDict() {
35
- return { ...this.unsignedDict(), signature: this.signature };
36
- }
37
- static fromDict(data) {
38
- return new _AnnounceFrame(
39
- data["nid"],
40
- data["addresses"],
41
- data["capabilities"],
42
- data["ttl"],
43
- data["timestamp"],
44
- data["signature"],
45
- data["node_type"] ?? void 0
46
- );
47
- }
48
- };
49
- var ResolveFrame = class _ResolveFrame {
50
- constructor(target, requesterNid, resolved) {
51
- this.target = target;
52
- this.requesterNid = requesterNid;
53
- this.resolved = resolved;
54
- }
55
- target;
56
- requesterNid;
57
- resolved;
58
- frameType = 49 /* RESOLVE */;
59
- preferredTier = 1 /* MSGPACK */;
60
- toDict() {
61
- return {
62
- target: this.target,
63
- requester_nid: this.requesterNid ?? null,
64
- resolved: this.resolved ?? null
65
- };
66
- }
67
- static fromDict(data) {
68
- return new _ResolveFrame(
69
- data["target"],
70
- data["requester_nid"] ?? void 0,
71
- data["resolved"] ?? void 0
72
- );
73
- }
74
- };
75
- var GraphFrame = class _GraphFrame {
76
- constructor(seq, initialSync, nodes, patch) {
77
- this.seq = seq;
78
- this.initialSync = initialSync;
79
- this.nodes = nodes;
80
- this.patch = patch;
81
- }
82
- seq;
83
- initialSync;
84
- nodes;
85
- patch;
86
- frameType = 50 /* GRAPH */;
87
- preferredTier = 1 /* MSGPACK */;
88
- toDict() {
89
- return {
90
- seq: this.seq,
91
- initial_sync: this.initialSync,
92
- nodes: this.nodes ?? null,
93
- patch: this.patch ?? null
94
- };
95
- }
96
- static fromDict(data) {
97
- return new _GraphFrame(
98
- data["seq"],
99
- data["initial_sync"],
100
- data["nodes"] ?? void 0,
101
- data["patch"] ?? void 0
102
- );
103
- }
104
- };
105
-
106
- // src/ndp/registry.ts
107
- function registerNdpFrames(registry) {
108
- registry.register(48 /* ANNOUNCE */, AnnounceFrame);
109
- registry.register(49 /* RESOLVE */, ResolveFrame);
110
- registry.register(50 /* GRAPH */, GraphFrame);
111
- }
112
-
113
- // src/ndp/ndp-registry.ts
114
- var InMemoryNdpRegistry = class _InMemoryNdpRegistry {
115
- _store = /* @__PURE__ */ new Map();
116
- // Replaceable for testing
117
- clock = () => Date.now();
118
- announce(frame) {
119
- const expiresAt = this.clock() + frame.ttl * 1e3;
120
- if (frame.ttl === 0) {
121
- this._store.delete(frame.nid);
122
- return;
123
- }
124
- this._store.set(frame.nid, { frame, expiresAt });
125
- }
126
- getByNid(nid) {
127
- const entry = this._store.get(nid);
128
- if (entry === void 0) return void 0;
129
- if (this.clock() > entry.expiresAt) {
130
- this._store.delete(nid);
131
- return void 0;
132
- }
133
- return entry.frame;
134
- }
135
- resolve(target) {
136
- for (const [nid, entry] of this._store) {
137
- if (this.clock() > entry.expiresAt) {
138
- this._store.delete(nid);
139
- continue;
140
- }
141
- if (!_InMemoryNdpRegistry.nwpTargetMatchesNid(nid, target)) continue;
142
- const addr = entry.frame.addresses[0];
143
- if (addr === void 0) continue;
144
- return { host: addr.host, port: addr.port, ttl: entry.frame.ttl };
145
- }
146
- return void 0;
147
- }
148
- getAll() {
149
- const now = this.clock();
150
- const result = [];
151
- for (const [nid, entry] of this._store) {
152
- if (now > entry.expiresAt) {
153
- this._store.delete(nid);
154
- continue;
155
- }
156
- result.push(entry.frame);
157
- }
158
- return result;
159
- }
160
- static nwpTargetMatchesNid(nid, target) {
161
- const nidParts = nid.split(":");
162
- if (nidParts.length < 5 || nidParts[0] !== "urn" || nidParts[1] !== "nps" || nidParts[2] !== "node") {
163
- return false;
164
- }
165
- if (!target.startsWith("nwp://")) return false;
166
- const nidAuthority = nidParts[3];
167
- const nidPath = nidParts[4];
168
- const rest = target.slice("nwp://".length);
169
- const slashIdx = rest.indexOf("/");
170
- if (slashIdx === -1) return false;
171
- const urlAuthority = rest.slice(0, slashIdx);
172
- const urlPath = rest.slice(slashIdx + 1);
173
- if (urlAuthority !== nidAuthority) return false;
174
- if (urlPath === nidPath) return true;
175
- if (urlPath.startsWith(nidPath + "/")) return true;
176
- return false;
177
- }
178
- };
179
- ed25519.etc.sha512Sync = (...m) => sha512(ed25519.etc.concatBytes(...m));
180
- var NdpAnnounceResult = {
181
- ok: () => ({ isValid: true }),
182
- fail: (errorCode, message) => ({ isValid: false, errorCode, message })
183
- };
184
- var NdpAnnounceValidator = class {
185
- _keys = /* @__PURE__ */ new Map();
186
- // nid → "ed25519:<hex>"
187
- registerPublicKey(nid, encodedPubKey) {
188
- this._keys.set(nid, encodedPubKey);
189
- }
190
- removePublicKey(nid) {
191
- this._keys.delete(nid);
192
- }
193
- get knownPublicKeys() {
194
- return this._keys;
195
- }
196
- validate(frame) {
197
- const encoded = this._keys.get(frame.nid);
198
- if (encoded === void 0) {
199
- return NdpAnnounceResult.fail("NDP-ANNOUNCE-NID-MISMATCH", `No public key registered for NID: ${frame.nid}`);
200
- }
201
- try {
202
- const prefix = "ed25519:";
203
- const pubHex = encoded.startsWith(prefix) ? encoded.slice(prefix.length) : encoded;
204
- const pubKey = Buffer.from(pubHex, "hex");
205
- const sig = frame.signature;
206
- if (!sig.startsWith(prefix)) {
207
- return NdpAnnounceResult.fail("NDP-ANNOUNCE-SIG-INVALID", "Signature must start with 'ed25519:'");
208
- }
209
- const sigBytes = Buffer.from(sig.slice(prefix.length), "base64");
210
- const unsigned = frame.unsignedDict();
211
- const canonical = JSON.stringify(unsigned, Object.keys(unsigned).sort());
212
- const message = new TextEncoder().encode(canonical);
213
- const valid = ed25519.verify(sigBytes, message, pubKey);
214
- if (!valid) return NdpAnnounceResult.fail("NDP-ANNOUNCE-SIG-INVALID", "Ed25519 signature verification failed.");
215
- return NdpAnnounceResult.ok();
216
- } catch {
217
- return NdpAnnounceResult.fail("NDP-ANNOUNCE-SIG-INVALID", "Ed25519 signature verification failed.");
218
- }
219
- }
220
- };
221
-
222
- export { AnnounceFrame, GraphFrame, InMemoryNdpRegistry, NdpAnnounceResult, NdpAnnounceValidator, ResolveFrame, registerNdpFrames };
223
- //# sourceMappingURL=index.js.map
1
+ // Copyright 2026 INNO LOTUS PTY LTD
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export * from "./frames.js";
4
+ export * from "./registry.js";
5
+ export * from "./ndp-registry.js";
6
+ export * from "./validator.js";
7
+ export * from "./dns-txt.js";
224
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/ndp/frames.ts","../../src/ndp/registry.ts","../../src/ndp/ndp-registry.ts","../../src/ndp/validator.ts"],"names":[],"mappings":";;;;AA0BO,IAAM,aAAA,GAAN,MAAM,cAAA,CAAkC;AAAA,EAI7C,YACkB,GAAA,EACA,SAAA,EACA,cACA,GAAA,EACA,SAAA,EACA,WACA,QAAA,EAChB;AAPgB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACA,IAAA,IAAA,CAAA,YAAA,GAAA,YAAA;AACA,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA,EACf;AAAA,EAPe,GAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,GAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EAVT,SAAA,GAAA,EAAA;AAAA,EACA,aAAA,GAAA,CAAA;AAAA,EAYT,YAAA,GAAwC;AACtC,IAAA,OAAO;AAAA,MACL,KAAc,IAAA,CAAK,GAAA;AAAA,MACnB,WAAc,IAAA,CAAK,SAAA;AAAA,MACnB,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,KAAc,IAAA,CAAK,GAAA;AAAA,MACnB,WAAc,IAAA,CAAK,SAAA;AAAA,MACnB,SAAA,EAAc,KAAK,QAAA,IAAY;AAAA,KACjC;AAAA,EACF;AAAA,EAEA,MAAA,GAAkC;AAChC,IAAA,OAAO,EAAE,GAAG,IAAA,CAAK,cAAa,EAAG,SAAA,EAAW,KAAK,SAAA,EAAU;AAAA,EAC7D;AAAA,EAEA,OAAO,SAAS,IAAA,EAA8C;AAC5D,IAAA,OAAO,IAAI,cAAA;AAAA,MACT,KAAK,KAAK,CAAA;AAAA,MACV,KAAK,WAAW,CAAA;AAAA,MAChB,KAAK,cAAc,CAAA;AAAA,MACnB,KAAK,KAAK,CAAA;AAAA,MACV,KAAK,WAAW,CAAA;AAAA,MAChB,KAAK,WAAW,CAAA;AAAA,MACf,IAAA,CAAK,WAAW,CAAA,IAA0B;AAAA,KAC7C;AAAA,EACF;AACF;AAEO,IAAM,YAAA,GAAN,MAAM,aAAA,CAAiC;AAAA,EAI5C,WAAA,CACkB,MAAA,EACA,YAAA,EACA,QAAA,EAChB;AAHgB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,YAAA,GAAA,YAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA,EACf;AAAA,EAHe,MAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EANT,SAAA,GAAA,EAAA;AAAA,EACA,aAAA,GAAA,CAAA;AAAA,EAQT,MAAA,GAAkC;AAChC,IAAA,OAAO;AAAA,MACL,QAAe,IAAA,CAAK,MAAA;AAAA,MACpB,aAAA,EAAe,KAAK,YAAA,IAAgB,IAAA;AAAA,MACpC,QAAA,EAAe,KAAK,QAAA,IAAgB;AAAA,KACtC;AAAA,EACF;AAAA,EAEA,OAAO,SAAS,IAAA,EAA6C;AAC3D,IAAA,OAAO,IAAI,aAAA;AAAA,MACT,KAAK,QAAQ,CAAA;AAAA,MACZ,IAAA,CAAK,eAAe,CAAA,IAAuB,MAAA;AAAA,MAC3C,IAAA,CAAK,UAAU,CAAA,IAAsC;AAAA,KACxD;AAAA,EACF;AACF;AAEO,IAAM,UAAA,GAAN,MAAM,WAAA,CAA+B;AAAA,EAI1C,WAAA,CACkB,GAAA,EACA,WAAA,EACA,KAAA,EACA,KAAA,EAChB;AAJgB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AACA,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AACA,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EACf;AAAA,EAJe,GAAA;AAAA,EACA,WAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EAPT,SAAA,GAAA,EAAA;AAAA,EACA,aAAA,GAAA,CAAA;AAAA,EAST,MAAA,GAAkC;AAChC,IAAA,OAAO;AAAA,MACL,KAAc,IAAA,CAAK,GAAA;AAAA,MACnB,cAAc,IAAA,CAAK,WAAA;AAAA,MACnB,KAAA,EAAc,KAAK,KAAA,IAAS,IAAA;AAAA,MAC5B,KAAA,EAAc,KAAK,KAAA,IAAS;AAAA,KAC9B;AAAA,EACF;AAAA,EAEA,OAAO,SAAS,IAAA,EAA2C;AACzD,IAAA,OAAO,IAAI,WAAA;AAAA,MACT,KAAK,KAAK,CAAA;AAAA,MACV,KAAK,cAAc,CAAA;AAAA,MAClB,IAAA,CAAK,OAAO,CAAA,IAA+B,MAAA;AAAA,MAC3C,IAAA,CAAK,OAAO,CAAA,IAA0C;AAAA,KACzD;AAAA,EACF;AACF;;;ACpHO,SAAS,kBAAkB,QAAA,EAA+B;AAC/D,EAAA,QAAA,CAAS,4BAA6B,aAAa,CAAA;AACnD,EAAA,QAAA,CAAS,2BAA6B,YAAY,CAAA;AAClD,EAAA,QAAA,CAAS,yBAA6B,UAAU,CAAA;AAClD;;;ACDO,IAAM,mBAAA,GAAN,MAAM,oBAAA,CAAoB;AAAA,EACd,MAAA,uBAAa,GAAA,EAA2B;AAAA;AAAA,EAGzD,KAAA,GAAsB,MAAM,IAAA,CAAK,GAAA,EAAI;AAAA,EAErC,SAAS,KAAA,EAA4B;AACnC,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,KAAA,EAAM,GAAI,MAAM,GAAA,GAAM,GAAA;AAC7C,IAAA,IAAI,KAAA,CAAM,QAAQ,CAAA,EAAG;AACnB,MAAA,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,KAAA,CAAM,GAAG,CAAA;AAC5B,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,OAAO,GAAA,CAAI,KAAA,CAAM,KAAK,EAAE,KAAA,EAAO,WAAW,CAAA;AAAA,EACjD;AAAA,EAEA,SAAS,GAAA,EAAwC;AAC/C,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AACjC,IAAA,IAAI,KAAA,KAAU,QAAW,OAAO,MAAA;AAChC,IAAA,IAAI,IAAA,CAAK,KAAA,EAAM,GAAI,KAAA,CAAM,SAAA,EAAW;AAClC,MAAA,IAAA,CAAK,MAAA,CAAO,OAAO,GAAG,CAAA;AACtB,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAO,KAAA,CAAM,KAAA;AAAA,EACf;AAAA,EAEA,QAAQ,MAAA,EAA8C;AACpD,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,KAAK,MAAA,EAAQ;AACtC,MAAA,IAAI,IAAA,CAAK,KAAA,EAAM,GAAI,KAAA,CAAM,SAAA,EAAW;AAAE,QAAA,IAAA,CAAK,MAAA,CAAO,OAAO,GAAG,CAAA;AAAG,QAAA;AAAA,MAAU;AACzE,MAAA,IAAI,CAAC,oBAAA,CAAoB,mBAAA,CAAoB,GAAA,EAAK,MAAM,CAAA,EAAG;AAC3D,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,SAAA,CAAU,CAAC,CAAA;AACpC,MAAA,IAAI,SAAS,MAAA,EAAW;AACxB,MAAA,OAAO,EAAE,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,IAAA,EAAM,KAAK,IAAA,EAAM,GAAA,EAAK,KAAA,CAAM,KAAA,CAAM,GAAA,EAAI;AAAA,IAClE;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,MAAA,GAA0B;AACxB,IAAA,MAAM,GAAA,GAAS,KAAK,KAAA,EAAM;AAC1B,IAAA,MAAM,SAA0B,EAAC;AACjC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,KAAK,MAAA,EAAQ;AACtC,MAAA,IAAI,GAAA,GAAM,MAAM,SAAA,EAAW;AAAE,QAAA,IAAA,CAAK,MAAA,CAAO,OAAO,GAAG,CAAA;AAAG,QAAA;AAAA,MAAU;AAChE,MAAA,MAAA,CAAO,IAAA,CAAK,MAAM,KAAK,CAAA;AAAA,IACzB;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,OAAO,mBAAA,CAAoB,GAAA,EAAa,MAAA,EAAyB;AAG/D,IAAA,MAAM,QAAA,GAAW,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA;AAC9B,IAAA,IAAI,QAAA,CAAS,MAAA,GAAS,CAAA,IAAK,QAAA,CAAS,CAAC,CAAA,KAAM,KAAA,IAAS,QAAA,CAAS,CAAC,CAAA,KAAM,KAAA,IAAS,QAAA,CAAS,CAAC,MAAM,MAAA,EAAQ;AACnG,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,IAAI,CAAC,MAAA,CAAO,UAAA,CAAW,QAAQ,GAAG,OAAO,KAAA;AAEzC,IAAA,MAAM,YAAA,GAAe,SAAS,CAAC,CAAA;AAC/B,IAAA,MAAM,OAAA,GAAe,SAAS,CAAC,CAAA;AAC/B,IAAA,MAAM,IAAA,GAAe,MAAA,CAAO,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA;AACjD,IAAA,MAAM,QAAA,GAAe,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AACrC,IAAA,IAAI,QAAA,KAAa,IAAI,OAAO,KAAA;AAE5B,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAA;AAC3C,IAAA,MAAM,OAAA,GAAe,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,CAAC,CAAA;AAE5C,IAAA,IAAI,YAAA,KAAiB,cAAc,OAAO,KAAA;AAG1C,IAAA,IAAI,OAAA,KAAY,SAAS,OAAO,IAAA;AAChC,IAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,OAAA,GAAU,GAAG,GAAG,OAAO,IAAA;AAC9C,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AC1EQ,OAAA,CAAA,GAAA,CAAI,UAAA,GAAa,IAAI,CAAA,KAAM,MAAA,CAAe,YAAI,WAAA,CAAY,GAAG,CAAC,CAAC,CAAA;AAQhE,IAAM,iBAAA,GAAoB;AAAA,EAC/B,EAAA,EAAI,OAA0B,EAAE,OAAA,EAAS,IAAA,EAAK,CAAA;AAAA,EAC9C,IAAA,EAAM,CAAC,SAAA,EAAmB,OAAA,MAAwC,EAAE,OAAA,EAAS,KAAA,EAAO,WAAW,OAAA,EAAQ;AACzG;AAEO,IAAM,uBAAN,MAA2B;AAAA,EACf,KAAA,uBAAY,GAAA,EAAoB;AAAA;AAAA,EAEjD,iBAAA,CAAkB,KAAa,aAAA,EAA6B;AAC1D,IAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,aAAa,CAAA;AAAA,EACnC;AAAA,EAEA,gBAAgB,GAAA,EAAmB;AACjC,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,EACvB;AAAA,EAEA,IAAI,eAAA,GAA+C;AACjD,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EACd;AAAA,EAEA,SAAS,KAAA,EAAyC;AAChD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,MAAM,GAAG,CAAA;AACxC,IAAA,IAAI,YAAY,MAAA,EAAW;AACzB,MAAA,OAAO,kBAAkB,IAAA,CAAK,2BAAA,EAA6B,CAAA,kCAAA,EAAqC,KAAA,CAAM,GAAG,CAAA,CAAE,CAAA;AAAA,IAC7G;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAU,UAAA;AAChB,MAAA,MAAM,MAAA,GAAU,QAAQ,UAAA,CAAW,MAAM,IAAI,OAAA,CAAQ,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA,GAAI,OAAA;AAC5E,MAAA,MAAM,MAAA,GAAU,MAAA,CAAO,IAAA,CAAK,MAAA,EAAQ,KAAK,CAAA;AAEzC,MAAA,MAAM,MAAM,KAAA,CAAM,SAAA;AAClB,MAAA,IAAI,CAAC,GAAA,CAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AAC3B,QAAA,OAAO,iBAAA,CAAkB,IAAA,CAAK,0BAAA,EAA4B,sCAAsC,CAAA;AAAA,MAClG;AACA,MAAA,MAAM,QAAA,GAAW,OAAO,IAAA,CAAK,GAAA,CAAI,MAAM,MAAA,CAAO,MAAM,GAAG,QAAQ,CAAA;AAE/D,MAAA,MAAM,QAAA,GAAY,MAAM,YAAA,EAAa;AACrC,MAAA,MAAM,SAAA,GAAY,KAAK,SAAA,CAAU,QAAA,EAAU,OAAO,IAAA,CAAK,QAAQ,CAAA,CAAE,IAAA,EAAM,CAAA;AACvE,MAAA,MAAM,OAAA,GAAY,IAAI,WAAA,EAAY,CAAE,OAAO,SAAS,CAAA;AAEpD,MAAA,MAAM,KAAA,GAAgB,OAAA,CAAA,MAAA,CAAO,QAAA,EAAU,OAAA,EAAS,MAAM,CAAA;AACtD,MAAA,IAAI,CAAC,KAAA,EAAO,OAAO,iBAAA,CAAkB,IAAA,CAAK,4BAA4B,wCAAwC,CAAA;AAC9G,MAAA,OAAO,kBAAkB,EAAA,EAAG;AAAA,IAC9B,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,iBAAA,CAAkB,IAAA,CAAK,0BAAA,EAA4B,wCAAwC,CAAA;AAAA,IACpG;AAAA,EACF;AACF","file":"index.js","sourcesContent":["// Copyright 2026 INNO LOTUS PTY LTD\n// SPDX-License-Identifier: Apache-2.0\n\nimport { EncodingTier, FrameType } from \"../core/frames.js\";\nimport type { NpsFrame } from \"../core/codec.js\";\n\nexport interface NdpAddress {\n host: string;\n port: number;\n protocol: string;\n}\n\nexport interface NdpGraphNode {\n nid: string;\n addresses: readonly NdpAddress[];\n capabilities: readonly string[];\n nodeType?: string;\n}\n\nexport interface NdpResolveResult {\n host: string;\n port: number;\n ttl: number;\n certFingerprint?: string;\n}\n\nexport class AnnounceFrame implements NpsFrame {\n readonly frameType = FrameType.ANNOUNCE;\n readonly preferredTier = EncodingTier.MSGPACK;\n\n constructor(\n public readonly nid: string,\n public readonly addresses: readonly NdpAddress[],\n public readonly capabilities: readonly string[],\n public readonly ttl: number,\n public readonly timestamp: string,\n public readonly signature: string,\n public readonly nodeType?: string,\n ) {}\n\n unsignedDict(): Record<string, unknown> {\n return {\n nid: this.nid,\n addresses: this.addresses,\n capabilities: this.capabilities,\n ttl: this.ttl,\n timestamp: this.timestamp,\n node_type: this.nodeType ?? null,\n };\n }\n\n toDict(): Record<string, unknown> {\n return { ...this.unsignedDict(), signature: this.signature };\n }\n\n static fromDict(data: Record<string, unknown>): AnnounceFrame {\n return new AnnounceFrame(\n data[\"nid\"] as string,\n data[\"addresses\"] as NdpAddress[],\n data[\"capabilities\"] as string[],\n data[\"ttl\"] as number,\n data[\"timestamp\"] as string,\n data[\"signature\"] as string,\n (data[\"node_type\"] as string | null) ?? undefined,\n );\n }\n}\n\nexport class ResolveFrame implements NpsFrame {\n readonly frameType = FrameType.RESOLVE;\n readonly preferredTier = EncodingTier.MSGPACK;\n\n constructor(\n public readonly target: string,\n public readonly requesterNid?: string,\n public readonly resolved?: NdpResolveResult,\n ) {}\n\n toDict(): Record<string, unknown> {\n return {\n target: this.target,\n requester_nid: this.requesterNid ?? null,\n resolved: this.resolved ?? null,\n };\n }\n\n static fromDict(data: Record<string, unknown>): ResolveFrame {\n return new ResolveFrame(\n data[\"target\"] as string,\n (data[\"requester_nid\"] as string | null) ?? undefined,\n (data[\"resolved\"] as NdpResolveResult | null) ?? undefined,\n );\n }\n}\n\nexport class GraphFrame implements NpsFrame {\n readonly frameType = FrameType.GRAPH;\n readonly preferredTier = EncodingTier.MSGPACK;\n\n constructor(\n public readonly seq: number,\n public readonly initialSync: boolean,\n public readonly nodes?: readonly NdpGraphNode[],\n public readonly patch?: readonly Record<string, unknown>[],\n ) {}\n\n toDict(): Record<string, unknown> {\n return {\n seq: this.seq,\n initial_sync: this.initialSync,\n nodes: this.nodes ?? null,\n patch: this.patch ?? null,\n };\n }\n\n static fromDict(data: Record<string, unknown>): GraphFrame {\n return new GraphFrame(\n data[\"seq\"] as number,\n data[\"initial_sync\"] as boolean,\n (data[\"nodes\"] as NdpGraphNode[] | null) ?? undefined,\n (data[\"patch\"] as Record<string, unknown>[] | null) ?? undefined,\n );\n }\n}\n","// Copyright 2026 INNO LOTUS PTY LTD\n// SPDX-License-Identifier: Apache-2.0\n\nimport { FrameRegistry } from \"../core/registry.js\";\nimport { FrameType } from \"../core/frames.js\";\nimport { AnnounceFrame, GraphFrame, ResolveFrame } from \"./frames.js\";\n\nexport function registerNdpFrames(registry: FrameRegistry): void {\n registry.register(FrameType.ANNOUNCE, AnnounceFrame);\n registry.register(FrameType.RESOLVE, ResolveFrame);\n registry.register(FrameType.GRAPH, GraphFrame);\n}\n","// Copyright 2026 INNO LOTUS PTY LTD\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { AnnounceFrame, NdpResolveResult } from \"./frames.js\";\n\ninterface RegistryEntry {\n frame: AnnounceFrame;\n expiresAt: number;\n}\n\nexport class InMemoryNdpRegistry {\n private readonly _store = new Map<string, RegistryEntry>();\n\n // Replaceable for testing\n clock: () => number = () => Date.now();\n\n announce(frame: AnnounceFrame): void {\n const expiresAt = this.clock() + frame.ttl * 1000;\n if (frame.ttl === 0) {\n this._store.delete(frame.nid);\n return;\n }\n this._store.set(frame.nid, { frame, expiresAt });\n }\n\n getByNid(nid: string): AnnounceFrame | undefined {\n const entry = this._store.get(nid);\n if (entry === undefined) return undefined;\n if (this.clock() > entry.expiresAt) {\n this._store.delete(nid);\n return undefined;\n }\n return entry.frame;\n }\n\n resolve(target: string): NdpResolveResult | undefined {\n for (const [nid, entry] of this._store) {\n if (this.clock() > entry.expiresAt) { this._store.delete(nid); continue; }\n if (!InMemoryNdpRegistry.nwpTargetMatchesNid(nid, target)) continue;\n const addr = entry.frame.addresses[0];\n if (addr === undefined) continue;\n return { host: addr.host, port: addr.port, ttl: entry.frame.ttl };\n }\n return undefined;\n }\n\n getAll(): AnnounceFrame[] {\n const now = this.clock();\n const result: AnnounceFrame[] = [];\n for (const [nid, entry] of this._store) {\n if (now > entry.expiresAt) { this._store.delete(nid); continue; }\n result.push(entry.frame);\n }\n return result;\n }\n\n static nwpTargetMatchesNid(nid: string, target: string): boolean {\n // NID: urn:nps:node:{authority}:{path-segment}\n // target: nwp://{authority}/{path}\n const nidParts = nid.split(\":\");\n if (nidParts.length < 5 || nidParts[0] !== \"urn\" || nidParts[1] !== \"nps\" || nidParts[2] !== \"node\") {\n return false;\n }\n if (!target.startsWith(\"nwp://\")) return false;\n\n const nidAuthority = nidParts[3]!;\n const nidPath = nidParts[4]!;\n const rest = target.slice(\"nwp://\".length);\n const slashIdx = rest.indexOf(\"/\");\n if (slashIdx === -1) return false;\n\n const urlAuthority = rest.slice(0, slashIdx);\n const urlPath = rest.slice(slashIdx + 1); // without leading slash\n\n if (urlAuthority !== nidAuthority) return false;\n\n // nidPath must be a prefix of urlPath at a segment boundary\n if (urlPath === nidPath) return true;\n if (urlPath.startsWith(nidPath + \"/\")) return true;\n return false;\n }\n}\n","// Copyright 2026 INNO LOTUS PTY LTD\n// SPDX-License-Identifier: Apache-2.0\n\nimport * as ed25519 from \"@noble/ed25519\";\nimport { sha512 } from \"@noble/hashes/sha512\";\nimport type { AnnounceFrame } from \"./frames.js\";\n\ned25519.etc.sha512Sync = (...m) => sha512(ed25519.etc.concatBytes(...m));\n\nexport interface NdpAnnounceResult {\n isValid: boolean;\n errorCode?: string;\n message?: string;\n}\n\nexport const NdpAnnounceResult = {\n ok: (): NdpAnnounceResult => ({ isValid: true }),\n fail: (errorCode: string, message: string): NdpAnnounceResult => ({ isValid: false, errorCode, message }),\n};\n\nexport class NdpAnnounceValidator {\n private readonly _keys = new Map<string, string>(); // nid → \"ed25519:<hex>\"\n\n registerPublicKey(nid: string, encodedPubKey: string): void {\n this._keys.set(nid, encodedPubKey);\n }\n\n removePublicKey(nid: string): void {\n this._keys.delete(nid);\n }\n\n get knownPublicKeys(): ReadonlyMap<string, string> {\n return this._keys;\n }\n\n validate(frame: AnnounceFrame): NdpAnnounceResult {\n const encoded = this._keys.get(frame.nid);\n if (encoded === undefined) {\n return NdpAnnounceResult.fail(\"NDP-ANNOUNCE-NID-MISMATCH\", `No public key registered for NID: ${frame.nid}`);\n }\n\n try {\n const prefix = \"ed25519:\";\n const pubHex = encoded.startsWith(prefix) ? encoded.slice(prefix.length) : encoded;\n const pubKey = Buffer.from(pubHex, \"hex\");\n\n const sig = frame.signature;\n if (!sig.startsWith(prefix)) {\n return NdpAnnounceResult.fail(\"NDP-ANNOUNCE-SIG-INVALID\", \"Signature must start with 'ed25519:'\");\n }\n const sigBytes = Buffer.from(sig.slice(prefix.length), \"base64\");\n\n const unsigned = frame.unsignedDict();\n const canonical = JSON.stringify(unsigned, Object.keys(unsigned).sort());\n const message = new TextEncoder().encode(canonical);\n\n const valid = ed25519.verify(sigBytes, message, pubKey);\n if (!valid) return NdpAnnounceResult.fail(\"NDP-ANNOUNCE-SIG-INVALID\", \"Ed25519 signature verification failed.\");\n return NdpAnnounceResult.ok();\n } catch {\n return NdpAnnounceResult.fail(\"NDP-ANNOUNCE-SIG-INVALID\", \"Ed25519 signature verification failed.\");\n }\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ndp/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,sCAAsC;AAEtC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import type { AnnounceFrame, NdpResolveResult } from "./frames.js";
2
+ import { type DnsTxtLookup } from "./dns-txt.js";
2
3
  export declare class InMemoryNdpRegistry {
3
4
  private readonly _store;
4
5
  clock: () => number;
@@ -6,6 +7,7 @@ export declare class InMemoryNdpRegistry {
6
7
  getByNid(nid: string): AnnounceFrame | undefined;
7
8
  resolve(target: string): NdpResolveResult | undefined;
8
9
  getAll(): AnnounceFrame[];
10
+ resolveWithDns(target: string, resolver?: DnsTxtLookup): Promise<NdpResolveResult | undefined>;
9
11
  static nwpTargetMatchesNid(nid: string, target: string): boolean;
10
12
  }
11
13
  //# sourceMappingURL=ndp-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ndp-registry.d.ts","sourceRoot":"","sources":["../../src/ndp/ndp-registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAOnE,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAG3D,KAAK,EAAE,MAAM,MAAM,CAAoB;IAEvC,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IASpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAUhD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAWrD,MAAM,IAAI,aAAa,EAAE;IAUzB,MAAM,CAAC,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;CAyBjE"}
1
+ {"version":3,"file":"ndp-registry.d.ts","sourceRoot":"","sources":["../../src/ndp/ndp-registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAOtB,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAG3D,KAAK,EAAE,MAAM,MAAM,CAAoB;IAEvC,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IASpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAUhD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAWrD,MAAM,IAAI,aAAa,EAAE;IAUnB,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,YAAuC,GAChD,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAyBxC,MAAM,CAAC,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;CAyBjE"}
@@ -0,0 +1,104 @@
1
+ // Copyright 2026 INNO LOTUS PTY LTD
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { extractHostFromTarget, parseNpsTxtRecord, SystemDnsTxtLookup, } from "./dns-txt.js";
4
+ export class InMemoryNdpRegistry {
5
+ _store = new Map();
6
+ // Replaceable for testing
7
+ clock = () => Date.now();
8
+ announce(frame) {
9
+ const expiresAt = this.clock() + frame.ttl * 1000;
10
+ if (frame.ttl === 0) {
11
+ this._store.delete(frame.nid);
12
+ return;
13
+ }
14
+ this._store.set(frame.nid, { frame, expiresAt });
15
+ }
16
+ getByNid(nid) {
17
+ const entry = this._store.get(nid);
18
+ if (entry === undefined)
19
+ return undefined;
20
+ if (this.clock() > entry.expiresAt) {
21
+ this._store.delete(nid);
22
+ return undefined;
23
+ }
24
+ return entry.frame;
25
+ }
26
+ resolve(target) {
27
+ for (const [nid, entry] of this._store) {
28
+ if (this.clock() > entry.expiresAt) {
29
+ this._store.delete(nid);
30
+ continue;
31
+ }
32
+ if (!InMemoryNdpRegistry.nwpTargetMatchesNid(nid, target))
33
+ continue;
34
+ const addr = entry.frame.addresses[0];
35
+ if (addr === undefined)
36
+ continue;
37
+ return { host: addr.host, port: addr.port, ttl: entry.frame.ttl };
38
+ }
39
+ return undefined;
40
+ }
41
+ getAll() {
42
+ const now = this.clock();
43
+ const result = [];
44
+ for (const [nid, entry] of this._store) {
45
+ if (now > entry.expiresAt) {
46
+ this._store.delete(nid);
47
+ continue;
48
+ }
49
+ result.push(entry.frame);
50
+ }
51
+ return result;
52
+ }
53
+ async resolveWithDns(target, resolver = new SystemDnsTxtLookup()) {
54
+ // 1. Try in-memory registry first
55
+ const cached = this.resolve(target);
56
+ if (cached !== undefined)
57
+ return cached;
58
+ // 2. Extract hostname and fall back to DNS TXT lookup
59
+ const host = extractHostFromTarget(target);
60
+ if (host === undefined)
61
+ return undefined;
62
+ const txtHost = `_nps-node.${host}`;
63
+ let records;
64
+ try {
65
+ records = await resolver.resolveTxt(txtHost);
66
+ }
67
+ catch {
68
+ return undefined;
69
+ }
70
+ for (const record of records) {
71
+ const result = parseNpsTxtRecord(record, host);
72
+ if (result !== undefined)
73
+ return result;
74
+ }
75
+ return undefined;
76
+ }
77
+ static nwpTargetMatchesNid(nid, target) {
78
+ // NID: urn:nps:node:{authority}:{path-segment}
79
+ // target: nwp://{authority}/{path}
80
+ const nidParts = nid.split(":");
81
+ if (nidParts.length < 5 || nidParts[0] !== "urn" || nidParts[1] !== "nps" || nidParts[2] !== "node") {
82
+ return false;
83
+ }
84
+ if (!target.startsWith("nwp://"))
85
+ return false;
86
+ const nidAuthority = nidParts[3];
87
+ const nidPath = nidParts[4];
88
+ const rest = target.slice("nwp://".length);
89
+ const slashIdx = rest.indexOf("/");
90
+ if (slashIdx === -1)
91
+ return false;
92
+ const urlAuthority = rest.slice(0, slashIdx);
93
+ const urlPath = rest.slice(slashIdx + 1); // without leading slash
94
+ if (urlAuthority !== nidAuthority)
95
+ return false;
96
+ // nidPath must be a prefix of urlPath at a segment boundary
97
+ if (urlPath === nidPath)
98
+ return true;
99
+ if (urlPath.startsWith(nidPath + "/"))
100
+ return true;
101
+ return false;
102
+ }
103
+ }
104
+ //# sourceMappingURL=ndp-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ndp-registry.js","sourceRoot":"","sources":["../../src/ndp/ndp-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ // Copyright 2026 INNO LOTUS PTY LTD
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { FrameType } from "../core/frames.js";
4
+ import { AnnounceFrame, GraphFrame, ResolveFrame } from "./frames.js";
5
+ export function registerNdpFrames(registry) {
6
+ registry.register(FrameType.ANNOUNCE, AnnounceFrame);
7
+ registry.register(FrameType.RESOLVE, ResolveFrame);
8
+ registry.register(FrameType.GRAPH, GraphFrame);
9
+ }
10
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/ndp/registry.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,sCAAsC;AAGtC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEtE,MAAM,UAAU,iBAAiB,CAAC,QAAuB;IACvD,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IACrD,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAG,YAAY,CAAC,CAAC;IACpD,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAK,UAAU,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,48 @@
1
+ // Copyright 2026 INNO LOTUS PTY LTD
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import * as ed25519 from "@noble/ed25519";
4
+ import { sha512 } from "@noble/hashes/sha512";
5
+ ed25519.etc.sha512Sync = (...m) => sha512(ed25519.etc.concatBytes(...m));
6
+ export const NdpAnnounceResult = {
7
+ ok: () => ({ isValid: true }),
8
+ fail: (errorCode, message) => ({ isValid: false, errorCode, message }),
9
+ };
10
+ export class NdpAnnounceValidator {
11
+ _keys = new Map(); // nid → "ed25519:<hex>"
12
+ registerPublicKey(nid, encodedPubKey) {
13
+ this._keys.set(nid, encodedPubKey);
14
+ }
15
+ removePublicKey(nid) {
16
+ this._keys.delete(nid);
17
+ }
18
+ get knownPublicKeys() {
19
+ return this._keys;
20
+ }
21
+ validate(frame) {
22
+ const encoded = this._keys.get(frame.nid);
23
+ if (encoded === undefined) {
24
+ return NdpAnnounceResult.fail("NDP-ANNOUNCE-NID-MISMATCH", `No public key registered for NID: ${frame.nid}`);
25
+ }
26
+ try {
27
+ const prefix = "ed25519:";
28
+ const pubHex = encoded.startsWith(prefix) ? encoded.slice(prefix.length) : encoded;
29
+ const pubKey = Buffer.from(pubHex, "hex");
30
+ const sig = frame.signature;
31
+ if (!sig.startsWith(prefix)) {
32
+ return NdpAnnounceResult.fail("NDP-ANNOUNCE-SIG-INVALID", "Signature must start with 'ed25519:'");
33
+ }
34
+ const sigBytes = Buffer.from(sig.slice(prefix.length), "base64");
35
+ const unsigned = frame.unsignedDict();
36
+ const canonical = JSON.stringify(unsigned, Object.keys(unsigned).sort());
37
+ const message = new TextEncoder().encode(canonical);
38
+ const valid = ed25519.verify(sigBytes, message, pubKey);
39
+ if (!valid)
40
+ return NdpAnnounceResult.fail("NDP-ANNOUNCE-SIG-INVALID", "Ed25519 signature verification failed.");
41
+ return NdpAnnounceResult.ok();
42
+ }
43
+ catch {
44
+ return NdpAnnounceResult.fail("NDP-ANNOUNCE-SIG-INVALID", "Ed25519 signature verification failed.");
45
+ }
46
+ }
47
+ }
48
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/ndp/validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export interface AcmeClientOptions {
2
+ /** ACME directory URL. */
3
+ directoryUrl: string;
4
+ /** Account/agent Ed25519 private key (32-byte raw). */
5
+ privateKey: Uint8Array;
6
+ /** Account/agent Ed25519 public key (32-byte raw). */
7
+ publicKey: Uint8Array;
8
+ /** Web Crypto Ed25519 keypair for CSR signing (must match privateKey). */
9
+ webCryptoKeys: CryptoKeyPair;
10
+ }
11
+ export declare class AcmeClient {
12
+ readonly options: AcmeClientOptions;
13
+ private directory;
14
+ private accountUrl;
15
+ private lastNonce;
16
+ constructor(options: AcmeClientOptions);
17
+ /** Drive the full agent-01 flow for `nid`. Returns issued PEM cert chain. */
18
+ issueAgentCert(nid: string): Promise<string>;
19
+ private ensureDirectory;
20
+ private refreshNonce;
21
+ private newAccount;
22
+ private newOrder;
23
+ private fetchAuthz;
24
+ private respondAgent01;
25
+ private finalizeOrder;
26
+ private downloadPem;
27
+ private post;
28
+ private captureNonce;
29
+ private buildCsr;
30
+ }
31
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/nip/acme/client.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,iBAAiB;IAChC,0BAA0B;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,UAAU,EAAI,UAAU,CAAC;IACzB,sDAAsD;IACtD,SAAS,EAAK,UAAU,CAAC;IACzB,0EAA0E;IAC1E,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,qBAAa,UAAU;aAKO,OAAO,EAAE,iBAAiB;IAJtD,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,SAAS,CAA2B;gBAEhB,OAAO,EAAE,iBAAiB;IAEtD,6EAA6E;IACvE,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAYpC,eAAe;YAQf,YAAY;YASZ,UAAU;YAcV,QAAQ;YAcR,UAAU;YAYV,cAAc;YAiBd,aAAa;YAYb,WAAW;YAaX,IAAI;IAQlB,OAAO,CAAC,YAAY;YAKN,QAAQ;CAWvB"}
@@ -0,0 +1,136 @@
1
+ // Copyright 2026 INNO LOTUS PTY LTD
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * ACME client implementing the `agent-01` challenge type per NPS-RFC-0002 §4.4.
5
+ *
6
+ * Flow: newNonce → newAccount → newOrder → fetch authz → sign challenge token →
7
+ * finalize with CSR → fetch leaf cert.
8
+ */
9
+ import * as ed25519 from "@noble/ed25519";
10
+ import { sha512 } from "@noble/hashes/sha512";
11
+ import * as x509 from "@peculiar/x509";
12
+ import * as Jws from "./jws.js";
13
+ import * as wire from "./wire.js";
14
+ ed25519.etc.sha512Sync = (...m) => sha512(ed25519.etc.concatBytes(...m));
15
+ x509.cryptoProvider.set(globalThis.crypto);
16
+ export class AcmeClient {
17
+ options;
18
+ directory = null;
19
+ accountUrl = null;
20
+ lastNonce = null;
21
+ constructor(options) {
22
+ this.options = options;
23
+ }
24
+ /** Drive the full agent-01 flow for `nid`. Returns issued PEM cert chain. */
25
+ async issueAgentCert(nid) {
26
+ await this.ensureDirectory();
27
+ if (this.accountUrl === null)
28
+ await this.newAccount();
29
+ const order = await this.newOrder(nid);
30
+ const authz = await this.fetchAuthz(order.authorizations[0]);
31
+ await this.respondAgent01(authz);
32
+ const finalized = await this.finalizeOrder(order, nid);
33
+ return this.downloadPem(finalized.certificate);
34
+ }
35
+ // ── Stages ───────────────────────────────────────────────────────────────
36
+ async ensureDirectory() {
37
+ if (this.directory !== null)
38
+ return;
39
+ const resp = await fetch(this.options.directoryUrl);
40
+ ensureSuccess(resp);
41
+ this.directory = await resp.json();
42
+ await this.refreshNonce();
43
+ }
44
+ async refreshNonce() {
45
+ const resp = await fetch(this.directory.newNonce, { method: "HEAD" });
46
+ ensureSuccess(resp);
47
+ this.lastNonce = resp.headers.get("Replay-Nonce");
48
+ if (this.lastNonce === null) {
49
+ throw new Error("server omitted Replay-Nonce");
50
+ }
51
+ }
52
+ async newAccount() {
53
+ const jwk = Jws.jwkFromPublicKey(this.options.publicKey);
54
+ const env = Jws.sign({ alg: Jws.ALG_EDDSA, nonce: this.lastNonce, url: this.directory.newAccount, jwk }, { termsOfServiceAgreed: true }, this.options.privateKey);
55
+ const resp = await this.post(this.directory.newAccount, env);
56
+ ensureSuccess(resp);
57
+ this.accountUrl = resp.headers.get("Location");
58
+ if (this.accountUrl === null)
59
+ throw new Error("server omitted account Location");
60
+ this.captureNonce(resp);
61
+ }
62
+ async newOrder(nid) {
63
+ const env = Jws.sign({ alg: Jws.ALG_EDDSA, nonce: this.lastNonce, url: this.directory.newOrder, kid: this.accountUrl }, {
64
+ identifiers: [{ type: wire.IDENTIFIER_TYPE_NID, value: nid }],
65
+ }, this.options.privateKey);
66
+ const resp = await this.post(this.directory.newOrder, env);
67
+ ensureSuccess(resp);
68
+ this.captureNonce(resp);
69
+ return await resp.json();
70
+ }
71
+ async fetchAuthz(url) {
72
+ // POST-as-GET (RFC 8555 §6.3).
73
+ const env = Jws.sign({ alg: Jws.ALG_EDDSA, nonce: this.lastNonce, url, kid: this.accountUrl }, null, this.options.privateKey);
74
+ const resp = await this.post(url, env);
75
+ ensureSuccess(resp);
76
+ this.captureNonce(resp);
77
+ return await resp.json();
78
+ }
79
+ async respondAgent01(authz) {
80
+ const challenge = authz.challenges.find((c) => c.type === wire.CHALLENGE_AGENT_01);
81
+ if (!challenge)
82
+ throw new Error("authz has no agent-01 challenge");
83
+ // Sign the challenge token with the account/NID private key.
84
+ const tokenBytes = new TextEncoder().encode(challenge.token);
85
+ const sig = ed25519.sign(tokenBytes, this.options.privateKey);
86
+ const env = Jws.sign({ alg: Jws.ALG_EDDSA, nonce: this.lastNonce, url: challenge.url, kid: this.accountUrl }, { agent_signature: Jws.b64uEncode(sig) }, this.options.privateKey);
87
+ const resp = await this.post(challenge.url, env);
88
+ ensureSuccess(resp);
89
+ this.captureNonce(resp);
90
+ }
91
+ async finalizeOrder(order, nid) {
92
+ const csrDer = await this.buildCsr(nid);
93
+ const env = Jws.sign({ alg: Jws.ALG_EDDSA, nonce: this.lastNonce, url: order.finalize, kid: this.accountUrl }, { csr: Jws.b64uEncode(csrDer) }, this.options.privateKey);
94
+ const resp = await this.post(order.finalize, env);
95
+ ensureSuccess(resp);
96
+ this.captureNonce(resp);
97
+ return await resp.json();
98
+ }
99
+ async downloadPem(certUrl) {
100
+ const env = Jws.sign({ alg: Jws.ALG_EDDSA, nonce: this.lastNonce, url: certUrl, kid: this.accountUrl }, null, this.options.privateKey);
101
+ const resp = await this.post(certUrl, env);
102
+ ensureSuccess(resp);
103
+ this.captureNonce(resp);
104
+ return await resp.text();
105
+ }
106
+ // ── helpers ──────────────────────────────────────────────────────────────
107
+ async post(url, env) {
108
+ return await fetch(url, {
109
+ method: "POST",
110
+ headers: { "Content-Type": wire.CONTENT_TYPE_JOSE_JSON },
111
+ body: JSON.stringify(env),
112
+ });
113
+ }
114
+ captureNonce(resp) {
115
+ const nonce = resp.headers.get("Replay-Nonce");
116
+ if (nonce !== null)
117
+ this.lastNonce = nonce;
118
+ }
119
+ async buildCsr(nid) {
120
+ const csr = await x509.Pkcs10CertificateRequestGenerator.create({
121
+ name: `CN=${nid.replace(/([",+;<>\\])/g, "\\$1")}`,
122
+ keys: this.options.webCryptoKeys,
123
+ signingAlgorithm: { name: "Ed25519" },
124
+ extensions: [
125
+ new x509.SubjectAlternativeNameExtension([{ type: "url", value: nid }], false),
126
+ ],
127
+ });
128
+ return new Uint8Array(csr.rawData);
129
+ }
130
+ }
131
+ function ensureSuccess(resp) {
132
+ if (!resp.ok) {
133
+ throw new Error(`ACME ${resp.url} HTTP ${resp.status}`);
134
+ }
135
+ }
136
+ //# sourceMappingURL=client.js.map