@labacacia/nps-sdk 1.0.0-alpha.4 → 1.0.0-alpha.6
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.
- package/CHANGELOG.cn.md +2 -73
- package/CHANGELOG.md +2 -82
- package/CONTRIBUTING.cn.md +0 -0
- package/CONTRIBUTING.md +0 -0
- package/LICENSE +0 -0
- package/NOTICE +0 -0
- package/README.cn.md +3 -3
- package/README.md +3 -3
- package/doc/nps-sdk.core.cn.md +0 -0
- package/doc/nps-sdk.core.md +0 -0
- package/doc/nps-sdk.ncp.cn.md +0 -0
- package/doc/nps-sdk.ncp.md +0 -0
- package/doc/nps-sdk.ndp.cn.md +0 -0
- package/doc/nps-sdk.ndp.md +0 -0
- package/doc/nps-sdk.nip.cn.md +30 -0
- package/doc/nps-sdk.nip.md +30 -0
- package/doc/nps-sdk.nop.cn.md +0 -0
- package/doc/nps-sdk.nop.md +0 -0
- package/doc/nps-sdk.nwp.cn.md +71 -0
- package/doc/nps-sdk.nwp.md +71 -0
- package/doc/overview.cn.md +0 -0
- package/doc/overview.md +0 -0
- package/nip-ca-server/Dockerfile +0 -0
- package/nip-ca-server/README.md +1 -1
- package/nip-ca-server/db/001_init.sql +0 -0
- package/nip-ca-server/docker-compose.yml +0 -0
- package/nip-ca-server/package.json +0 -0
- package/nip-ca-server/src/ca.ts +0 -0
- package/nip-ca-server/src/db.ts +0 -0
- package/nip-ca-server/src/index.ts +0 -0
- package/nip-ca-server/tsconfig.json +0 -0
- package/package.json +1 -1
- package/src/core/anchor-cache.ts +0 -0
- package/src/core/cache.ts +0 -0
- package/src/core/canonical-json.ts +0 -0
- package/src/core/codec.ts +0 -0
- package/src/core/codecs/index.ts +0 -0
- package/src/core/codecs/ncp-codec.ts +0 -0
- package/src/core/codecs/tier1-json-codec.ts +0 -0
- package/src/core/codecs/tier2-msgpack-codec.ts +0 -0
- package/src/core/crypto-provider.ts +0 -0
- package/src/core/exceptions.ts +0 -0
- package/src/core/frame-header.ts +0 -0
- package/src/core/frame-registry.ts +0 -0
- package/src/core/frames.ts +0 -0
- package/src/core/index.ts +0 -0
- package/src/core/registry.ts +0 -0
- package/src/core/status-codes.ts +1 -0
- package/src/ncp/frames/anchor-frame.ts +0 -0
- package/src/ncp/frames/caps-frame.ts +0 -0
- package/src/ncp/frames/diff-frame.ts +0 -0
- package/src/ncp/frames/error-frame.ts +0 -0
- package/src/ncp/frames/hello-frame.ts +0 -0
- package/src/ncp/frames/stream-frame.ts +0 -0
- package/src/ncp/frames.ts +0 -0
- package/src/ncp/handshake.ts +0 -0
- package/src/ncp/ncp-patch-format.ts +0 -0
- package/src/ncp/registry.ts +0 -0
- package/src/ncp/stream-manager.ts +0 -0
- package/src/ndp/dns-txt.ts +86 -0
- package/src/ndp/frames.ts +0 -0
- package/src/ndp/index.ts +1 -0
- package/src/ndp/ndp-registry.ts +34 -0
- package/src/ndp/registry.ts +0 -0
- package/src/ndp/validator.ts +0 -0
- package/src/nip/assurance-level.ts +6 -1
- package/src/nip/error-codes.ts +4 -2
- package/src/nip/identity.ts +0 -0
- package/src/nip/registry.ts +0 -0
- package/src/nip/x509/oids.ts +3 -4
- package/src/nop/client.ts +1 -1
- package/src/nop/frames.ts +0 -0
- package/src/nop/index.ts +0 -0
- package/src/nop/models.ts +0 -0
- package/src/nop/nop-types.ts +0 -0
- package/src/nop/registry.ts +0 -0
- package/src/nwp/client.ts +4 -4
- package/src/nwp/frames.ts +0 -0
- package/src/nwp/index.ts +0 -0
- package/src/nwp/registry.ts +0 -0
- package/src/setup.ts +0 -0
- package/tests/core/anchor-cache.test.ts +0 -0
- package/tests/core/codec.test.ts +0 -0
- package/tests/core/frame-registry.test.ts +0 -0
- package/tests/core.test.ts +0 -0
- package/tests/ncp/diff-binary-bitset.test.ts +0 -0
- package/tests/ncp/e2e-enc-reject.test.ts +0 -0
- package/tests/ncp/err-error-frame.test.ts +0 -0
- package/tests/ncp/frames.test.ts +0 -0
- package/tests/ncp/framing.test.ts +0 -0
- package/tests/ncp/hello-frame.test.ts +0 -0
- package/tests/ncp/inline-anchor.test.ts +0 -0
- package/tests/ncp/security.test.ts +0 -0
- package/tests/ncp/stream-window.test.ts +0 -0
- package/tests/ncp/stream.test.ts +0 -0
- package/tests/ncp/version-negotiation.test.ts +0 -0
- package/tests/ndp.test.ts +106 -0
- package/tests/nip.test.ts +0 -0
- package/tests/nop.test.ts +0 -0
- package/tests/nwp.test.ts +0 -0
- package/tsconfig.json +0 -0
- package/tsup.config.ts +0 -0
- package/vitest.config.ts +0 -0
- package/dist/core/anchor-cache.d.ts +0 -42
- package/dist/core/anchor-cache.d.ts.map +0 -1
- package/dist/core/anchor-cache.js +0 -104
- package/dist/core/anchor-cache.js.map +0 -1
- package/dist/core/cache.d.ts +0 -14
- package/dist/core/cache.d.ts.map +0 -1
- package/dist/core/cache.js +0 -80
- package/dist/core/cache.js.map +0 -1
- package/dist/core/canonical-json.d.ts +0 -12
- package/dist/core/canonical-json.d.ts.map +0 -1
- package/dist/core/canonical-json.js +0 -44
- package/dist/core/canonical-json.js.map +0 -1
- package/dist/core/codec.d.ts +0 -32
- package/dist/core/codec.d.ts.map +0 -1
- package/dist/core/codec.js +0 -119
- package/dist/core/codec.js.map +0 -1
- package/dist/core/codecs/index.d.ts +0 -4
- package/dist/core/codecs/index.d.ts.map +0 -1
- package/dist/core/codecs/index.js +0 -6
- package/dist/core/codecs/index.js.map +0 -1
- package/dist/core/codecs/ncp-codec.d.ts +0 -39
- package/dist/core/codecs/ncp-codec.d.ts.map +0 -1
- package/dist/core/codecs/ncp-codec.js +0 -93
- package/dist/core/codecs/ncp-codec.js.map +0 -1
- package/dist/core/codecs/tier1-json-codec.d.ts +0 -10
- package/dist/core/codecs/tier1-json-codec.d.ts.map +0 -1
- package/dist/core/codecs/tier1-json-codec.js +0 -28
- package/dist/core/codecs/tier1-json-codec.js.map +0 -1
- package/dist/core/codecs/tier2-msgpack-codec.d.ts +0 -10
- package/dist/core/codecs/tier2-msgpack-codec.d.ts.map +0 -1
- package/dist/core/codecs/tier2-msgpack-codec.js +0 -26
- package/dist/core/codecs/tier2-msgpack-codec.js.map +0 -1
- package/dist/core/crypto-provider.d.ts +0 -31
- package/dist/core/crypto-provider.d.ts.map +0 -1
- package/dist/core/crypto-provider.js +0 -10
- package/dist/core/crypto-provider.js.map +0 -1
- package/dist/core/exceptions.d.ts +0 -27
- package/dist/core/exceptions.d.ts.map +0 -1
- package/dist/core/exceptions.js +0 -52
- package/dist/core/exceptions.js.map +0 -1
- package/dist/core/frame-header.d.ts +0 -87
- package/dist/core/frame-header.d.ts.map +0 -1
- package/dist/core/frame-header.js +0 -185
- package/dist/core/frame-header.js.map +0 -1
- package/dist/core/frame-registry.d.ts +0 -35
- package/dist/core/frame-registry.d.ts.map +0 -1
- package/dist/core/frame-registry.js +0 -63
- package/dist/core/frame-registry.js.map +0 -1
- package/dist/core/frames.d.ts +0 -81
- package/dist/core/frames.d.ts.map +0 -1
- package/dist/core/frames.js +0 -154
- package/dist/core/frames.js.map +0 -1
- package/dist/core/index.d.ts +0 -11
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -22
- package/dist/core/index.js.map +0 -1
- package/dist/core/registry.d.ts +0 -11
- package/dist/core/registry.d.ts.map +0 -1
- package/dist/core/registry.js +0 -17
- package/dist/core/registry.js.map +0 -1
- package/dist/core/status-codes.d.ts +0 -28
- package/dist/core/status-codes.d.ts.map +0 -1
- package/dist/core/status-codes.js +0 -38
- package/dist/core/status-codes.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -10
- package/dist/index.js.map +0 -1
- package/dist/ncp/frames/anchor-frame.d.ts +0 -29
- package/dist/ncp/frames/anchor-frame.d.ts.map +0 -1
- package/dist/ncp/frames/anchor-frame.js +0 -54
- package/dist/ncp/frames/anchor-frame.js.map +0 -1
- package/dist/ncp/frames/caps-frame.d.ts +0 -29
- package/dist/ncp/frames/caps-frame.d.ts.map +0 -1
- package/dist/ncp/frames/caps-frame.js +0 -29
- package/dist/ncp/frames/caps-frame.js.map +0 -1
- package/dist/ncp/frames/diff-frame.d.ts +0 -32
- package/dist/ncp/frames/diff-frame.d.ts.map +0 -1
- package/dist/ncp/frames/diff-frame.js +0 -37
- package/dist/ncp/frames/diff-frame.js.map +0 -1
- package/dist/ncp/frames/error-frame.d.ts +0 -16
- package/dist/ncp/frames/error-frame.d.ts.map +0 -1
- package/dist/ncp/frames/error-frame.js +0 -13
- package/dist/ncp/frames/error-frame.js.map +0 -1
- package/dist/ncp/frames/hello-frame.d.ts +0 -21
- package/dist/ncp/frames/hello-frame.d.ts.map +0 -1
- package/dist/ncp/frames/hello-frame.js +0 -25
- package/dist/ncp/frames/hello-frame.js.map +0 -1
- package/dist/ncp/frames/stream-frame.d.ts +0 -16
- package/dist/ncp/frames/stream-frame.d.ts.map +0 -1
- package/dist/ncp/frames/stream-frame.js +0 -18
- package/dist/ncp/frames/stream-frame.js.map +0 -1
- package/dist/ncp/frames.d.ts +0 -94
- package/dist/ncp/frames.d.ts.map +0 -1
- package/dist/ncp/frames.js +0 -192
- package/dist/ncp/frames.js.map +0 -1
- package/dist/ncp/handshake.d.ts +0 -30
- package/dist/ncp/handshake.d.ts.map +0 -1
- package/dist/ncp/handshake.js +0 -80
- package/dist/ncp/handshake.js.map +0 -1
- package/dist/ncp/index.d.ts +0 -12
- package/dist/ncp/index.d.ts.map +0 -1
- package/dist/ncp/index.js +0 -14
- package/dist/ncp/index.js.map +0 -1
- package/dist/ncp/ncp-error-codes.d.ts +0 -23
- package/dist/ncp/ncp-error-codes.d.ts.map +0 -1
- package/dist/ncp/ncp-error-codes.js +0 -34
- package/dist/ncp/ncp-error-codes.js.map +0 -1
- package/dist/ncp/ncp-patch-format.d.ts +0 -7
- package/dist/ncp/ncp-patch-format.d.ts.map +0 -1
- package/dist/ncp/ncp-patch-format.js +0 -13
- package/dist/ncp/ncp-patch-format.js.map +0 -1
- package/dist/ncp/preamble.d.ts +0 -47
- package/dist/ncp/preamble.d.ts.map +0 -1
- package/dist/ncp/preamble.js +0 -74
- package/dist/ncp/preamble.js.map +0 -1
- package/dist/ncp/registry.d.ts +0 -3
- package/dist/ncp/registry.d.ts.map +0 -1
- package/dist/ncp/registry.js +0 -13
- package/dist/ncp/registry.js.map +0 -1
- package/dist/ncp/stream-manager.d.ts +0 -57
- package/dist/ncp/stream-manager.d.ts.map +0 -1
- package/dist/ncp/stream-manager.js +0 -163
- package/dist/ncp/stream-manager.js.map +0 -1
- package/dist/ndp/frames.d.ts +0 -56
- package/dist/ndp/frames.d.ts.map +0 -1
- package/dist/ndp/frames.js +0 -87
- package/dist/ndp/frames.js.map +0 -1
- package/dist/ndp/index.d.ts +0 -5
- package/dist/ndp/index.d.ts.map +0 -1
- package/dist/ndp/index.js +0 -7
- package/dist/ndp/index.js.map +0 -1
- package/dist/ndp/ndp-registry.d.ts +0 -11
- package/dist/ndp/ndp-registry.d.ts.map +0 -1
- package/dist/ndp/ndp-registry.js +0 -79
- package/dist/ndp/ndp-registry.js.map +0 -1
- package/dist/ndp/registry.d.ts +0 -3
- package/dist/ndp/registry.d.ts.map +0 -1
- package/dist/ndp/registry.js +0 -10
- package/dist/ndp/registry.js.map +0 -1
- package/dist/ndp/validator.d.ts +0 -18
- package/dist/ndp/validator.d.ts.map +0 -1
- package/dist/ndp/validator.js +0 -48
- package/dist/ndp/validator.js.map +0 -1
- package/dist/nip/acme/client.d.ts +0 -31
- package/dist/nip/acme/client.d.ts.map +0 -1
- package/dist/nip/acme/client.js +0 -136
- package/dist/nip/acme/client.js.map +0 -1
- package/dist/nip/acme/index.d.ts +0 -6
- package/dist/nip/acme/index.d.ts.map +0 -1
- package/dist/nip/acme/index.js +0 -8
- package/dist/nip/acme/index.js.map +0 -1
- package/dist/nip/acme/jws.d.ts +0 -31
- package/dist/nip/acme/jws.d.ts.map +0 -1
- package/dist/nip/acme/jws.js +0 -76
- package/dist/nip/acme/jws.js.map +0 -1
- package/dist/nip/acme/messages.d.ts +0 -71
- package/dist/nip/acme/messages.d.ts.map +0 -1
- package/dist/nip/acme/messages.js +0 -4
- package/dist/nip/acme/messages.js.map +0 -1
- package/dist/nip/acme/server.d.ts +0 -41
- package/dist/nip/acme/server.d.ts.map +0 -1
- package/dist/nip/acme/server.js +0 -458
- package/dist/nip/acme/server.js.map +0 -1
- package/dist/nip/acme/wire.d.ts +0 -19
- package/dist/nip/acme/wire.d.ts.map +0 -1
- package/dist/nip/acme/wire.js +0 -21
- package/dist/nip/acme/wire.js.map +0 -1
- package/dist/nip/assurance-level.d.ts +0 -14
- package/dist/nip/assurance-level.d.ts.map +0 -1
- package/dist/nip/assurance-level.js +0 -33
- package/dist/nip/assurance-level.js.map +0 -1
- package/dist/nip/cert-format.d.ts +0 -5
- package/dist/nip/cert-format.d.ts.map +0 -1
- package/dist/nip/cert-format.js +0 -6
- package/dist/nip/cert-format.js.map +0 -1
- package/dist/nip/error-codes.d.ts +0 -23
- package/dist/nip/error-codes.d.ts.map +0 -1
- package/dist/nip/error-codes.js +0 -30
- package/dist/nip/error-codes.js.map +0 -1
- package/dist/nip/frames.d.ts +0 -53
- package/dist/nip/frames.d.ts.map +0 -1
- package/dist/nip/frames.js +0 -106
- package/dist/nip/frames.js.map +0 -1
- package/dist/nip/identity.d.ts +0 -18
- package/dist/nip/identity.d.ts.map +0 -1
- package/dist/nip/identity.js +0 -94
- package/dist/nip/identity.js.map +0 -1
- package/dist/nip/index.d.ts +0 -10
- package/dist/nip/index.d.ts.map +0 -1
- package/dist/nip/index.js +0 -13
- package/dist/nip/index.js.map +0 -1
- package/dist/nip/registry.d.ts +0 -3
- package/dist/nip/registry.d.ts.map +0 -1
- package/dist/nip/registry.js +0 -10
- package/dist/nip/registry.js.map +0 -1
- package/dist/nip/verifier.d.ts +0 -23
- package/dist/nip/verifier.d.ts.map +0 -1
- package/dist/nip/verifier.js +0 -90
- package/dist/nip/verifier.js.map +0 -1
- package/dist/nip/x509/builder.d.ts +0 -35
- package/dist/nip/x509/builder.d.ts.map +0 -1
- package/dist/nip/x509/builder.js +0 -59
- package/dist/nip/x509/builder.js.map +0 -1
- package/dist/nip/x509/index.d.ts +0 -4
- package/dist/nip/x509/index.d.ts.map +0 -1
- package/dist/nip/x509/index.js +0 -6
- package/dist/nip/x509/index.js.map +0 -1
- package/dist/nip/x509/oids.d.ts +0 -17
- package/dist/nip/x509/oids.d.ts.map +0 -1
- package/dist/nip/x509/oids.js +0 -23
- package/dist/nip/x509/oids.js.map +0 -1
- package/dist/nip/x509/verifier.d.ts +0 -26
- package/dist/nip/x509/verifier.d.ts.map +0 -1
- package/dist/nip/x509/verifier.js +0 -171
- package/dist/nip/x509/verifier.js.map +0 -1
- package/dist/nop/client.d.ts +0 -34
- package/dist/nop/client.d.ts.map +0 -1
- package/dist/nop/client.js +0 -90
- package/dist/nop/client.js.map +0 -1
- package/dist/nop/frames.d.ts +0 -65
- package/dist/nop/frames.d.ts.map +0 -1
- package/dist/nop/frames.js +0 -148
- package/dist/nop/frames.js.map +0 -1
- package/dist/nop/index.d.ts +0 -5
- package/dist/nop/index.d.ts.map +0 -1
- package/dist/nop/index.js +0 -7
- package/dist/nop/index.js.map +0 -1
- package/dist/nop/models.d.ts +0 -58
- package/dist/nop/models.d.ts.map +0 -1
- package/dist/nop/models.js +0 -50
- package/dist/nop/models.js.map +0 -1
- package/dist/nop/nop-types.d.ts +0 -136
- package/dist/nop/nop-types.d.ts.map +0 -1
- package/dist/nop/nop-types.js +0 -44
- package/dist/nop/nop-types.js.map +0 -1
- package/dist/nop/registry.d.ts +0 -3
- package/dist/nop/registry.d.ts.map +0 -1
- package/dist/nop/registry.js +0 -11
- package/dist/nop/registry.js.map +0 -1
- package/dist/nwp/client.d.ts +0 -22
- package/dist/nwp/client.d.ts.map +0 -1
- package/dist/nwp/client.js +0 -101
- package/dist/nwp/client.js.map +0 -1
- package/dist/nwp/frames.d.ts +0 -46
- package/dist/nwp/frames.d.ts.map +0 -1
- package/dist/nwp/frames.js +0 -81
- package/dist/nwp/frames.js.map +0 -1
- package/dist/nwp/index.d.ts +0 -4
- package/dist/nwp/index.d.ts.map +0 -1
- package/dist/nwp/index.js +0 -6
- package/dist/nwp/index.js.map +0 -1
- package/dist/nwp/registry.d.ts +0 -3
- package/dist/nwp/registry.d.ts.map +0 -1
- package/dist/nwp/registry.js +0 -9
- package/dist/nwp/registry.js.map +0 -1
- package/dist/setup.d.ts +0 -10
- package/dist/setup.d.ts.map +0 -1
- package/dist/setup.js +0 -29
- package/dist/setup.js.map +0 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Copyright 2026 INNO LOTUS PTY LTD
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import type { NdpResolveResult } from "./frames.js";
|
|
5
|
+
|
|
6
|
+
export const DNS_TXT_DEFAULT_TTL = 300;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extract the hostname from an NWP target URI.
|
|
10
|
+
* e.g. "nwp://api.example.com/products" → "api.example.com"
|
|
11
|
+
*/
|
|
12
|
+
export function extractHostFromTarget(target: string): string | undefined {
|
|
13
|
+
if (!target.startsWith("nwp://")) return undefined;
|
|
14
|
+
const rest = target.slice("nwp://".length);
|
|
15
|
+
const slashIdx = rest.indexOf("/");
|
|
16
|
+
const host = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
|
|
17
|
+
return host.length > 0 ? host : undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parse one TXT record (array of string chunks) into an NdpResolveResult.
|
|
22
|
+
*
|
|
23
|
+
* Expected format (chunks joined with a single space):
|
|
24
|
+
* v=nps1 type=<type> port=<port> nid=<nid> fp=sha256:<fingerprint>
|
|
25
|
+
*
|
|
26
|
+
* Rules:
|
|
27
|
+
* - `v` MUST be present and equal to "nps1"
|
|
28
|
+
* - `nid` MUST be present
|
|
29
|
+
* - `port` defaults to 17433 when absent
|
|
30
|
+
* - `fp` is mapped to certFingerprint
|
|
31
|
+
*/
|
|
32
|
+
export function parseNpsTxtRecord(
|
|
33
|
+
parts: string[],
|
|
34
|
+
host: string,
|
|
35
|
+
): NdpResolveResult | undefined {
|
|
36
|
+
const joined = parts.join(" ");
|
|
37
|
+
const kv = new Map<string, string>();
|
|
38
|
+
|
|
39
|
+
for (const token of joined.split(/\s+/)) {
|
|
40
|
+
const eq = token.indexOf("=");
|
|
41
|
+
if (eq === -1) continue;
|
|
42
|
+
const key = token.slice(0, eq);
|
|
43
|
+
const val = token.slice(eq + 1);
|
|
44
|
+
kv.set(key, val);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (kv.get("v") !== "nps1") return undefined;
|
|
48
|
+
|
|
49
|
+
const nid = kv.get("nid");
|
|
50
|
+
if (!nid) return undefined;
|
|
51
|
+
|
|
52
|
+
const rawPort = kv.get("port");
|
|
53
|
+
const port = rawPort !== undefined ? Number(rawPort) : 17433;
|
|
54
|
+
const fp = kv.get("fp");
|
|
55
|
+
|
|
56
|
+
const result: NdpResolveResult = {
|
|
57
|
+
host,
|
|
58
|
+
port,
|
|
59
|
+
ttl: DNS_TXT_DEFAULT_TTL,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (fp !== undefined) {
|
|
63
|
+
result.certFingerprint = fp;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Abstraction over DNS TXT lookups.
|
|
71
|
+
* The default implementation uses Node's `dns.promises`; pass a mock in tests.
|
|
72
|
+
*/
|
|
73
|
+
export interface DnsTxtLookup {
|
|
74
|
+
resolveTxt(hostname: string): Promise<string[][]>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* System DNS TXT resolver backed by Node's built-in `dns.promises`.
|
|
79
|
+
* The dynamic `import()` keeps browser bundles from failing at parse time.
|
|
80
|
+
*/
|
|
81
|
+
export class SystemDnsTxtLookup implements DnsTxtLookup {
|
|
82
|
+
async resolveTxt(hostname: string): Promise<string[][]> {
|
|
83
|
+
const { promises: dns } = await import("node:dns");
|
|
84
|
+
return dns.resolveTxt(hostname);
|
|
85
|
+
}
|
|
86
|
+
}
|
package/src/ndp/frames.ts
CHANGED
|
File without changes
|
package/src/ndp/index.ts
CHANGED
package/src/ndp/ndp-registry.ts
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import type { AnnounceFrame, NdpResolveResult } from "./frames.js";
|
|
5
|
+
import {
|
|
6
|
+
extractHostFromTarget,
|
|
7
|
+
parseNpsTxtRecord,
|
|
8
|
+
SystemDnsTxtLookup,
|
|
9
|
+
type DnsTxtLookup,
|
|
10
|
+
} from "./dns-txt.js";
|
|
5
11
|
|
|
6
12
|
interface RegistryEntry {
|
|
7
13
|
frame: AnnounceFrame;
|
|
@@ -54,6 +60,34 @@ export class InMemoryNdpRegistry {
|
|
|
54
60
|
return result;
|
|
55
61
|
}
|
|
56
62
|
|
|
63
|
+
async resolveWithDns(
|
|
64
|
+
target: string,
|
|
65
|
+
resolver: DnsTxtLookup = new SystemDnsTxtLookup(),
|
|
66
|
+
): Promise<NdpResolveResult | undefined> {
|
|
67
|
+
// 1. Try in-memory registry first
|
|
68
|
+
const cached = this.resolve(target);
|
|
69
|
+
if (cached !== undefined) return cached;
|
|
70
|
+
|
|
71
|
+
// 2. Extract hostname and fall back to DNS TXT lookup
|
|
72
|
+
const host = extractHostFromTarget(target);
|
|
73
|
+
if (host === undefined) return undefined;
|
|
74
|
+
|
|
75
|
+
const txtHost = `_nps-node.${host}`;
|
|
76
|
+
let records: string[][];
|
|
77
|
+
try {
|
|
78
|
+
records = await resolver.resolveTxt(txtHost);
|
|
79
|
+
} catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (const record of records) {
|
|
84
|
+
const result = parseNpsTxtRecord(record, host);
|
|
85
|
+
if (result !== undefined) return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
57
91
|
static nwpTargetMatchesNid(nid: string, target: string): boolean {
|
|
58
92
|
// NID: urn:nps:node:{authority}:{path-segment}
|
|
59
93
|
// target: nwp://{authority}/{path}
|
package/src/ndp/registry.ts
CHANGED
|
File without changes
|
package/src/ndp/validator.ts
CHANGED
|
File without changes
|
|
@@ -18,8 +18,13 @@ export class AssuranceLevel {
|
|
|
18
18
|
return this.rank >= required.rank;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Parse a wire string. `null`, `undefined`, or `""` → `ANONYMOUS`
|
|
23
|
+
* (backward compat per NPS-RFC-0003 §5.1.1). Any other unrecognised
|
|
24
|
+
* non-empty value throws — callers MUST surface it as `NIP-ASSURANCE-UNKNOWN`.
|
|
25
|
+
*/
|
|
21
26
|
static fromWire(wire: string | null | undefined): AssuranceLevel {
|
|
22
|
-
if (wire
|
|
27
|
+
if (!wire) return AssuranceLevel.ANONYMOUS; // null, undefined, or ""
|
|
23
28
|
for (const level of [AssuranceLevel.ANONYMOUS, AssuranceLevel.ATTESTED, AssuranceLevel.VERIFIED]) {
|
|
24
29
|
if (level.wire === wire) return level;
|
|
25
30
|
}
|
package/src/nip/error-codes.ts
CHANGED
|
@@ -26,8 +26,10 @@ export const ASSURANCE_MISMATCH = "NIP-ASSURANCE-MISMATCH";
|
|
|
26
26
|
export const ASSURANCE_UNKNOWN = "NIP-ASSURANCE-UNKNOWN";
|
|
27
27
|
|
|
28
28
|
// ── RFC-0004 (reputation log) ────────────────────────────────────────────────
|
|
29
|
-
export const REPUTATION_ENTRY_INVALID
|
|
30
|
-
export const REPUTATION_LOG_UNREACHABLE
|
|
29
|
+
export const REPUTATION_ENTRY_INVALID = "NIP-REPUTATION-ENTRY-INVALID";
|
|
30
|
+
export const REPUTATION_LOG_UNREACHABLE = "NIP-REPUTATION-LOG-UNREACHABLE";
|
|
31
|
+
export const REPUTATION_GOSSIP_FORK = "NIP-REPUTATION-GOSSIP-FORK";
|
|
32
|
+
export const REPUTATION_GOSSIP_SIG_INVALID = "NIP-REPUTATION-GOSSIP-SIG-INVALID";
|
|
31
33
|
|
|
32
34
|
// ── RFC-0002 (X.509 + ACME) ──────────────────────────────────────────────────
|
|
33
35
|
export const CERT_FORMAT_INVALID = "NIP-CERT-FORMAT-INVALID";
|
package/src/nip/identity.ts
CHANGED
|
File without changes
|
package/src/nip/registry.ts
CHANGED
|
File without changes
|
package/src/nip/x509/oids.ts
CHANGED
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* OID constants for NPS X.509 certificates per NPS-RFC-0002 §4.
|
|
6
6
|
*
|
|
7
|
-
* The 1.3.6.1.4.1.
|
|
8
|
-
* Number
|
|
9
|
-
* these constants when the official PEN is granted.
|
|
7
|
+
* The 1.3.6.1.4.1.65715 arc is the LabAcacia IANA-assigned Private Enterprise
|
|
8
|
+
* Number (PEN 65715, NPS-CR-0004, 2026-05-08).
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
|
-
export const LAB_ACACIA_PEN_ARC = "1.3.6.1.4.1.
|
|
11
|
+
export const LAB_ACACIA_PEN_ARC = "1.3.6.1.4.1.65715";
|
|
13
12
|
export const EKU_ARC = `${LAB_ACACIA_PEN_ARC}.1`;
|
|
14
13
|
export const EXTENSION_ARC = `${LAB_ACACIA_PEN_ARC}.2`;
|
|
15
14
|
|
package/src/nop/client.ts
CHANGED
|
@@ -57,7 +57,7 @@ export class NopClient {
|
|
|
57
57
|
const wire = this._codec.encode(frame, { overrideTier: this._tier });
|
|
58
58
|
const res = await fetch(`${this._baseUrl}/task`, {
|
|
59
59
|
method: "POST",
|
|
60
|
-
body: wire,
|
|
60
|
+
body: wire as BodyInit,
|
|
61
61
|
headers: { "Content-Type": "application/x-nps-frame", "Accept": "application/json" },
|
|
62
62
|
});
|
|
63
63
|
if (!res.ok) throw new Error(`NOP /task failed: HTTP ${res.status}`);
|
package/src/nop/frames.ts
CHANGED
|
File without changes
|
package/src/nop/index.ts
CHANGED
|
File without changes
|
package/src/nop/models.ts
CHANGED
|
File without changes
|
package/src/nop/nop-types.ts
CHANGED
|
File without changes
|
package/src/nop/registry.ts
CHANGED
|
File without changes
|
package/src/nwp/client.ts
CHANGED
|
@@ -44,7 +44,7 @@ export class NwpClient {
|
|
|
44
44
|
const wire = this._codec.encode(frame, { overrideTier: this._tier });
|
|
45
45
|
const res = await fetch(`${this._baseUrl}/anchor`, {
|
|
46
46
|
method: "POST",
|
|
47
|
-
body: wire,
|
|
47
|
+
body: wire as BodyInit,
|
|
48
48
|
headers: { "Content-Type": CONTENT_TYPE, "Accept": CONTENT_TYPE },
|
|
49
49
|
});
|
|
50
50
|
if (!res.ok) throw new Error(`NWP /anchor failed: HTTP ${res.status}`);
|
|
@@ -54,7 +54,7 @@ export class NwpClient {
|
|
|
54
54
|
const wire = this._codec.encode(frame, { overrideTier: this._tier });
|
|
55
55
|
const res = await fetch(`${this._baseUrl}/query`, {
|
|
56
56
|
method: "POST",
|
|
57
|
-
body: wire,
|
|
57
|
+
body: wire as BodyInit,
|
|
58
58
|
headers: { "Content-Type": CONTENT_TYPE, "Accept": CONTENT_TYPE },
|
|
59
59
|
});
|
|
60
60
|
if (!res.ok) throw new Error(`NWP /query failed: HTTP ${res.status}`);
|
|
@@ -71,7 +71,7 @@ export class NwpClient {
|
|
|
71
71
|
const wire = this._codec.encode(frame, { overrideTier: this._tier });
|
|
72
72
|
const res = await fetch(`${this._baseUrl}/stream`, {
|
|
73
73
|
method: "POST",
|
|
74
|
-
body: wire,
|
|
74
|
+
body: wire as BodyInit,
|
|
75
75
|
headers: { "Content-Type": CONTENT_TYPE, "Accept": CONTENT_TYPE },
|
|
76
76
|
});
|
|
77
77
|
if (!res.ok) throw new Error(`NWP /stream failed: HTTP ${res.status}`);
|
|
@@ -95,7 +95,7 @@ export class NwpClient {
|
|
|
95
95
|
const wire = this._codec.encode(frame, { overrideTier: this._tier });
|
|
96
96
|
const res = await fetch(`${this._baseUrl}/invoke`, {
|
|
97
97
|
method: "POST",
|
|
98
|
-
body: wire,
|
|
98
|
+
body: wire as BodyInit,
|
|
99
99
|
headers: { "Content-Type": CONTENT_TYPE, "Accept": CONTENT_TYPE },
|
|
100
100
|
});
|
|
101
101
|
if (!res.ok) throw new Error(`NWP /invoke failed: HTTP ${res.status}`);
|
package/src/nwp/frames.ts
CHANGED
|
File without changes
|
package/src/nwp/index.ts
CHANGED
|
File without changes
|
package/src/nwp/registry.ts
CHANGED
|
File without changes
|
package/src/setup.ts
CHANGED
|
File without changes
|
|
File without changes
|
package/tests/core/codec.test.ts
CHANGED
|
File without changes
|
|
File without changes
|
package/tests/core.test.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/tests/ncp/frames.test.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/tests/ncp/stream.test.ts
CHANGED
|
File without changes
|
|
File without changes
|
package/tests/ndp.test.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { describe, expect, it } from "vitest";
|
|
|
5
5
|
import { AnnounceFrame, ResolveFrame, GraphFrame } from "../src/ndp/frames.js";
|
|
6
6
|
import { InMemoryNdpRegistry } from "../src/ndp/ndp-registry.js";
|
|
7
7
|
import { NdpAnnounceValidator, NdpAnnounceResult } from "../src/ndp/validator.js";
|
|
8
|
+
import { parseNpsTxtRecord, extractHostFromTarget, type DnsTxtLookup } from "../src/ndp/dns-txt.js";
|
|
8
9
|
import { NipIdentity } from "../src/nip/identity.js";
|
|
9
10
|
import { createFullRegistry } from "../src/setup.js";
|
|
10
11
|
import { NpsFrameCodec } from "../src/core/index.js";
|
|
@@ -269,3 +270,108 @@ describe("NdpAnnounceValidator", () => {
|
|
|
269
270
|
expect(v.knownPublicKeys.size).toBe(1);
|
|
270
271
|
});
|
|
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
|
+
});
|
package/tests/nip.test.ts
CHANGED
|
File without changes
|
package/tests/nop.test.ts
CHANGED
|
File without changes
|
package/tests/nwp.test.ts
CHANGED
|
File without changes
|
package/tsconfig.json
CHANGED
|
File without changes
|
package/tsup.config.ts
CHANGED
|
File without changes
|
package/vitest.config.ts
CHANGED
|
File without changes
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { AnchorFrame } from "../ncp/frames/anchor-frame.js";
|
|
2
|
-
/**
|
|
3
|
-
* AnchorFrame cache with:
|
|
4
|
-
* - TTL-based expiry (NPS-1 §5.3)
|
|
5
|
-
* - LRU eviction at maxSize (NPS-1 §9, default 1000)
|
|
6
|
-
* - Anchor poisoning detection (NPS-1 §7.2)
|
|
7
|
-
*/
|
|
8
|
-
export declare class AnchorCache {
|
|
9
|
-
private readonly cache;
|
|
10
|
-
private readonly maxSize;
|
|
11
|
-
private readonly getNow;
|
|
12
|
-
constructor(options?: {
|
|
13
|
-
maxSize?: number;
|
|
14
|
-
getNow?: () => number;
|
|
15
|
-
});
|
|
16
|
-
/**
|
|
17
|
-
* Cache an AnchorFrame.
|
|
18
|
-
*
|
|
19
|
-
* - ttl=0: frame is valid but not cached (NPS-1 §4.1)
|
|
20
|
-
* - Same anchor_id + same schema: idempotent (no-op)
|
|
21
|
-
* - Same anchor_id + different schema: NCP-ANCHOR-ID-MISMATCH (poison detection)
|
|
22
|
-
*
|
|
23
|
-
* @throws {NcpError} NCP-ANCHOR-ID-MISMATCH on anchor poisoning.
|
|
24
|
-
*/
|
|
25
|
-
set(frame: AnchorFrame): void;
|
|
26
|
-
/**
|
|
27
|
-
* Get a cached AnchorFrame by anchor_id.
|
|
28
|
-
*
|
|
29
|
-
* @returns The cached frame, or null if not found or expired.
|
|
30
|
-
*/
|
|
31
|
-
get(anchorId: string): AnchorFrame | null;
|
|
32
|
-
/**
|
|
33
|
-
* Get a cached AnchorFrame, throwing if not found.
|
|
34
|
-
* @throws {NcpError} NCP-ANCHOR-NOT-FOUND if not in cache or expired.
|
|
35
|
-
*/
|
|
36
|
-
getRequired(anchorId: string): AnchorFrame;
|
|
37
|
-
/** Current cache size. */
|
|
38
|
-
get size(): number;
|
|
39
|
-
/** Evict the least recently accessed entry. */
|
|
40
|
-
private evictLru;
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=anchor-cache.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"anchor-cache.d.ts","sourceRoot":"","sources":["../../src/core/anchor-cache.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAQjE;;;;;GAKG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;gBAE1B,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,MAAM,CAAA;KAAE;IAKjE;;;;;;;;OAQG;IACH,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAiC7B;;;;OAIG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAczC;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW;IAW1C,0BAA0B;IAC1B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,+CAA+C;IAC/C,OAAO,CAAC,QAAQ;CAejB"}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// Copyright (c) 2026 LabAcacia / INNO LOTUS PTY LTD
|
|
3
|
-
//
|
|
4
|
-
// AnchorCache — Schema cache with TTL, LRU eviction, poison detection
|
|
5
|
-
// NPS-1 §5.3, §7.2, §9
|
|
6
|
-
import { NcpError } from "./frame-header.js";
|
|
7
|
-
/**
|
|
8
|
-
* AnchorFrame cache with:
|
|
9
|
-
* - TTL-based expiry (NPS-1 §5.3)
|
|
10
|
-
* - LRU eviction at maxSize (NPS-1 §9, default 1000)
|
|
11
|
-
* - Anchor poisoning detection (NPS-1 §7.2)
|
|
12
|
-
*/
|
|
13
|
-
export class AnchorCache {
|
|
14
|
-
cache = new Map();
|
|
15
|
-
maxSize;
|
|
16
|
-
getNow;
|
|
17
|
-
constructor(options) {
|
|
18
|
-
this.maxSize = options?.maxSize ?? 1000;
|
|
19
|
-
this.getNow = options?.getNow ?? (() => Date.now());
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Cache an AnchorFrame.
|
|
23
|
-
*
|
|
24
|
-
* - ttl=0: frame is valid but not cached (NPS-1 §4.1)
|
|
25
|
-
* - Same anchor_id + same schema: idempotent (no-op)
|
|
26
|
-
* - Same anchor_id + different schema: NCP-ANCHOR-ID-MISMATCH (poison detection)
|
|
27
|
-
*
|
|
28
|
-
* @throws {NcpError} NCP-ANCHOR-ID-MISMATCH on anchor poisoning.
|
|
29
|
-
*/
|
|
30
|
-
set(frame) {
|
|
31
|
-
// ttl=0 means use once, don't cache
|
|
32
|
-
if (frame.ttl === 0)
|
|
33
|
-
return;
|
|
34
|
-
const existing = this.cache.get(frame.anchor_id);
|
|
35
|
-
if (existing) {
|
|
36
|
-
// Poison detection: same ID, different schema
|
|
37
|
-
const existingJson = JSON.stringify(existing.frame.schema);
|
|
38
|
-
const newJson = JSON.stringify(frame.schema);
|
|
39
|
-
if (existingJson !== newJson) {
|
|
40
|
-
throw new NcpError("NCP-ANCHOR-ID-MISMATCH", `Anchor poisoning detected: anchor_id ${frame.anchor_id} received with different schema`);
|
|
41
|
-
}
|
|
42
|
-
// Same schema — idempotent, update access time
|
|
43
|
-
existing.lastAccessed = this.getNow();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
// LRU eviction if at capacity
|
|
47
|
-
if (this.cache.size >= this.maxSize) {
|
|
48
|
-
this.evictLru();
|
|
49
|
-
}
|
|
50
|
-
const ttlMs = (frame.ttl ?? 3600) * 1000;
|
|
51
|
-
this.cache.set(frame.anchor_id, {
|
|
52
|
-
frame,
|
|
53
|
-
expiresAt: this.getNow() + ttlMs,
|
|
54
|
-
lastAccessed: this.getNow(),
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Get a cached AnchorFrame by anchor_id.
|
|
59
|
-
*
|
|
60
|
-
* @returns The cached frame, or null if not found or expired.
|
|
61
|
-
*/
|
|
62
|
-
get(anchorId) {
|
|
63
|
-
const entry = this.cache.get(anchorId);
|
|
64
|
-
if (!entry)
|
|
65
|
-
return null;
|
|
66
|
-
// Check TTL expiry
|
|
67
|
-
if (this.getNow() >= entry.expiresAt) {
|
|
68
|
-
this.cache.delete(anchorId);
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
entry.lastAccessed = this.getNow();
|
|
72
|
-
return entry.frame;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Get a cached AnchorFrame, throwing if not found.
|
|
76
|
-
* @throws {NcpError} NCP-ANCHOR-NOT-FOUND if not in cache or expired.
|
|
77
|
-
*/
|
|
78
|
-
getRequired(anchorId) {
|
|
79
|
-
const frame = this.get(anchorId);
|
|
80
|
-
if (!frame) {
|
|
81
|
-
throw new NcpError("NCP-ANCHOR-NOT-FOUND", `Schema anchor ${anchorId} not found in cache`);
|
|
82
|
-
}
|
|
83
|
-
return frame;
|
|
84
|
-
}
|
|
85
|
-
/** Current cache size. */
|
|
86
|
-
get size() {
|
|
87
|
-
return this.cache.size;
|
|
88
|
-
}
|
|
89
|
-
/** Evict the least recently accessed entry. */
|
|
90
|
-
evictLru() {
|
|
91
|
-
let oldestKey;
|
|
92
|
-
let oldestTime = Infinity;
|
|
93
|
-
for (const [key, entry] of this.cache) {
|
|
94
|
-
if (entry.lastAccessed < oldestTime) {
|
|
95
|
-
oldestTime = entry.lastAccessed;
|
|
96
|
-
oldestKey = key;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (oldestKey) {
|
|
100
|
-
this.cache.delete(oldestKey);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
//# sourceMappingURL=anchor-cache.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"anchor-cache.js","sourceRoot":"","sources":["../../src/core/anchor-cache.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,oDAAoD;AACpD,EAAE;AACF,sEAAsE;AACtE,uBAAuB;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAS7C;;;;;GAKG;AACH,MAAM,OAAO,WAAW;IACL,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IACtC,OAAO,CAAS;IAChB,MAAM,CAAe;IAEtC,YAAY,OAAqD;QAC/D,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED;;;;;;;;OAQG;IACH,GAAG,CAAC,KAAkB;QACpB,oCAAoC;QACpC,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC;YAAE,OAAO;QAE5B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACjD,IAAI,QAAQ,EAAE,CAAC;YACb,8CAA8C;YAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,YAAY,KAAK,OAAO,EAAE,CAAC;gBAC7B,MAAM,IAAI,QAAQ,CAChB,wBAAwB,EACxB,wCAAwC,KAAK,CAAC,SAAS,iCAAiC,CACzF,CAAC;YACJ,CAAC;YACD,+CAA+C;YAC/C,QAAQ,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YACtC,OAAO;QACT,CAAC;QAED,8BAA8B;QAC9B,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,CAAC;QAED,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE;YAC9B,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK;YAChC,YAAY,EAAE,IAAI,CAAC,MAAM,EAAE;SAC5B,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,GAAG,CAAC,QAAgB;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,mBAAmB;QACnB,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,QAAgB;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,QAAQ,CAChB,sBAAsB,EACtB,iBAAiB,QAAQ,qBAAqB,CAC/C,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,0BAA0B;IAC1B,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED,+CAA+C;IACvC,QAAQ;QACd,IAAI,SAA6B,CAAC;QAClC,IAAI,UAAU,GAAG,QAAQ,CAAC;QAE1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,YAAY,GAAG,UAAU,EAAE,CAAC;gBACpC,UAAU,GAAG,KAAK,CAAC,YAAY,CAAC;gBAChC,SAAS,GAAG,GAAG,CAAC;YAClB,CAAC;QACH,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;CACF"}
|
package/dist/core/cache.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { AnchorFrame, FrameSchema } from "../ncp/frames.js";
|
|
2
|
-
export declare class AnchorFrameCache {
|
|
3
|
-
private readonly _store;
|
|
4
|
-
clock: () => number;
|
|
5
|
-
set(frame: AnchorFrame): string;
|
|
6
|
-
get(anchorId: string): AnchorFrame | undefined;
|
|
7
|
-
getRequired(anchorId: string): AnchorFrame;
|
|
8
|
-
invalidate(anchorId: string): void;
|
|
9
|
-
get size(): number;
|
|
10
|
-
static computeAnchorId(schema: FrameSchema): string;
|
|
11
|
-
private _evictExpired;
|
|
12
|
-
private static _schemasEqual;
|
|
13
|
-
}
|
|
14
|
-
//# sourceMappingURL=cache.d.ts.map
|
package/dist/core/cache.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/core/cache.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEjE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgE;IAGvF,KAAK,EAAE,MAAM,MAAM,CAAoB;IAIvC,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM;IAmB/B,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAU9C,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW;IAM1C,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIlC,IAAI,IAAI,IAAI,MAAM,CAGjB;IAID,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAiBnD,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,MAAM,CAAC,aAAa;CAG7B"}
|