nervepay 1.3.5 → 1.3.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.
@@ -437,7 +437,9 @@ async function deviceNodePairing(options) {
437
437
 
438
438
  // Pre-compute crypto outside WS handler to avoid async race conditions
439
439
  const pubKeyBytes = decodeBase58(publicKeyBase58);
440
- const pubKeyB64 = Buffer.from(pubKeyBytes).toString('base64');
440
+ // OpenClaw expects base64url encoding (no padding) for public key
441
+ const pubKeyB64Url = Buffer.from(pubKeyBytes).toString('base64')
442
+ .replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
441
443
 
442
444
  const result = await new Promise((resolve, reject) => {
443
445
  let settled = false;
@@ -472,13 +474,36 @@ async function deviceNodePairing(options) {
472
474
  const frame = JSON.parse(data.toString());
473
475
  logDebug('<<', JSON.stringify(frame).slice(0, 300));
474
476
 
475
- // Challenge → sign and connect
477
+ // Challenge → build auth payload, sign, and connect
476
478
  if (frame.event === 'connect.challenge') {
477
479
  const nonce = frame.payload?.nonce || '';
478
480
  logOk('Challenge received');
479
481
 
480
- const nonceBytes = new TextEncoder().encode(nonce);
481
- const signatureB64 = await signRawBytes(privateKey, nonceBytes);
482
+ const signedAtMs = Date.now();
483
+ const role = 'operator';
484
+ const scopes = ['operator.read', 'operator.write'];
485
+
486
+ // Build structured payload per OpenClaw device-auth protocol (v2)
487
+ // Format: v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
488
+ const payload = [
489
+ 'v2',
490
+ deviceId,
491
+ 'node-host',
492
+ 'node',
493
+ role,
494
+ scopes.join(','),
495
+ String(signedAtMs),
496
+ '', // token (empty for initial connect)
497
+ nonce,
498
+ ].join('|');
499
+
500
+ logDebug('Auth payload:', payload);
501
+
502
+ const payloadBytes = new TextEncoder().encode(payload);
503
+ const signatureB64 = await signRawBytes(privateKey, payloadBytes);
504
+ // Convert to base64url (no padding)
505
+ const signatureB64Url = signatureB64
506
+ .replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
482
507
 
483
508
  connectId = crypto.randomUUID();
484
509
  ws.send(JSON.stringify({
@@ -486,10 +511,10 @@ async function deviceNodePairing(options) {
486
511
  id: connectId,
487
512
  method: 'connect',
488
513
  params: {
489
- role: 'operator',
514
+ role,
490
515
  minProtocol: 3,
491
516
  maxProtocol: 3,
492
- scopes: ['operator.read', 'operator.write'],
517
+ scopes,
493
518
  client: {
494
519
  id: 'node-host',
495
520
  version: VERSION,
@@ -498,9 +523,9 @@ async function deviceNodePairing(options) {
498
523
  },
499
524
  device: {
500
525
  id: deviceId,
501
- publicKey: pubKeyB64,
502
- signature: signatureB64,
503
- signedAt: Date.now(),
526
+ publicKey: pubKeyB64Url,
527
+ signature: signatureB64Url,
528
+ signedAt: signedAtMs,
504
529
  nonce,
505
530
  },
506
531
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nervepay",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
4
4
  "description": "NervePay plugin for OpenClaw - Self-sovereign identity, vault, and orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",