@labacacia/nps-sdk 1.0.0-alpha.1 → 1.0.0-alpha.13
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 +181 -0
- package/CHANGELOG.md +234 -0
- package/LICENSE +0 -0
- package/NOTICE +0 -0
- package/README.cn.md +163 -0
- package/README.md +16 -6
- package/dist/core/anchor-cache.d.ts +0 -0
- package/dist/core/anchor-cache.d.ts.map +0 -0
- package/dist/core/anchor-cache.js +0 -0
- package/dist/core/anchor-cache.js.map +0 -0
- package/dist/core/cache.d.ts +0 -0
- package/dist/core/cache.d.ts.map +0 -0
- package/dist/core/cache.js +0 -0
- package/dist/core/cache.js.map +0 -0
- package/dist/core/canonical-json.d.ts +0 -0
- package/dist/core/canonical-json.d.ts.map +0 -0
- package/dist/core/canonical-json.js +0 -0
- package/dist/core/canonical-json.js.map +0 -0
- package/dist/core/codec.d.ts +0 -0
- package/dist/core/codec.d.ts.map +0 -0
- package/dist/core/codec.js +0 -0
- package/dist/core/codec.js.map +0 -0
- package/dist/core/codecs/index.d.ts +0 -0
- package/dist/core/codecs/index.d.ts.map +0 -0
- package/dist/core/codecs/index.js +0 -0
- package/dist/core/codecs/index.js.map +0 -0
- package/dist/core/codecs/ncp-codec.d.ts +0 -0
- package/dist/core/codecs/ncp-codec.d.ts.map +0 -0
- package/dist/core/codecs/ncp-codec.js +0 -0
- package/dist/core/codecs/ncp-codec.js.map +0 -0
- package/dist/core/codecs/tier1-json-codec.d.ts +0 -0
- package/dist/core/codecs/tier1-json-codec.d.ts.map +0 -0
- package/dist/core/codecs/tier1-json-codec.js +0 -0
- package/dist/core/codecs/tier1-json-codec.js.map +0 -0
- package/dist/core/codecs/tier2-msgpack-codec.d.ts +0 -0
- package/dist/core/codecs/tier2-msgpack-codec.d.ts.map +0 -0
- package/dist/core/codecs/tier2-msgpack-codec.js +0 -0
- package/dist/core/codecs/tier2-msgpack-codec.js.map +0 -0
- package/dist/core/crypto-provider.d.ts +0 -0
- package/dist/core/crypto-provider.d.ts.map +0 -0
- package/dist/core/crypto-provider.js +0 -0
- package/dist/core/crypto-provider.js.map +0 -0
- package/dist/core/exceptions.d.ts +0 -0
- package/dist/core/exceptions.d.ts.map +0 -0
- package/dist/core/exceptions.js +0 -0
- package/dist/core/exceptions.js.map +0 -0
- package/dist/core/frame-header.d.ts +1 -0
- package/dist/core/frame-header.d.ts.map +1 -1
- package/dist/core/frame-header.js +1 -0
- package/dist/core/frame-header.js.map +1 -1
- package/dist/core/frame-registry.d.ts +0 -0
- package/dist/core/frame-registry.d.ts.map +1 -1
- package/dist/core/frame-registry.js +1 -0
- package/dist/core/frame-registry.js.map +1 -1
- package/dist/core/frames.d.ts +3 -0
- package/dist/core/frames.d.ts.map +1 -1
- package/dist/core/frames.js +3 -0
- package/dist/core/frames.js.map +1 -1
- package/dist/core/index.d.ts +6 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +17 -5
- package/dist/core/index.js.map +1 -1
- package/dist/core/registry.d.ts +0 -0
- package/dist/core/registry.d.ts.map +0 -0
- package/dist/core/registry.js +0 -0
- package/dist/core/registry.js.map +0 -0
- package/dist/core/status-codes.d.ts +20 -0
- package/dist/core/status-codes.d.ts.map +1 -1
- package/dist/core/status-codes.js +51 -0
- package/dist/core/status-codes.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/ncp/frames/anchor-frame.d.ts +0 -0
- package/dist/ncp/frames/anchor-frame.d.ts.map +0 -0
- package/dist/ncp/frames/anchor-frame.js +0 -0
- package/dist/ncp/frames/anchor-frame.js.map +0 -0
- package/dist/ncp/frames/caps-frame.d.ts +0 -0
- package/dist/ncp/frames/caps-frame.d.ts.map +0 -0
- package/dist/ncp/frames/caps-frame.js +0 -0
- package/dist/ncp/frames/caps-frame.js.map +0 -0
- package/dist/ncp/frames/diff-frame.d.ts +0 -0
- package/dist/ncp/frames/diff-frame.d.ts.map +0 -0
- package/dist/ncp/frames/diff-frame.js +0 -0
- package/dist/ncp/frames/diff-frame.js.map +0 -0
- package/dist/ncp/frames/error-frame.d.ts +0 -0
- package/dist/ncp/frames/error-frame.d.ts.map +0 -0
- package/dist/ncp/frames/error-frame.js +0 -0
- package/dist/ncp/frames/error-frame.js.map +0 -0
- package/dist/ncp/frames/hello-frame.d.ts +0 -0
- package/dist/ncp/frames/hello-frame.d.ts.map +0 -0
- package/dist/ncp/frames/hello-frame.js +0 -0
- package/dist/ncp/frames/hello-frame.js.map +0 -0
- package/dist/ncp/frames/stream-frame.d.ts +0 -0
- package/dist/ncp/frames/stream-frame.d.ts.map +0 -0
- package/dist/ncp/frames/stream-frame.js +0 -0
- package/dist/ncp/frames/stream-frame.js.map +0 -0
- package/dist/ncp/frames.d.ts +25 -0
- package/dist/ncp/frames.d.ts.map +1 -1
- package/dist/ncp/frames.js +61 -0
- package/dist/ncp/frames.js.map +1 -1
- package/dist/ncp/handshake.d.ts +0 -0
- package/dist/ncp/handshake.d.ts.map +0 -0
- package/dist/ncp/handshake.js +0 -0
- package/dist/ncp/handshake.js.map +0 -0
- package/dist/ncp/index.d.ts +1 -0
- package/dist/ncp/index.d.ts.map +1 -1
- package/dist/ncp/index.js +1 -0
- package/dist/ncp/index.js.map +1 -1
- package/dist/ncp/ncp-error-codes.d.ts +5 -0
- package/dist/ncp/ncp-error-codes.d.ts.map +1 -1
- package/dist/ncp/ncp-error-codes.js +27 -0
- package/dist/ncp/ncp-error-codes.js.map +1 -1
- package/dist/ncp/ncp-patch-format.d.ts +0 -0
- package/dist/ncp/ncp-patch-format.d.ts.map +0 -0
- package/dist/ncp/ncp-patch-format.js +0 -0
- package/dist/ncp/ncp-patch-format.js.map +0 -0
- package/dist/ncp/preamble.d.ts +47 -0
- package/dist/ncp/preamble.d.ts.map +1 -0
- package/dist/ncp/preamble.js +74 -0
- package/dist/ncp/preamble.js.map +1 -0
- package/dist/ncp/registry.d.ts +0 -0
- package/dist/ncp/registry.d.ts.map +1 -1
- package/dist/ncp/registry.js +2 -1
- package/dist/ncp/registry.js.map +1 -1
- package/dist/ncp/stream-manager.d.ts +0 -0
- package/dist/ncp/stream-manager.d.ts.map +0 -0
- package/dist/ncp/stream-manager.js +0 -0
- package/dist/ncp/stream-manager.js.map +0 -0
- package/dist/ndp/dns-txt.d.ts +35 -0
- package/dist/ndp/dns-txt.d.ts.map +1 -0
- package/dist/ndp/dns-txt.js +67 -0
- package/dist/ndp/dns-txt.js.map +1 -0
- package/dist/ndp/frames.d.ts +34 -9
- package/dist/ndp/frames.d.ts.map +1 -1
- package/dist/ndp/frames.js +54 -15
- package/dist/ndp/frames.js.map +1 -1
- package/dist/ndp/index.d.ts +3 -0
- package/dist/ndp/index.d.ts.map +1 -1
- package/dist/ndp/index.js +3 -0
- package/dist/ndp/index.js.map +1 -1
- package/dist/ndp/ndp-error-codes.d.ts +25 -0
- package/dist/ndp/ndp-error-codes.d.ts.map +1 -0
- package/dist/ndp/ndp-error-codes.js +48 -0
- package/dist/ndp/ndp-error-codes.js.map +1 -0
- package/dist/ndp/ndp-registry.d.ts +2 -0
- package/dist/ndp/ndp-registry.d.ts.map +1 -1
- package/dist/ndp/ndp-registry.js +25 -0
- package/dist/ndp/ndp-registry.js.map +1 -1
- package/dist/ndp/registry.d.ts +0 -0
- package/dist/ndp/registry.d.ts.map +0 -0
- package/dist/ndp/registry.js +0 -0
- package/dist/ndp/registry.js.map +0 -0
- package/dist/ndp/security.d.ts +8 -0
- package/dist/ndp/security.d.ts.map +1 -0
- package/dist/ndp/security.js +9 -0
- package/dist/ndp/security.js.map +1 -0
- package/dist/ndp/validator.d.ts +0 -0
- package/dist/ndp/validator.d.ts.map +0 -0
- package/dist/ndp/validator.js +0 -0
- package/dist/ndp/validator.js.map +0 -0
- package/dist/nip/acme/client.d.ts +31 -0
- package/dist/nip/acme/client.d.ts.map +1 -0
- package/dist/nip/acme/client.js +136 -0
- package/dist/nip/acme/client.js.map +1 -0
- package/dist/nip/acme/index.d.ts +6 -0
- package/dist/nip/acme/index.d.ts.map +1 -0
- package/dist/nip/acme/index.js +8 -0
- package/dist/nip/acme/index.js.map +1 -0
- package/dist/nip/acme/jws.d.ts +31 -0
- package/dist/nip/acme/jws.d.ts.map +1 -0
- package/dist/nip/acme/jws.js +76 -0
- package/dist/nip/acme/jws.js.map +1 -0
- package/dist/nip/acme/messages.d.ts +71 -0
- package/dist/nip/acme/messages.d.ts.map +1 -0
- package/dist/nip/acme/messages.js +4 -0
- package/dist/nip/acme/messages.js.map +1 -0
- package/dist/nip/acme/server.d.ts +41 -0
- package/dist/nip/acme/server.d.ts.map +1 -0
- package/dist/nip/acme/server.js +458 -0
- package/dist/nip/acme/server.js.map +1 -0
- package/dist/nip/acme/wire.d.ts +19 -0
- package/dist/nip/acme/wire.d.ts.map +1 -0
- package/dist/nip/acme/wire.js +21 -0
- package/dist/nip/acme/wire.js.map +1 -0
- package/dist/nip/assurance-level.d.ts +19 -0
- package/dist/nip/assurance-level.d.ts.map +1 -0
- package/dist/nip/assurance-level.js +38 -0
- package/dist/nip/assurance-level.js.map +1 -0
- package/dist/nip/cert-format.d.ts +5 -0
- package/dist/nip/cert-format.d.ts.map +1 -0
- package/dist/nip/cert-format.js +6 -0
- package/dist/nip/cert-format.js.map +1 -0
- package/dist/nip/error-codes.d.ts +44 -0
- package/dist/nip/error-codes.d.ts.map +1 -0
- package/dist/nip/error-codes.js +97 -0
- package/dist/nip/error-codes.js.map +1 -0
- package/dist/nip/frames.d.ts +19 -1
- package/dist/nip/frames.d.ts.map +1 -1
- package/dist/nip/frames.js +39 -4
- package/dist/nip/frames.js.map +1 -1
- package/dist/nip/identity.d.ts +0 -0
- package/dist/nip/identity.d.ts.map +0 -0
- package/dist/nip/identity.js +0 -0
- package/dist/nip/identity.js.map +0 -0
- package/dist/nip/index.d.ts +7 -0
- package/dist/nip/index.d.ts.map +1 -1
- package/dist/nip/index.js +9 -0
- package/dist/nip/index.js.map +1 -1
- package/dist/nip/registry.d.ts +0 -0
- package/dist/nip/registry.d.ts.map +0 -0
- package/dist/nip/registry.js +0 -0
- package/dist/nip/registry.js.map +0 -0
- package/dist/nip/reputation-client.d.ts +116 -0
- package/dist/nip/reputation-client.d.ts.map +1 -0
- package/dist/nip/reputation-client.js +261 -0
- package/dist/nip/reputation-client.js.map +1 -0
- package/dist/nip/verifier.d.ts +23 -0
- package/dist/nip/verifier.d.ts.map +1 -0
- package/dist/nip/verifier.js +90 -0
- package/dist/nip/verifier.js.map +1 -0
- package/dist/nip/x509/builder.d.ts +35 -0
- package/dist/nip/x509/builder.d.ts.map +1 -0
- package/dist/nip/x509/builder.js +59 -0
- package/dist/nip/x509/builder.js.map +1 -0
- package/dist/nip/x509/index.d.ts +4 -0
- package/dist/nip/x509/index.d.ts.map +1 -0
- package/dist/nip/x509/index.js +6 -0
- package/dist/nip/x509/index.js.map +1 -0
- package/dist/nip/x509/oids.d.ts +16 -0
- package/dist/nip/x509/oids.d.ts.map +1 -0
- package/dist/nip/x509/oids.js +22 -0
- package/dist/nip/x509/oids.js.map +1 -0
- package/dist/nip/x509/verifier.d.ts +26 -0
- package/dist/nip/x509/verifier.d.ts.map +1 -0
- package/dist/nip/x509/verifier.js +171 -0
- package/dist/nip/x509/verifier.js.map +1 -0
- package/dist/nop/client.d.ts +0 -0
- package/dist/nop/client.d.ts.map +0 -0
- package/dist/nop/client.js +0 -0
- package/dist/nop/client.js.map +1 -1
- package/dist/nop/dag-validator.d.ts +15 -0
- package/dist/nop/dag-validator.d.ts.map +1 -0
- package/dist/nop/dag-validator.js +91 -0
- package/dist/nop/dag-validator.js.map +1 -0
- package/dist/nop/frames.d.ts +8 -3
- package/dist/nop/frames.d.ts.map +1 -1
- package/dist/nop/frames.js +21 -6
- package/dist/nop/frames.js.map +1 -1
- package/dist/nop/index.d.ts +3 -0
- package/dist/nop/index.d.ts.map +1 -1
- package/dist/nop/index.js +3 -0
- package/dist/nop/index.js.map +1 -1
- package/dist/nop/models.d.ts +13 -2
- package/dist/nop/models.d.ts.map +1 -1
- package/dist/nop/models.js +9 -0
- package/dist/nop/models.js.map +1 -1
- package/dist/nop/nop-error-codes.d.ts +31 -0
- package/dist/nop/nop-error-codes.d.ts.map +1 -0
- package/dist/nop/nop-error-codes.js +61 -0
- package/dist/nop/nop-error-codes.js.map +1 -0
- package/dist/nop/nop-types.d.ts +0 -0
- package/dist/nop/nop-types.d.ts.map +0 -0
- package/dist/nop/nop-types.js +0 -0
- package/dist/nop/nop-types.js.map +0 -0
- package/dist/nop/orchestrator.d.ts +66 -0
- package/dist/nop/orchestrator.d.ts.map +1 -0
- package/dist/nop/orchestrator.js +229 -0
- package/dist/nop/orchestrator.js.map +1 -0
- package/dist/nop/registry.d.ts +0 -0
- package/dist/nop/registry.d.ts.map +0 -0
- package/dist/nop/registry.js +0 -0
- package/dist/nop/registry.js.map +0 -0
- package/dist/nwp/anchor-client.d.ts +109 -0
- package/dist/nwp/anchor-client.d.ts.map +1 -0
- package/dist/nwp/anchor-client.js +279 -0
- package/dist/nwp/anchor-client.js.map +1 -0
- package/dist/nwp/anchor-server.d.ts +127 -0
- package/dist/nwp/anchor-server.d.ts.map +1 -0
- package/dist/nwp/anchor-server.js +649 -0
- package/dist/nwp/anchor-server.js.map +1 -0
- package/dist/nwp/bridge.d.ts +24 -0
- package/dist/nwp/bridge.d.ts.map +1 -0
- package/dist/nwp/bridge.js +26 -0
- package/dist/nwp/bridge.js.map +1 -0
- package/dist/nwp/cgn.d.ts +19 -0
- package/dist/nwp/cgn.d.ts.map +1 -0
- package/dist/nwp/cgn.js +29 -0
- package/dist/nwp/cgn.js.map +1 -0
- package/dist/nwp/client.d.ts +10 -3
- package/dist/nwp/client.d.ts.map +1 -1
- package/dist/nwp/client.js +58 -7
- package/dist/nwp/client.js.map +1 -1
- package/dist/nwp/frames.d.ts +56 -2
- package/dist/nwp/frames.d.ts.map +1 -1
- package/dist/nwp/frames.js +86 -4
- package/dist/nwp/frames.js.map +1 -1
- package/dist/nwp/http-headers.d.ts +24 -0
- package/dist/nwp/http-headers.d.ts.map +1 -0
- package/dist/nwp/http-headers.js +29 -0
- package/dist/nwp/http-headers.js.map +1 -0
- package/dist/nwp/index.d.ts +9 -0
- package/dist/nwp/index.d.ts.map +1 -1
- package/dist/nwp/index.js +9 -0
- package/dist/nwp/index.js.map +1 -1
- package/dist/nwp/manifest.d.ts +75 -0
- package/dist/nwp/manifest.d.ts.map +1 -0
- package/dist/nwp/manifest.js +5 -0
- package/dist/nwp/manifest.js.map +1 -0
- package/dist/nwp/memory-node-server.d.ts +70 -0
- package/dist/nwp/memory-node-server.d.ts.map +1 -0
- package/dist/nwp/memory-node-server.js +315 -0
- package/dist/nwp/memory-node-server.js.map +1 -0
- package/dist/nwp/nwp-error-codes.d.ts +48 -0
- package/dist/nwp/nwp-error-codes.d.ts.map +1 -0
- package/dist/nwp/nwp-error-codes.js +108 -0
- package/dist/nwp/nwp-error-codes.js.map +1 -0
- package/dist/nwp/registry.d.ts +0 -0
- package/dist/nwp/registry.d.ts.map +0 -0
- package/dist/nwp/registry.js +0 -0
- package/dist/nwp/registry.js.map +0 -0
- package/dist/nwp/reputation.d.ts +37 -0
- package/dist/nwp/reputation.d.ts.map +1 -0
- package/dist/nwp/reputation.js +118 -0
- package/dist/nwp/reputation.js.map +1 -0
- package/dist/setup.d.ts +0 -0
- package/dist/setup.d.ts.map +0 -0
- package/dist/setup.js +0 -0
- package/dist/setup.js.map +0 -0
- package/doc/nps-sdk.core.cn.md +321 -0
- package/doc/nps-sdk.core.md +326 -0
- package/doc/nps-sdk.ncp.cn.md +270 -0
- package/doc/nps-sdk.ncp.md +276 -0
- package/doc/nps-sdk.ndp.cn.md +267 -0
- package/doc/nps-sdk.ndp.md +273 -0
- package/doc/nps-sdk.nip.cn.md +265 -0
- package/doc/nps-sdk.nip.md +272 -0
- package/doc/nps-sdk.nop.cn.md +329 -0
- package/doc/nps-sdk.nop.md +332 -0
- package/doc/nps-sdk.nwp.cn.md +288 -0
- package/doc/nps-sdk.nwp.md +295 -0
- package/doc/overview.cn.md +149 -0
- package/doc/overview.md +153 -0
- package/package.json +33 -4
- package/CONTRIBUTING.md +0 -33
- package/dist/codec-CmHeovTV.d.cts +0 -120
- package/dist/codec-CmHeovTV.d.ts +0 -120
- package/dist/core/index.cjs +0 -371
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -41
- package/dist/frames-B3qLdl_g.d.cts +0 -77
- package/dist/frames-Ff7-ZPUl.d.ts +0 -77
- package/dist/index.cjs +0 -1556
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -21
- package/dist/ncp/index.cjs +0 -188
- package/dist/ncp/index.cjs.map +0 -1
- package/dist/ncp/index.d.cts +0 -6
- package/dist/ndp/index.cjs +0 -252
- package/dist/ndp/index.cjs.map +0 -1
- package/dist/ndp/index.d.cts +0 -86
- package/dist/nip/index.cjs +0 -214
- package/dist/nip/index.cjs.map +0 -1
- package/dist/nip/index.d.cts +0 -65
- package/dist/nop/index.cjs +0 -762
- package/dist/nop/index.cjs.map +0 -1
- package/dist/nop/index.d.cts +0 -155
- package/dist/nwp/index.cjs +0 -658
- package/dist/nwp/index.cjs.map +0 -1
- package/dist/nwp/index.d.cts +0 -65
- package/nip-ca-server/Dockerfile +0 -27
- package/nip-ca-server/README.md +0 -45
- package/nip-ca-server/db/001_init.sql +0 -25
- package/nip-ca-server/docker-compose.yml +0 -29
- package/nip-ca-server/package.json +0 -23
- package/nip-ca-server/src/ca.ts +0 -155
- package/nip-ca-server/src/db.ts +0 -104
- package/nip-ca-server/src/index.ts +0 -157
- package/nip-ca-server/tsconfig.json +0 -13
- package/src/core/anchor-cache.ts +0 -129
- package/src/core/cache.ts +0 -93
- package/src/core/canonical-json.ts +0 -50
- package/src/core/codec.ts +0 -158
- package/src/core/codecs/index.ts +0 -5
- package/src/core/codecs/ncp-codec.ts +0 -170
- package/src/core/codecs/tier1-json-codec.ts +0 -33
- package/src/core/codecs/tier2-msgpack-codec.ts +0 -30
- package/src/core/crypto-provider.ts +0 -47
- package/src/core/exceptions.ts +0 -57
- package/src/core/frame-header.ts +0 -282
- package/src/core/frame-registry.ts +0 -91
- package/src/core/frames.ts +0 -183
- package/src/core/index.ts +0 -10
- package/src/core/registry.ts +0 -28
- package/src/core/status-codes.ts +0 -46
- package/src/index.ts +0 -10
- package/src/ncp/frames/anchor-frame.ts +0 -87
- package/src/ncp/frames/caps-frame.ts +0 -59
- package/src/ncp/frames/diff-frame.ts +0 -69
- package/src/ncp/frames/error-frame.ts +0 -26
- package/src/ncp/frames/hello-frame.ts +0 -50
- package/src/ncp/frames/stream-frame.ts +0 -35
- package/src/ncp/frames.ts +0 -199
- package/src/ncp/handshake.ts +0 -95
- package/src/ncp/index.ts +0 -12
- package/src/ncp/ncp-error-codes.ts +0 -34
- package/src/ncp/ncp-patch-format.ts +0 -16
- package/src/ncp/registry.ts +0 -14
- package/src/ncp/stream-manager.ts +0 -212
- package/src/ndp/frames.ts +0 -124
- package/src/ndp/index.ts +0 -7
- package/src/ndp/ndp-registry.ts +0 -82
- package/src/ndp/registry.ts +0 -12
- package/src/ndp/validator.ts +0 -64
- package/src/nip/frames.ts +0 -106
- package/src/nip/identity.ts +0 -113
- package/src/nip/index.ts +0 -6
- package/src/nip/registry.ts +0 -12
- package/src/nop/client.ts +0 -103
- package/src/nop/frames.ts +0 -181
- package/src/nop/index.ts +0 -7
- package/src/nop/models.ts +0 -79
- package/src/nop/nop-types.ts +0 -208
- package/src/nop/registry.ts +0 -13
- package/src/nwp/client.ts +0 -114
- package/src/nwp/frames.ts +0 -116
- package/src/nwp/index.ts +0 -6
- package/src/nwp/registry.ts +0 -11
- package/src/setup.ts +0 -32
- package/tests/core/anchor-cache.test.ts +0 -242
- package/tests/core/codec.test.ts +0 -205
- package/tests/core/frame-registry.test.ts +0 -46
- package/tests/core.test.ts +0 -327
- package/tests/ncp/diff-binary-bitset.test.ts +0 -107
- package/tests/ncp/e2e-enc-reject.test.ts +0 -93
- package/tests/ncp/err-error-frame.test.ts +0 -152
- package/tests/ncp/frames.test.ts +0 -359
- package/tests/ncp/framing.test.ts +0 -233
- package/tests/ncp/hello-frame.test.ts +0 -122
- package/tests/ncp/inline-anchor.test.ts +0 -88
- package/tests/ncp/security.test.ts +0 -184
- package/tests/ncp/stream-window.test.ts +0 -167
- package/tests/ncp/stream.test.ts +0 -242
- package/tests/ncp/version-negotiation.test.ts +0 -123
- package/tests/ndp.test.ts +0 -271
- package/tests/nip.test.ts +0 -184
- package/tests/nop.test.ts +0 -344
- package/tests/nwp.test.ts +0 -237
- package/tsconfig.json +0 -20
- package/tsup.config.ts +0 -20
- package/vitest.config.ts +0 -10
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
// Copyright 2026 INNO LOTUS PTY LTD
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* NPS NWP — Anchor Node server (Web-standard Fetch handler, zero deps).
|
|
5
|
+
*
|
|
6
|
+
* Server-side counterpart of {@link AnchorNodeClient}, porting the .NET
|
|
7
|
+
* `AnchorNodeMiddleware` wire contract (NPS-AaaS §2, NPS-2 §12) and mirroring
|
|
8
|
+
* the Python `nps_sdk.nwp.anchor_server`. {@link AnchorNodeApp.fetch} takes a
|
|
9
|
+
* standard `Request` and returns a `Response` — mount it on Node (≥18), Deno,
|
|
10
|
+
* Bun, Cloudflare Workers, or any WHATWG-fetch runtime.
|
|
11
|
+
*
|
|
12
|
+
* The business execution behind `/invoke` is delegated to an injected
|
|
13
|
+
* {@link AnchorInvokeHandler} (NOP orchestration is host-supplied), mirroring
|
|
14
|
+
* how the .NET middleware requires an `IAnchorRouter` + `INopOrchestrator`.
|
|
15
|
+
*/
|
|
16
|
+
import { AssuranceLevel } from "../nip/assurance-level.js";
|
|
17
|
+
import { ActionFrame } from "./frames.js";
|
|
18
|
+
import * as ErrorCodes from "./nwp-error-codes.js";
|
|
19
|
+
import * as H from "./http-headers.js";
|
|
20
|
+
import { RepOutcome, } from "./reputation.js";
|
|
21
|
+
// ── Wire constants (NPS-2 §12) ─────────────────────────────────────────────────
|
|
22
|
+
export const TopologyWire = {
|
|
23
|
+
TYPE_SNAPSHOT: "topology.snapshot",
|
|
24
|
+
TYPE_STREAM: "topology.stream",
|
|
25
|
+
SCOPE_CLUSTER: "cluster",
|
|
26
|
+
SCOPE_MEMBER: "member",
|
|
27
|
+
SNAPSHOT_ANCHOR_REF: "nps:system:topology:snapshot",
|
|
28
|
+
};
|
|
29
|
+
// ── Errors ─────────────────────────────────────────────────────────────────────
|
|
30
|
+
export class TopologyProtocolError extends Error {
|
|
31
|
+
nwpErrorCode;
|
|
32
|
+
npsStatus;
|
|
33
|
+
constructor(nwpErrorCode, npsStatus, message) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.nwpErrorCode = nwpErrorCode;
|
|
36
|
+
this.npsStatus = npsStatus;
|
|
37
|
+
this.name = "TopologyProtocolError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export class AnchorActionError extends Error {
|
|
41
|
+
httpStatus;
|
|
42
|
+
npsStatus;
|
|
43
|
+
errorCode;
|
|
44
|
+
details;
|
|
45
|
+
constructor(httpStatus, npsStatus, errorCode, message, details) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.httpStatus = httpStatus;
|
|
48
|
+
this.npsStatus = npsStatus;
|
|
49
|
+
this.errorCode = errorCode;
|
|
50
|
+
this.details = details;
|
|
51
|
+
this.name = "AnchorActionError";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Reference in-memory topology service. */
|
|
55
|
+
export class InMemoryAnchorTopologyService {
|
|
56
|
+
anchorNid;
|
|
57
|
+
members;
|
|
58
|
+
version;
|
|
59
|
+
events;
|
|
60
|
+
constructor(anchorNid, members = [], version = 1, events = []) {
|
|
61
|
+
this.anchorNid = anchorNid;
|
|
62
|
+
this.members = members;
|
|
63
|
+
this.version = version;
|
|
64
|
+
this.events = events;
|
|
65
|
+
}
|
|
66
|
+
async getSnapshot(request) {
|
|
67
|
+
const members = request.include.has("members") ? this.members : [];
|
|
68
|
+
return {
|
|
69
|
+
version: this.version,
|
|
70
|
+
anchor_nid: this.anchorNid,
|
|
71
|
+
cluster_size: this.members.length,
|
|
72
|
+
members,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async *subscribe(_request) {
|
|
76
|
+
for (const ev of this.events)
|
|
77
|
+
yield ev;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export class AllowAllRateLimiter {
|
|
81
|
+
tryAcquire() {
|
|
82
|
+
return { allowed: true };
|
|
83
|
+
}
|
|
84
|
+
release() {
|
|
85
|
+
/* no-op */
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// ── Serialization helpers ──────────────────────────────────────────────────────
|
|
89
|
+
function snapshotToDict(s) {
|
|
90
|
+
// MemberInfo / TopologySnapshot fields are already snake_case wire keys;
|
|
91
|
+
// JSON.stringify drops `undefined` optionals.
|
|
92
|
+
return { ...s };
|
|
93
|
+
}
|
|
94
|
+
function eventToEnvelope(streamId, ev) {
|
|
95
|
+
let seq;
|
|
96
|
+
let payload;
|
|
97
|
+
switch (ev.kind) {
|
|
98
|
+
case "member_joined":
|
|
99
|
+
seq = ev.version;
|
|
100
|
+
payload = ev.member;
|
|
101
|
+
break;
|
|
102
|
+
case "member_left":
|
|
103
|
+
seq = ev.version;
|
|
104
|
+
payload = { nid: ev.nid };
|
|
105
|
+
break;
|
|
106
|
+
case "member_updated":
|
|
107
|
+
seq = ev.version;
|
|
108
|
+
payload = { nid: ev.nid, changes: ev.changes };
|
|
109
|
+
break;
|
|
110
|
+
case "anchor_state":
|
|
111
|
+
seq = ev.version;
|
|
112
|
+
payload = { field: ev.field, details: ev.details };
|
|
113
|
+
break;
|
|
114
|
+
case "resync_required":
|
|
115
|
+
seq = undefined;
|
|
116
|
+
payload = { reason: ev.reason };
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
const raw = JSON.stringify(payload);
|
|
120
|
+
const cgnEst = Math.max(1, Math.floor(new TextEncoder().encode(raw).length / 4));
|
|
121
|
+
const env = {
|
|
122
|
+
stream_id: streamId,
|
|
123
|
+
event_type: ev.kind,
|
|
124
|
+
timestamp: new Date().toISOString(),
|
|
125
|
+
payload,
|
|
126
|
+
cgn_est: cgnEst,
|
|
127
|
+
};
|
|
128
|
+
if (seq !== undefined)
|
|
129
|
+
env["seq"] = seq;
|
|
130
|
+
return env;
|
|
131
|
+
}
|
|
132
|
+
function randomHex(bytes) {
|
|
133
|
+
const buf = new Uint8Array(bytes);
|
|
134
|
+
crypto.getRandomValues(buf);
|
|
135
|
+
return [...buf].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
136
|
+
}
|
|
137
|
+
/** Serialize the (camelCase) reference ReputationPolicy to the snake_case NWM wire form. */
|
|
138
|
+
function reputationPolicyToWire(p) {
|
|
139
|
+
const rule = (r) => {
|
|
140
|
+
const d = { incident: r.incident ?? "*", severity: r.severity ?? ">=minor" };
|
|
141
|
+
if (r.withinDays !== undefined)
|
|
142
|
+
d["within_days"] = r.withinDays;
|
|
143
|
+
if (r.count !== undefined)
|
|
144
|
+
d["count"] = r.count;
|
|
145
|
+
return d;
|
|
146
|
+
};
|
|
147
|
+
const out = {};
|
|
148
|
+
if (p.enabled !== undefined)
|
|
149
|
+
out["enabled"] = p.enabled;
|
|
150
|
+
if (p.logSources !== undefined)
|
|
151
|
+
out["log_sources"] = p.logSources;
|
|
152
|
+
if (p.minAssuranceLevel !== undefined)
|
|
153
|
+
out["min_assurance_level"] = p.minAssuranceLevel;
|
|
154
|
+
if (p.cacheTtlSeconds !== undefined)
|
|
155
|
+
out["cache_ttl_seconds"] = p.cacheTtlSeconds;
|
|
156
|
+
if (p.banTtlSeconds !== undefined)
|
|
157
|
+
out["ban_ttl_seconds"] = p.banTtlSeconds;
|
|
158
|
+
if (p.onLogUnavailable !== undefined)
|
|
159
|
+
out["on_log_unavailable"] = p.onLogUnavailable;
|
|
160
|
+
if (p.throttleOn !== undefined)
|
|
161
|
+
out["throttle_on"] = p.throttleOn.map(rule);
|
|
162
|
+
if (p.rejectOn !== undefined)
|
|
163
|
+
out["reject_on"] = p.rejectOn.map(rule);
|
|
164
|
+
if (p.banOn !== undefined)
|
|
165
|
+
out["ban_on"] = p.banOn.map(rule);
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
// ── The Fetch app ──────────────────────────────────────────────────────────────
|
|
169
|
+
/** Anchor sub-paths the node serves; an unknown sub-path is a 404, not a 401. */
|
|
170
|
+
const KNOWN_ROUTES = new Set([
|
|
171
|
+
"/.nwm", "/.nwm/", "/.schema", "/.schema/", "/actions", "/actions/",
|
|
172
|
+
"/invoke", "/invoke/", "/query", "/query/", "/subscribe", "/subscribe/",
|
|
173
|
+
]);
|
|
174
|
+
export class AnchorNodeApp {
|
|
175
|
+
opt;
|
|
176
|
+
prefix;
|
|
177
|
+
handler;
|
|
178
|
+
topology;
|
|
179
|
+
evaluator;
|
|
180
|
+
limiter;
|
|
181
|
+
nwmJson;
|
|
182
|
+
actionsJson;
|
|
183
|
+
constructor(options, deps = {}) {
|
|
184
|
+
this.opt = {
|
|
185
|
+
requireAuth: true,
|
|
186
|
+
requireTopologyCapability: false,
|
|
187
|
+
defaultTimeoutMs: 30_000,
|
|
188
|
+
maxTimeoutMs: 300_000,
|
|
189
|
+
defaultTokenBudget: 0,
|
|
190
|
+
cgnLimit: 0,
|
|
191
|
+
autoInjectTraceContext: true,
|
|
192
|
+
...options,
|
|
193
|
+
};
|
|
194
|
+
this.prefix = options.pathPrefix.replace(/\/+$/, "");
|
|
195
|
+
this.handler = deps.invokeHandler;
|
|
196
|
+
this.topology = deps.topologyService;
|
|
197
|
+
this.evaluator = deps.reputationEvaluator;
|
|
198
|
+
this.limiter = deps.rateLimiter ?? new AllowAllRateLimiter();
|
|
199
|
+
this.nwmJson = JSON.stringify(this.buildManifest());
|
|
200
|
+
this.actionsJson = JSON.stringify({ actions: this.actionsDict() });
|
|
201
|
+
}
|
|
202
|
+
/** WHATWG Fetch handler. */
|
|
203
|
+
fetch = async (req) => {
|
|
204
|
+
const url = new URL(req.url);
|
|
205
|
+
const path = url.pathname;
|
|
206
|
+
if (!path.startsWith(this.prefix)) {
|
|
207
|
+
return this.errorResponse(404, "NPS-CLIENT-NOT-FOUND", ErrorCodes.NWP_ACTION_NOT_FOUND, "no NWP node at this path.");
|
|
208
|
+
}
|
|
209
|
+
const sub = path.slice(this.prefix.length);
|
|
210
|
+
// Resolve the route BEFORE the auth gate: an unknown sub-path is a 404 regardless of auth, so
|
|
211
|
+
// a missing X-NWP-Agent on a non-existent route does not leak a 401 (auth state) for a path
|
|
212
|
+
// that has no resource. Known routes fall through to the auth check below.
|
|
213
|
+
if (!KNOWN_ROUTES.has(sub)) {
|
|
214
|
+
return this.errorResponse(404, "NPS-CLIENT-NOT-FOUND", ErrorCodes.NWP_ACTION_NOT_FOUND, "unknown anchor sub-path.");
|
|
215
|
+
}
|
|
216
|
+
if (this.opt.requireAuth && !req.headers.get(H.HDR_AGENT)) {
|
|
217
|
+
return this.errorResponse(401, "NPS-AUTH-UNAUTHENTICATED", ErrorCodes.NWP_AUTH_NID_SCOPE_VIOLATION, "X-NWP-Agent header is required.");
|
|
218
|
+
}
|
|
219
|
+
switch (true) {
|
|
220
|
+
case sub === "/.nwm" || sub === "/.nwm/":
|
|
221
|
+
return new Response(this.nwmJson, {
|
|
222
|
+
status: 200,
|
|
223
|
+
headers: { "content-type": H.MIME_MANIFEST, [H.HDR_NODE_TYPE.toLowerCase()]: "anchor" },
|
|
224
|
+
});
|
|
225
|
+
case sub === "/.schema" || sub === "/.schema/" || sub === "/actions" || sub === "/actions/":
|
|
226
|
+
return new Response(this.actionsJson, { status: 200, headers: { "content-type": "application/json" } });
|
|
227
|
+
case sub === "/invoke" || sub === "/invoke/":
|
|
228
|
+
if (req.method !== "POST")
|
|
229
|
+
return new Response(null, { status: 405 });
|
|
230
|
+
return this.handleInvoke(req);
|
|
231
|
+
case sub === "/query" || sub === "/query/":
|
|
232
|
+
if (req.method !== "POST")
|
|
233
|
+
return new Response(null, { status: 405 });
|
|
234
|
+
return this.handleQuery(req);
|
|
235
|
+
case sub === "/subscribe" || sub === "/subscribe/":
|
|
236
|
+
if (req.method !== "POST")
|
|
237
|
+
return new Response(null, { status: 405 });
|
|
238
|
+
return this.handleSubscribe(req);
|
|
239
|
+
default:
|
|
240
|
+
return this.errorResponse(404, "NPS-CLIENT-NOT-FOUND", ErrorCodes.NWP_ACTION_NOT_FOUND, "unknown anchor sub-path.");
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
// ── /query ───────────────────────────────────────────────────────────────────
|
|
244
|
+
async handleQuery(req) {
|
|
245
|
+
const capCheck = this.checkTopologyCapability(req);
|
|
246
|
+
if (capCheck)
|
|
247
|
+
return capCheck;
|
|
248
|
+
let body;
|
|
249
|
+
try {
|
|
250
|
+
body = (await req.json());
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
return this.errorResponse(400, "NPS-CLIENT-BAD-REQUEST", ErrorCodes.NWP_QUERY_FILTER_INVALID, String(e));
|
|
254
|
+
}
|
|
255
|
+
if (body["type"] !== TopologyWire.TYPE_SNAPSHOT) {
|
|
256
|
+
const t = body["type"];
|
|
257
|
+
return this.errorResponse(501, "NPS-SERVER-UNSUPPORTED", ErrorCodes.NWP_RESERVED_TYPE_UNSUPPORTED, t == null ? "Anchor /query requires a reserved type per NPS-2 §12."
|
|
258
|
+
: `Reserved query type '${String(t)}' is not implemented by this Anchor Node.`);
|
|
259
|
+
}
|
|
260
|
+
if (!this.topology) {
|
|
261
|
+
return this.errorResponse(501, "NPS-SERVER-UNSUPPORTED", ErrorCodes.NWP_NODE_UNAVAILABLE, "topology.snapshot is not available — no topology service registered.");
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
const request = parseSnapshotRequest(body);
|
|
265
|
+
const snapshot = await this.topology.getSnapshot(request);
|
|
266
|
+
const caps = { anchor_ref: TopologyWire.SNAPSHOT_ANCHOR_REF, count: 1, data: [snapshotToDict(snapshot)] };
|
|
267
|
+
return new Response(JSON.stringify(caps), {
|
|
268
|
+
status: 200,
|
|
269
|
+
headers: {
|
|
270
|
+
"content-type": H.MIME_CAPSULE,
|
|
271
|
+
[H.HDR_NODE_TYPE.toLowerCase()]: "anchor",
|
|
272
|
+
[H.HDR_SCHEMA.toLowerCase()]: TopologyWire.SNAPSHOT_ANCHOR_REF,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
catch (e) {
|
|
277
|
+
if (e instanceof TopologyProtocolError) {
|
|
278
|
+
const status = e.npsStatus === "NPS-AUTH-FORBIDDEN" ? 403 : 400;
|
|
279
|
+
return this.errorResponse(status, e.npsStatus, e.nwpErrorCode, e.message);
|
|
280
|
+
}
|
|
281
|
+
throw e;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// ── /subscribe ───────────────────────────────────────────────────────────────
|
|
285
|
+
async handleSubscribe(req) {
|
|
286
|
+
const capCheck = this.checkTopologyCapability(req);
|
|
287
|
+
if (capCheck)
|
|
288
|
+
return capCheck;
|
|
289
|
+
let body;
|
|
290
|
+
try {
|
|
291
|
+
body = (await req.json());
|
|
292
|
+
}
|
|
293
|
+
catch (e) {
|
|
294
|
+
return this.errorResponse(400, "NPS-CLIENT-BAD-REQUEST", ErrorCodes.NWP_QUERY_FILTER_INVALID, String(e));
|
|
295
|
+
}
|
|
296
|
+
if (body["type"] !== TopologyWire.TYPE_STREAM) {
|
|
297
|
+
const t = body["type"];
|
|
298
|
+
return this.errorResponse(501, "NPS-SERVER-UNSUPPORTED", ErrorCodes.NWP_RESERVED_TYPE_UNSUPPORTED, t == null ? "Anchor /subscribe requires a reserved type per NPS-2 §12."
|
|
299
|
+
: `Reserved subscribe type '${String(t)}' is not implemented by this Anchor Node.`);
|
|
300
|
+
}
|
|
301
|
+
if (!this.topology) {
|
|
302
|
+
return this.errorResponse(501, "NPS-SERVER-UNSUPPORTED", ErrorCodes.NWP_NODE_UNAVAILABLE, "topology.stream is not available — no topology service registered.");
|
|
303
|
+
}
|
|
304
|
+
let request;
|
|
305
|
+
let streamId;
|
|
306
|
+
try {
|
|
307
|
+
[request, streamId] = parseStreamRequest(body);
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
if (e instanceof TopologyProtocolError) {
|
|
311
|
+
const status = e.npsStatus === "NPS-AUTH-FORBIDDEN" ? 403 : 400;
|
|
312
|
+
return this.errorResponse(status, e.npsStatus, e.nwpErrorCode, e.message);
|
|
313
|
+
}
|
|
314
|
+
throw e;
|
|
315
|
+
}
|
|
316
|
+
const topology = this.topology;
|
|
317
|
+
const encoder = new TextEncoder();
|
|
318
|
+
const stream = new ReadableStream({
|
|
319
|
+
async start(controller) {
|
|
320
|
+
const writeLine = (obj) => controller.enqueue(encoder.encode(JSON.stringify(obj) + "\n"));
|
|
321
|
+
writeLine({ kind: "ack", stream_id: streamId, status: "subscribed", last_seq: 0,
|
|
322
|
+
resumed: request.sinceVersion !== undefined });
|
|
323
|
+
try {
|
|
324
|
+
for await (const ev of topology.subscribe(request)) {
|
|
325
|
+
writeLine(eventToEnvelope(streamId, ev));
|
|
326
|
+
if (ev.kind === "resync_required")
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch (e) {
|
|
331
|
+
if (e instanceof TopologyProtocolError) {
|
|
332
|
+
writeLine({ status: e.npsStatus, error: e.nwpErrorCode, message: e.message });
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
finally {
|
|
336
|
+
controller.close();
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
return new Response(stream, {
|
|
341
|
+
status: 200,
|
|
342
|
+
headers: { "content-type": H.MIME_CAPSULE, [H.HDR_NODE_TYPE.toLowerCase()]: "anchor" },
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
// ── /invoke ──────────────────────────────────────────────────────────────────
|
|
346
|
+
async handleInvoke(req) {
|
|
347
|
+
let frame;
|
|
348
|
+
try {
|
|
349
|
+
frame = ActionFrame.fromDict((await req.json()));
|
|
350
|
+
}
|
|
351
|
+
catch (e) {
|
|
352
|
+
return this.errorResponse(400, "NPS-CLIENT-BAD-REQUEST", ErrorCodes.NWP_ACTION_PARAMS_INVALID, String(e));
|
|
353
|
+
}
|
|
354
|
+
const spec = this.opt.actions[frame.actionId];
|
|
355
|
+
if (!spec) {
|
|
356
|
+
return this.errorResponse(404, "NPS-CLIENT-NOT-FOUND", ErrorCodes.NWP_ACTION_NOT_FOUND, `Unknown action_id '${frame.actionId}'.`);
|
|
357
|
+
}
|
|
358
|
+
if (frame.async_ && !spec.async_) {
|
|
359
|
+
return this.errorResponse(400, "NPS-CLIENT-BAD-REQUEST", ErrorCodes.NWP_ACTION_PARAMS_INVALID, `action '${frame.actionId}' does not support async execution.`);
|
|
360
|
+
}
|
|
361
|
+
const agentNid = req.headers.get(H.HDR_AGENT) ?? undefined;
|
|
362
|
+
const consumerKey = agentNid ?? "anonymous";
|
|
363
|
+
const effectiveTimeout = this.clampTimeout(frame.timeoutMs ?? 0, spec);
|
|
364
|
+
const budgetCgn = this.readEffectiveBudget(req);
|
|
365
|
+
const cgnCostHint = spec.estimatedCgn ?? 0;
|
|
366
|
+
const rate = this.limiter.tryAcquire(consumerKey, cgnCostHint);
|
|
367
|
+
if (!rate.allowed) {
|
|
368
|
+
const extra = rate.retryAfterSeconds ? { "retry-after": String(rate.retryAfterSeconds) } : undefined;
|
|
369
|
+
return this.errorResponse(429, "NPS-LIMIT-RATE", ErrorCodes.NWP_BUDGET_EXCEEDED, rate.reason ?? "rate limit exceeded.", undefined, extra);
|
|
370
|
+
}
|
|
371
|
+
try {
|
|
372
|
+
const assurance = extractIdentAssurance(req);
|
|
373
|
+
const policy = this.opt.reputationPolicy;
|
|
374
|
+
if (policy && (policy.enabled ?? true) && this.evaluator) {
|
|
375
|
+
let decision;
|
|
376
|
+
try {
|
|
377
|
+
decision = await this.evaluator.evaluate(consumerKey, assurance.wire, policy);
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
return this.errorResponse(500, "NPS-SERVER-INTERNAL", ErrorCodes.NWP_NODE_UNAVAILABLE, "reputation evaluation failed.");
|
|
381
|
+
}
|
|
382
|
+
const repResp = this.applyReputation(decision, policy, assurance);
|
|
383
|
+
if (repResp)
|
|
384
|
+
return repResp;
|
|
385
|
+
}
|
|
386
|
+
if (budgetCgn > 0 && cgnCostHint > 0 && cgnCostHint > budgetCgn) {
|
|
387
|
+
return this.errorResponse(400, "NPS-CLIENT-REQUEST-TOO-LARGE", ErrorCodes.NWP_CGN_LIMIT_EXCEEDED, `estimated CGN ${cgnCostHint} exceeds effective budget ${budgetCgn}.`, { effective_budget: budgetCgn, estimated_cgn: cgnCostHint });
|
|
388
|
+
}
|
|
389
|
+
if (!this.handler) {
|
|
390
|
+
return this.errorResponse(501, "NPS-SERVER-UNSUPPORTED", ErrorCodes.NWP_NODE_UNAVAILABLE, "no invoke handler registered on this Anchor Node.");
|
|
391
|
+
}
|
|
392
|
+
const traceId = this.opt.autoInjectTraceContext ? randomHex(16) : undefined;
|
|
393
|
+
const spanId = this.opt.autoInjectTraceContext ? randomHex(8) : undefined;
|
|
394
|
+
const ctx = { agentNid, effectiveTimeoutMs: effectiveTimeout, budgetCgn, traceId, spanId };
|
|
395
|
+
if (frame.async_) {
|
|
396
|
+
const taskId = randomHex(16);
|
|
397
|
+
void Promise.resolve(this.handler(frame.actionId, frame, ctx)).catch(() => undefined);
|
|
398
|
+
return new Response(JSON.stringify({ task_id: taskId, status: "pending", poll_url: `${this.prefix}/invoke` }), { status: 202, headers: { "content-type": "application/json", [H.HDR_NODE_TYPE.toLowerCase()]: "anchor" } });
|
|
399
|
+
}
|
|
400
|
+
let result;
|
|
401
|
+
try {
|
|
402
|
+
result = await this.handler(frame.actionId, frame, ctx);
|
|
403
|
+
}
|
|
404
|
+
catch (e) {
|
|
405
|
+
if (e instanceof AnchorActionError) {
|
|
406
|
+
return this.errorResponse(e.httpStatus, e.npsStatus, e.errorCode, e.message, e.details);
|
|
407
|
+
}
|
|
408
|
+
return this.errorResponse(500, "NPS-SERVER-INTERNAL", ErrorCodes.NWP_NODE_UNAVAILABLE, "anchor task execution failed.");
|
|
409
|
+
}
|
|
410
|
+
const caps = {
|
|
411
|
+
anchor_ref: spec.resultAnchor ?? "",
|
|
412
|
+
count: result == null ? 0 : 1,
|
|
413
|
+
data: result == null ? [] : [result],
|
|
414
|
+
};
|
|
415
|
+
const headers = {
|
|
416
|
+
"content-type": H.MIME_CAPSULE,
|
|
417
|
+
[H.HDR_NODE_TYPE.toLowerCase()]: "anchor",
|
|
418
|
+
};
|
|
419
|
+
if (spec.resultAnchor)
|
|
420
|
+
headers[H.HDR_SCHEMA.toLowerCase()] = spec.resultAnchor;
|
|
421
|
+
if (spec.estimatedCgn)
|
|
422
|
+
headers[H.HDR_TOKENS.toLowerCase()] = String(spec.estimatedCgn);
|
|
423
|
+
return new Response(JSON.stringify(caps), { status: 200, headers });
|
|
424
|
+
}
|
|
425
|
+
finally {
|
|
426
|
+
this.limiter.release(consumerKey);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
applyReputation(decision, policy, assurance) {
|
|
430
|
+
// The reference evaluator (nwp/reputation.ts) carries the matched rule rather than separate
|
|
431
|
+
// incident/severity fields; derive them from matchedRule.
|
|
432
|
+
const incident = decision.matchedRule?.incident;
|
|
433
|
+
const severity = decision.matchedRule?.severity;
|
|
434
|
+
switch (decision.outcome) {
|
|
435
|
+
case RepOutcome.Accept:
|
|
436
|
+
return null;
|
|
437
|
+
case RepOutcome.Ban: {
|
|
438
|
+
const details = incident !== undefined
|
|
439
|
+
? { matched_incident: incident, matched_severity: severity }
|
|
440
|
+
: undefined;
|
|
441
|
+
return this.errorResponse(403, "NPS-AUTH-FORBIDDEN", ErrorCodes.NWP_REPUTATION_BANNED, incident !== undefined
|
|
442
|
+
? `Request rejected: ${incident} (${severity}) — NID temporarily banned.`
|
|
443
|
+
: "Request rejected: NID temporarily banned.", details);
|
|
444
|
+
}
|
|
445
|
+
case RepOutcome.Reject:
|
|
446
|
+
if (decision.errorCode === ErrorCodes.NWP_AUTH_ASSURANCE_TOO_LOW) {
|
|
447
|
+
return this.errorResponse(403, "NPS-AUTH-FORBIDDEN", ErrorCodes.NWP_AUTH_ASSURANCE_TOO_LOW, `Assurance level too low: requires '${policy.minAssuranceLevel ?? "anonymous"}', caller declared '${assurance.wire}'.`, { matched_incident: null, hint: this.opt.assuranceHintUrl });
|
|
448
|
+
}
|
|
449
|
+
return this.errorResponse(403, "NPS-AUTH-FORBIDDEN", ErrorCodes.NWP_REPUTATION_REJECTED, incident !== undefined
|
|
450
|
+
? `Request rejected: ${incident} (${severity}).`
|
|
451
|
+
: "Request rejected by reputation policy.", incident !== undefined
|
|
452
|
+
? { matched_incident: incident, matched_severity: severity }
|
|
453
|
+
: undefined);
|
|
454
|
+
case RepOutcome.Throttle:
|
|
455
|
+
return this.errorResponse(429, "NPS-CLIENT-RATE-LIMITED", ErrorCodes.NWP_REPUTATION_THROTTLED, incident !== undefined
|
|
456
|
+
? `Request rate-limited: ${incident} (${severity}).`
|
|
457
|
+
: "Request rate-limited by reputation policy.", incident !== undefined
|
|
458
|
+
? { matched_incident: incident, matched_severity: severity }
|
|
459
|
+
: undefined, { "retry-after": "60" });
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// ── Gates / helpers ────────────────────────────────────────────────────────────
|
|
463
|
+
checkTopologyCapability(req) {
|
|
464
|
+
if (!this.opt.requireTopologyCapability)
|
|
465
|
+
return null;
|
|
466
|
+
const raw = req.headers.get(H.HDR_CAPABILITIES) ?? "";
|
|
467
|
+
const caps = new Set(raw.split(",").map((c) => c.trim().toLowerCase()).filter(Boolean));
|
|
468
|
+
if (caps.has("topology:read"))
|
|
469
|
+
return null;
|
|
470
|
+
return this.errorResponse(403, "NPS-AUTH-FORBIDDEN", ErrorCodes.NWP_TOPOLOGY_UNAUTHORIZED, "Caller must declare 'topology:read' in X-NWP-Capabilities to access topology endpoints.");
|
|
471
|
+
}
|
|
472
|
+
clampTimeout(requested, spec) {
|
|
473
|
+
const specMax = spec.timeoutMsMax ?? this.opt.maxTimeoutMs;
|
|
474
|
+
const hardMax = Math.min(specMax, this.opt.maxTimeoutMs);
|
|
475
|
+
if (requested <= 0)
|
|
476
|
+
return spec.timeoutMsDefault ?? this.opt.defaultTimeoutMs;
|
|
477
|
+
return Math.min(requested, hardMax);
|
|
478
|
+
}
|
|
479
|
+
readEffectiveBudget(req) {
|
|
480
|
+
const raw = req.headers.get(H.HDR_BUDGET);
|
|
481
|
+
let agentBudget = this.opt.defaultTokenBudget;
|
|
482
|
+
if (raw !== null) {
|
|
483
|
+
const parsed = Number.parseInt(raw, 10);
|
|
484
|
+
if (!Number.isNaN(parsed))
|
|
485
|
+
agentBudget = parsed;
|
|
486
|
+
}
|
|
487
|
+
const cgnLimit = this.opt.cgnLimit;
|
|
488
|
+
if (cgnLimit === 0)
|
|
489
|
+
return agentBudget;
|
|
490
|
+
if (agentBudget === 0)
|
|
491
|
+
return cgnLimit;
|
|
492
|
+
return Math.min(cgnLimit, agentBudget);
|
|
493
|
+
}
|
|
494
|
+
errorResponse(httpStatus, npsStatus, errorCode, message, details, extraHeaders) {
|
|
495
|
+
const env = { status: npsStatus, error: errorCode, message };
|
|
496
|
+
if (details !== undefined)
|
|
497
|
+
env["details"] = details;
|
|
498
|
+
return new Response(JSON.stringify(env), {
|
|
499
|
+
status: httpStatus,
|
|
500
|
+
headers: { "content-type": "application/json", ...(extraHeaders ?? {}) },
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
// ── Manifest ───────────────────────────────────────────────────────────────────
|
|
504
|
+
actionsDict() {
|
|
505
|
+
const out = {};
|
|
506
|
+
for (const [actionId, spec] of Object.entries(this.opt.actions)) {
|
|
507
|
+
const entry = { action_id: actionId, async: spec.async_ ?? false };
|
|
508
|
+
if (spec.description !== undefined)
|
|
509
|
+
entry["description"] = spec.description;
|
|
510
|
+
if (spec.paramsAnchor !== undefined)
|
|
511
|
+
entry["params_anchor"] = spec.paramsAnchor;
|
|
512
|
+
if (spec.resultAnchor !== undefined)
|
|
513
|
+
entry["result_anchor"] = spec.resultAnchor;
|
|
514
|
+
if (spec.estimatedCgn !== undefined)
|
|
515
|
+
entry["cgn_est"] = spec.estimatedCgn;
|
|
516
|
+
if (spec.timeoutMsDefault !== undefined)
|
|
517
|
+
entry["timeout_ms_default"] = spec.timeoutMsDefault;
|
|
518
|
+
if (spec.timeoutMsMax !== undefined)
|
|
519
|
+
entry["timeout_ms_max"] = spec.timeoutMsMax;
|
|
520
|
+
if (spec.requiredCapability !== undefined)
|
|
521
|
+
entry["required_capability"] = spec.requiredCapability;
|
|
522
|
+
out[actionId] = entry;
|
|
523
|
+
}
|
|
524
|
+
return out;
|
|
525
|
+
}
|
|
526
|
+
buildManifest() {
|
|
527
|
+
const o = this.opt;
|
|
528
|
+
const base = this.prefix;
|
|
529
|
+
const m = { nwp: "0.4", node_id: o.nodeId, node_type: "anchor" };
|
|
530
|
+
if (o.displayName !== undefined)
|
|
531
|
+
m["display_name"] = o.displayName;
|
|
532
|
+
m["wire_formats"] = ["ncp-capsule", "json"];
|
|
533
|
+
m["preferred_format"] = "json";
|
|
534
|
+
m["capabilities"] = {
|
|
535
|
+
query: false, stream: false, subscribe: false, vector_search: false,
|
|
536
|
+
token_budget_hint: true, ext_frame: false,
|
|
537
|
+
};
|
|
538
|
+
const auth = {
|
|
539
|
+
required: o.requireAuth,
|
|
540
|
+
identity_type: o.requireAuth ? "nip-cert" : "none",
|
|
541
|
+
};
|
|
542
|
+
if (o.requiredCapabilities !== undefined)
|
|
543
|
+
auth["required_capabilities"] = o.requiredCapabilities;
|
|
544
|
+
m["auth"] = auth;
|
|
545
|
+
m["endpoints"] = { invoke: `${base}/invoke`, schema: `${base}/.schema` };
|
|
546
|
+
if (o.cgnLimit > 0)
|
|
547
|
+
m["token_budget"] = { cgn_limit: o.cgnLimit, profile: "cgn.v1" };
|
|
548
|
+
if (o.rateLimits !== undefined)
|
|
549
|
+
m["rate_limits"] = o.rateLimits;
|
|
550
|
+
if (o.reputationPolicy && (o.reputationPolicy.enabled ?? true)) {
|
|
551
|
+
m["reputation_policy"] = reputationPolicyToWire(o.reputationPolicy);
|
|
552
|
+
}
|
|
553
|
+
if (o.trustAnchors && o.trustAnchors.length > 0)
|
|
554
|
+
m["trust_anchors"] = o.trustAnchors;
|
|
555
|
+
m["actions"] = Object.values(this.actionsDict());
|
|
556
|
+
return m;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
// ── Request parsing ────────────────────────────────────────────────────────────
|
|
560
|
+
function parseSnapshotRequest(body) {
|
|
561
|
+
const topo = body["topology"];
|
|
562
|
+
if (typeof topo !== "object" || topo === null) {
|
|
563
|
+
throw new TopologyProtocolError(ErrorCodes.NWP_TOPOLOGY_UNSUPPORTED_SCOPE, "NPS-CLIENT-BAD-PARAM", "topology.snapshot requires a 'topology' object per NPS-2 §12.1.");
|
|
564
|
+
}
|
|
565
|
+
const t = topo;
|
|
566
|
+
const scope = parseScope(t);
|
|
567
|
+
const include = parseInclude(t);
|
|
568
|
+
const depth = parseDepth(t);
|
|
569
|
+
const targetNid = typeof t["target_nid"] === "string" ? t["target_nid"] : undefined;
|
|
570
|
+
if (scope === TopologyWire.SCOPE_MEMBER && !targetNid) {
|
|
571
|
+
throw new TopologyProtocolError(ErrorCodes.NWP_TOPOLOGY_UNSUPPORTED_SCOPE, "NPS-CLIENT-BAD-PARAM", 'topology.target_nid is required when topology.scope = "member".');
|
|
572
|
+
}
|
|
573
|
+
return { scope, include, depth, targetNid };
|
|
574
|
+
}
|
|
575
|
+
function parseStreamRequest(body) {
|
|
576
|
+
const topo = body["topology"];
|
|
577
|
+
if (typeof topo !== "object" || topo === null) {
|
|
578
|
+
throw new TopologyProtocolError(ErrorCodes.NWP_TOPOLOGY_UNSUPPORTED_SCOPE, "NPS-CLIENT-BAD-PARAM", "topology.stream requires a 'topology' object per NPS-2 §12.2.");
|
|
579
|
+
}
|
|
580
|
+
const t = topo;
|
|
581
|
+
const scope = parseScope(t);
|
|
582
|
+
let filter;
|
|
583
|
+
const f = t["filter"];
|
|
584
|
+
if (typeof f === "object" && f !== null) {
|
|
585
|
+
validateFilterKeys(f);
|
|
586
|
+
const fo = f;
|
|
587
|
+
filter = {
|
|
588
|
+
tags_any: fo["tags_any"],
|
|
589
|
+
tags_all: fo["tags_all"],
|
|
590
|
+
node_roles: fo["node_roles"],
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
let since = t["since_version"];
|
|
594
|
+
if (since === undefined)
|
|
595
|
+
since = body["resume_from_seq"];
|
|
596
|
+
const sinceVersion = typeof since === "number" ? since : undefined;
|
|
597
|
+
let streamId = body["stream_id"];
|
|
598
|
+
if (typeof streamId !== "string" || !streamId)
|
|
599
|
+
streamId = randomHex(16);
|
|
600
|
+
return [{ scope, filter, sinceVersion }, streamId];
|
|
601
|
+
}
|
|
602
|
+
function parseScope(t) {
|
|
603
|
+
const s = t["scope"];
|
|
604
|
+
if (s === undefined)
|
|
605
|
+
return TopologyWire.SCOPE_CLUSTER;
|
|
606
|
+
if (s === TopologyWire.SCOPE_CLUSTER || s === TopologyWire.SCOPE_MEMBER)
|
|
607
|
+
return s;
|
|
608
|
+
throw new TopologyProtocolError(ErrorCodes.NWP_TOPOLOGY_UNSUPPORTED_SCOPE, "NPS-CLIENT-BAD-PARAM", `unknown topology.scope '${String(s)}'.`);
|
|
609
|
+
}
|
|
610
|
+
function parseInclude(t) {
|
|
611
|
+
const inc = t["include"];
|
|
612
|
+
if (!Array.isArray(inc))
|
|
613
|
+
return new Set(["members"]);
|
|
614
|
+
const known = new Set(["members", "capabilities", "tags", "metrics"]);
|
|
615
|
+
const flags = new Set(inc.filter((v) => typeof v === "string" && known.has(v)));
|
|
616
|
+
return flags.size ? flags : new Set(["members"]);
|
|
617
|
+
}
|
|
618
|
+
function parseDepth(t) {
|
|
619
|
+
const d = t["depth"];
|
|
620
|
+
if (typeof d === "number" && d > 0)
|
|
621
|
+
return d;
|
|
622
|
+
return 1;
|
|
623
|
+
}
|
|
624
|
+
function validateFilterKeys(filterObj) {
|
|
625
|
+
for (const key of Object.keys(filterObj)) {
|
|
626
|
+
if (key === "tags_any" || key === "tags_all" || key === "node_roles")
|
|
627
|
+
continue;
|
|
628
|
+
if (key === "node_kind") {
|
|
629
|
+
throw new TopologyProtocolError(ErrorCodes.NWP_TOPOLOGY_FILTER_UNSUPPORTED, "NPS-CLIENT-BAD-PARAM", "topology.filter.node_kind expired after alpha.5; use node_roles.");
|
|
630
|
+
}
|
|
631
|
+
throw new TopologyProtocolError(ErrorCodes.NWP_TOPOLOGY_FILTER_UNSUPPORTED, "NPS-CLIENT-BAD-PARAM", `topology.filter key '${key}' is not recognized.`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
function extractIdentAssurance(req) {
|
|
635
|
+
const raw = req.headers.get(H.HDR_IDENT);
|
|
636
|
+
if (!raw)
|
|
637
|
+
return AssuranceLevel.ANONYMOUS;
|
|
638
|
+
try {
|
|
639
|
+
const doc = JSON.parse(raw);
|
|
640
|
+
const lvl = doc["assurance_level"];
|
|
641
|
+
if (typeof lvl === "string")
|
|
642
|
+
return AssuranceLevel.fromWire(lvl);
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
/* malformed — fall through */
|
|
646
|
+
}
|
|
647
|
+
return AssuranceLevel.ANONYMOUS;
|
|
648
|
+
}
|
|
649
|
+
//# sourceMappingURL=anchor-server.js.map
|