+
NHP Relay Transport Test
+
Browser → HTTP Relay → NHP Server (UDP) → Relay → Browser
+
+
+
+
⚙ Configuration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
▶ Sequence
+
+
+
+
Initialize NHPAgent (load key pair)
+
+
+
+
Set identity & add server
+
+
+
+
Build KNK packet (ECDH + AEAD encrypt)
+
+
+
+
POST → Relay → NHP Server (UDP)
+
+
+
+
Receive & parse ACK/COK response
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nhp-js/main.js b/nhp-js/main.js
index d1bc672..d1f006a 100644
--- a/nhp-js/main.js
+++ b/nhp-js/main.js
@@ -1,11 +1,63 @@
-import {buildNHPPacket, parseNHPPacket} from "./dist/nhp-js-lib.js"
-
-const privateKeyBase641 = "QL2U9Ag18Fqdc1Bpw4wx/sNJbFqq36rfhtkPm2WId1c=";
-const publicKeyBase641 = "Z0QA6bPlRpq8xWsVFe8NGR43bjGHjK0mX7CT1daudAA=";
-const privateKeyBase642 = "oLqkmcl6wVqUaOOrGo4xvpaUkuCThgM37DUZKGExFVE=";
-const publicKeyBase642 = "E3o8gUUxI5dUAUiEOi6SvgV9bWKmjpWWQaOO6GChO2A=";
-const packet = await buildNHPPacket(2, privateKeyBase641, publicKeyBase641, publicKeyBase642, "this is a test packet", true, "")
-console.log("packet: ", packet)
-
-const obj = await parseNHPPacket(packet, privateKeyBase642, publicKeyBase642, publicKeyBase641)
-console.log("obj: ", obj)
+import { buildNHPPacket, parseNHPPacket, NHP_PACKET_TYPES } from "./dist/index.js"
+
+const agentPrivKey = "QL2U9Ag18Fqdc1Bpw4wx/sNJbFqq36rfhtkPm2WId1c=";
+const agentPubKey = "Z0QA6bPlRpq8xWsVFe8NGR43bjGHjK0mX7CT1daudAA=";
+const serverPrivKey = "oLqkmcl6wVqUaOOrGo4xvpaUkuCThgM37DUZKGExFVE=";
+const serverPubKey = "E3o8gUUxI5dUAUiEOi6SvgV9bWKmjpWWQaOO6GChO2A=";
+
+console.group("=== CURVE25519 full round-trip ===");
+
+// Step 1: Agent → Server (KNK)
+console.group("Step 1: Agent builds KNK");
+const knockPayload = JSON.stringify({
+ usrId: "user@example.com",
+ devId: "device-001",
+ aspId: "demo",
+ resId: "resource-1"
+});
+const knkPacket = await buildNHPPacket(
+ NHP_PACKET_TYPES.KNK,
+ agentPrivKey, agentPubKey,
+ serverPubKey,
+ knockPayload, true, "curve25519"
+);
+console.log("KNK packet:", knkPacket.length, "bytes");
+console.groupEnd();
+
+// Step 2: Server → Agent (ACK) — server replies with access grant
+console.group("Step 2: Server builds ACK");
+const ackPayload = JSON.stringify({
+ errCode: "",
+ resHost: { "demo": "192.168.1.100:8080" },
+ opnTime: 300,
+ agentAddr: "1.2.3.4:12345"
+});
+const ackPacket = await buildNHPPacket(
+ NHP_PACKET_TYPES.ACK,
+ serverPrivKey, serverPubKey,
+ agentPubKey,
+ ackPayload, true, "curve25519"
+);
+console.log("ACK packet:", ackPacket.length, "bytes");
+console.groupEnd();
+
+// Step 3: Agent parses ACK
+console.group("Step 3: Agent parses ACK");
+try {
+ const parsed = await parseNHPPacket(
+ ackPacket,
+ agentPrivKey, agentPubKey,
+ serverPubKey
+ );
+ console.log("Packet type:", parsed.type, "(expected 2 = ACK)");
+ const msg = JSON.parse(parsed.message);
+ console.log("Access granted!");
+ console.log(" Resource host:", msg.resHost);
+ console.log(" Open time:", msg.opnTime, "seconds");
+ console.log(" Agent address:", msg.agentAddr);
+} catch (err) {
+ console.error("Parse failed:", err.message);
+}
+console.groupEnd();
+
+console.groupEnd();
diff --git a/nhp-js/package-lock.json b/nhp-js/package-lock.json
index 664f1e7..5c732cc 100644
--- a/nhp-js/package-lock.json
+++ b/nhp-js/package-lock.json
@@ -1417,7 +1417,6 @@
"integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"json-schema-traverse": "^1.0.0",
@@ -1669,7 +1668,6 @@
"integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@@ -1726,7 +1724,6 @@
"integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.51.0",
"@typescript-eslint/types": "8.51.0",
@@ -2236,7 +2233,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2663,7 +2659,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -3950,7 +3945,6 @@
"integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -4472,7 +4466,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -4552,7 +4545,6 @@
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -4678,7 +4670,6 @@
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/expect": "3.2.4",
diff --git a/nhp-js/src/NHPAgent.ts b/nhp-js/src/NHPAgent.ts
index c081f46..d8ef778 100644
--- a/nhp-js/src/NHPAgent.ts
+++ b/nhp-js/src/NHPAgent.ts
@@ -25,6 +25,7 @@ import { NHP_PACKET_TYPES } from './protocol/constants.js';
import { WebSocketTransport } from './transport/websocket.js';
import { UdpTransport } from './transport/udp.js';
import { WebRTCTransport } from './transport/webrtc.js';
+import { HttpRelayTransport } from './transport/relay.js';
/** Common transport interface */
interface Transport {
@@ -40,7 +41,7 @@ interface Transport {
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
/** Default agent configuration */
-const DEFAULT_CONFIG: Required