@oneaddress/setup 1.1.8 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +63 -19
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -48,8 +48,8 @@ From the [Partner Portal](https://partners.oneaddress.io) → My Profile → Int
48
48
  ## Documentation
49
49
 
50
50
  - [Partner Portal](https://partners.oneaddress.io)
51
- - [Protocol Specification](https://github.com/oneaddress-io/oneaddress-app/blob/main/docs/integration/SPEC.md)
52
- - [Service Level Objectives](https://github.com/oneaddress-io/oneaddress-app/blob/main/docs/integration/SLOS.md)
51
+ - [Protocol Specification](https://github.com/oneaddress-io/oneaddress-app/blob/main/docs/integrations/SPEC.md)
52
+ - [Service Level Objectives](https://github.com/oneaddress-io/oneaddress-app/blob/main/docs/integrations/SLOS.md)
53
53
 
54
54
  ## Issues
55
55
 
package/dist/index.js CHANGED
@@ -835,7 +835,7 @@ var _R = ["\u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588"
835
835
  var _S = [" \u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 ", "\u2588\u2588 ", " \u2588\u2588\u2588\u2588\u2588 ", " \u2588\u2588", " \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588\u2588 "];
836
836
  var ONE_ROWS = Array.from({ length: 7 }, (_2, i) => [_O[i], _N[i], _E[i]].join(" "));
837
837
  var ADDR_ROWS = Array.from({ length: 7 }, (_2, i) => [_A[i], _D2[i], _D2[i], _R[i], _E[i], _S[i], _S[i]].join(" "));
838
- var WIZARD_VERSION = true ? "1.1.8" : "?";
838
+ var WIZARD_VERSION = true ? "1.2.0" : "?";
839
839
  function printHeader() {
840
840
  const BAR_LEN = 85;
841
841
  const TOP_BAR = fn("\u250C") + dm("\u2500".repeat(BAR_LEN)) + fn("\u2510");
@@ -860,7 +860,7 @@ function printHeader() {
860
860
  console.log("");
861
861
  }
862
862
  function stripAnsi(s) {
863
- return s.replace(/\x1b\[[0-9;]*m/g, "");
863
+ return s.replaceAll(/\x1b\[[0-9;]*m/g, "");
864
864
  }
865
865
 
866
866
  // src/scaffold.ts
@@ -1080,9 +1080,24 @@ export async function verifyAddress(customer: Customer, address: Address): Promi
1080
1080
  * \u2551 To wire up your database: edit src/store.ts instead. \u2551
1081
1081
  * \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1082
1082
  */
1083
+ // Node.js 22.5+ required for the built-in node:sqlite module used by src/store.ts.
1084
+ // Fail fast with a clear message rather than a cryptic ERR_UNSUPPORTED_FEATURE later.
1085
+ const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(Number);
1086
+ if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 5)) {
1087
+ console.error(\`[startup] Node.js 22.5+ is required (you are running \${process.version}).\`);
1088
+ console.error('[startup] Upgrade Node.js: https://nodejs.org/en/download');
1089
+ process.exit(1);
1090
+ }
1091
+
1083
1092
  import 'dotenv/config';
1084
1093
  import express, { Request, Response } from 'express';
1085
- import { verifySignature, decryptAddress, isFreshTimestamp } from '@oneaddress/partner-sdk';
1094
+ import {
1095
+ verifySignature,
1096
+ decryptAddress,
1097
+ decryptSession,
1098
+ isFreshTimestamp,
1099
+ type SessionKeyShare,
1100
+ } from '@oneaddress/partner-sdk';
1086
1101
  import { saveAddress, verifyAddress } from './store.js';
1087
1102
 
1088
1103
  const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET ?? '';
@@ -1128,23 +1143,43 @@ app.post('/webhook', async (req: Request, res: Response) => {
1128
1143
  }
1129
1144
  if (dispatch) seenDispatches.add(dispatch);
1130
1145
 
1131
- // 5. Decrypt address payload
1132
- const encPayload = body.address_encrypted as {
1146
+ // 5. Decrypt address payload. Two shapes possible:
1147
+ // (a) D5 (new, 2026+): body.session_envelope + body.session_key_share.
1148
+ // (b) Legacy: body.address_encrypted.
1149
+ // A webhook will carry exactly one shape. If you've registered multiple
1150
+ // versioned keys (D5 supports key rotation with 90-day overlap), look up
1151
+ // the right private key by session_key_share.key_id from your key store.
1152
+ // This scaffold uses a single PARTNER_PRIVATE_KEY_PEM for simplicity \u2014
1153
+ // production deployments should hold a Map<key_id, privateKeyPem>.
1154
+ let address: Record<string, unknown>;
1155
+
1156
+ const sessionEnvelope = typeof body.session_envelope === 'string' ? body.session_envelope : null;
1157
+ const sessionKeyShare = body.session_key_share as SessionKeyShare | undefined;
1158
+ const legacyPayload = body.address_encrypted as {
1133
1159
  ephemeralPublicKey: string; iv: string; ciphertext: string; hkdfSalt?: string;
1134
1160
  } | null;
1135
1161
 
1136
- if (!encPayload) {
1162
+ if (sessionEnvelope && sessionKeyShare) {
1163
+ // D5 \u2014 call decryptSession. Wrong key_id / wrong partner_id / tampered
1164
+ // ciphertext all surface as an AES-GCM authentication-tag error.
1165
+ try {
1166
+ const data = await decryptSession(sessionKeyShare, sessionEnvelope, PARTNER_PRIVATE_KEY, PARTNER_ID);
1167
+ address = data.new_address as unknown as Record<string, unknown>;
1168
+ } catch (err) {
1169
+ console.error('[webhook] D5 decryption failed \u2014 check PARTNER_PRIVATE_KEY_PEM matches key_id', sessionKeyShare.key_id, ':', err);
1170
+ return res.status(200).json({ ok: true, note: 'Received \u2014 D5 decryption pending key setup' });
1171
+ }
1172
+ } else if (legacyPayload) {
1173
+ try {
1174
+ address = await decryptAddress(legacyPayload, PARTNER_PRIVATE_KEY, PARTNER_ID);
1175
+ } catch (err) {
1176
+ console.error('[webhook] Decryption failed \u2014 check PARTNER_PRIVATE_KEY_PEM in .env:', err);
1177
+ return res.status(200).json({ ok: true, note: 'Received \u2014 decryption pending key setup' });
1178
+ }
1179
+ } else {
1137
1180
  return res.status(200).json({ ok: true, note: 'No encrypted payload' });
1138
1181
  }
1139
1182
 
1140
- let address: Record<string, unknown>;
1141
- try {
1142
- address = await decryptAddress(encPayload, PARTNER_PRIVATE_KEY, PARTNER_ID);
1143
- } catch (err) {
1144
- console.error('[webhook] Decryption failed \u2014 check PARTNER_PRIVATE_KEY_PEM in .env:', err);
1145
- return res.status(200).json({ ok: true, note: 'Received \u2014 decryption pending key setup' });
1146
- }
1147
-
1148
1183
  const event = body.event as string;
1149
1184
  const customer = body.customer as { email?: string; name?: string } | undefined;
1150
1185
  const ctx = { email: customer?.email ?? '', name: customer?.name ?? '' };
@@ -3031,8 +3066,17 @@ async function runConformance(webhookUrl, _partnerId, webhookSecret) {
3031
3066
  body: validBody,
3032
3067
  signal: AbortSignal.timeout(8e3)
3033
3068
  });
3034
- const gotOk = res.ok;
3035
- const pass = t.expectOk ? gotOk : !gotOk;
3069
+ let bodyOk = true;
3070
+ if (t.expectOk) {
3071
+ try {
3072
+ const json = await res.json();
3073
+ bodyOk = json.ok === true;
3074
+ } catch {
3075
+ bodyOk = false;
3076
+ }
3077
+ }
3078
+ const gotOk = res.ok && bodyOk;
3079
+ const pass = t.expectOk ? gotOk : !res.ok;
3036
3080
  if (pass) {
3037
3081
  v2.success(` \u2713 ${t.name} (${res.status})`);
3038
3082
  passed++;
@@ -3375,7 +3419,7 @@ function normalisePrivateKey(raw) {
3375
3419
  if (!(0, import_node_fs4.existsSync)(abs)) return { pem: "", error: `File not found: ${abs}` };
3376
3420
  return normalisePrivateKey((0, import_node_fs4.readFileSync)(abs, "utf8"));
3377
3421
  }
3378
- const unescaped = trimmed.replace(/\\n/g, "\n");
3422
+ const unescaped = trimmed.replaceAll(/\\n/g, "\n");
3379
3423
  if (unescaped.includes("-----BEGIN PRIVATE KEY-----")) {
3380
3424
  return { pem: unescaped };
3381
3425
  }
@@ -3395,7 +3439,7 @@ function normalisePrivateKey(raw) {
3395
3439
  if (b64Lines.length === 0) return { pem: "", error: "Could not extract key body from the OneAddress PEM file" };
3396
3440
  return wrapAsPkcs8(b64Lines.join(""));
3397
3441
  }
3398
- const b64clean = trimmed.replace(/\s+/g, "");
3442
+ const b64clean = trimmed.replaceAll(/\s+/g, "");
3399
3443
  if (/^[A-Za-z0-9+/]+=*$/.test(b64clean) && b64clean.length >= 50) {
3400
3444
  return wrapAsPkcs8(b64clean);
3401
3445
  }
@@ -3632,7 +3676,7 @@ Continuing will overwrite it.`
3632
3676
  `Files in: ${outDir}`
3633
3677
  ].join("\n");
3634
3678
  const nextSteps = [
3635
- "1. Trigger a live event: update your address at app.oneaddress.io\n \u2192 src/store.ts will save it to data.db automatically",
3679
+ "1. Trigger a live event: update your address at oneaddress.io\n \u2192 src/store.ts will save it to data.db automatically",
3636
3680
  "2. View your database: open data.db with DB Browser for SQLite\n (free download at sqlitebrowser.org)",
3637
3681
  "3. For production: swap src/store.ts for your real database (Postgres, MySQL, etc.)\n then deploy and update the webhook URL in the portal",
3638
3682
  "4. Questions? partners.oneaddress.io or partners@oneaddress.io"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneaddress/setup",
3
- "version": "1.1.8",
3
+ "version": "1.2.0",
4
4
  "description": "Interactive setup wizard for OneAddress partner webhook integrations",
5
5
  "main": "dist/index.js",
6
6
  "bin": {