@threadbase-sh/streamer 1.61.2 → 1.63.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.
package/dist/index.cjs CHANGED
@@ -9150,7 +9150,10 @@ var ConversationHandlers = class {
9150
9150
  score: r.score,
9151
9151
  matches: Array.isArray(r.matches) ? r.matches.map((m) => ({
9152
9152
  field: m.field,
9153
- snippet: m.snippet
9153
+ snippet: m.snippet,
9154
+ // Offsets into `snippet` for the matched tokens. Absent on metadata
9155
+ // hits, and on scanners older than the one that added them.
9156
+ highlights: m.highlights
9154
9157
  })) : []
9155
9158
  }));
9156
9159
  const page = paginate(applyFilters(adapted, filters), offset, limit);
@@ -11576,6 +11579,55 @@ function parseE2eeRequest(raw) {
11576
11579
  return { version: v, message1 };
11577
11580
  }
11578
11581
 
11582
+ // src/e2ee/pair-payload.ts
11583
+ var MAX_DEVICE_NAME_CHARS = 100;
11584
+ function parseE2eeMsg1Payload(payload) {
11585
+ let raw;
11586
+ try {
11587
+ raw = JSON.parse(payload.toString("utf-8"));
11588
+ } catch {
11589
+ throw new E2eeRequestError("E2EE_MALFORMED", "e2ee message 1 payload is not valid JSON");
11590
+ }
11591
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
11592
+ throw new E2eeRequestError("E2EE_MALFORMED", "e2ee message 1 payload must be an object");
11593
+ }
11594
+ const { v, deviceName, readOnly } = raw;
11595
+ if (typeof v !== "number" || !Number.isInteger(v)) {
11596
+ throw new E2eeRequestError("E2EE_MALFORMED", "e2ee message 1 payload v must be an integer");
11597
+ }
11598
+ if (v !== E2EE_EXCHANGE_VERSION) {
11599
+ throw new E2eeRequestError(
11600
+ "E2EE_VERSION_UNSUPPORTED",
11601
+ `e2ee message 1 payload v ${v} is not supported; this server speaks ${E2EE_EXCHANGE_VERSION}`
11602
+ );
11603
+ }
11604
+ if (typeof readOnly !== "boolean") {
11605
+ throw new E2eeRequestError(
11606
+ "E2EE_MALFORMED",
11607
+ "e2ee message 1 payload readOnly must be a boolean"
11608
+ );
11609
+ }
11610
+ if (deviceName !== void 0 && deviceName !== null && typeof deviceName !== "string") {
11611
+ throw new E2eeRequestError(
11612
+ "E2EE_MALFORMED",
11613
+ "e2ee message 1 payload deviceName must be a string when present"
11614
+ );
11615
+ }
11616
+ return {
11617
+ version: v,
11618
+ deviceName: typeof deviceName === "string" ? deviceName.slice(0, MAX_DEVICE_NAME_CHARS) : null,
11619
+ readOnly
11620
+ };
11621
+ }
11622
+ function encodeE2eeMsg2Payload(fields) {
11623
+ const payload = {
11624
+ v: E2EE_EXCHANGE_VERSION,
11625
+ ...fields,
11626
+ e2eeRequired: true
11627
+ };
11628
+ return Buffer.from(JSON.stringify(payload), "utf-8");
11629
+ }
11630
+
11579
11631
  // src/external-tails.ts
11580
11632
  var import_fs16 = require("fs");
11581
11633
  var EXTERNAL_TAIL_RECENCY_MS = RESUME_BUSY_WINDOW_MS;
@@ -16684,6 +16736,7 @@ var StreamerServer = class {
16684
16736
  return;
16685
16737
  }
16686
16738
  let handshake = null;
16739
+ let registration = null;
16687
16740
  if (e2eeRequest) {
16688
16741
  try {
16689
16742
  handshake = readMessage1({
@@ -16701,6 +16754,13 @@ var StreamerServer = class {
16701
16754
  });
16702
16755
  return;
16703
16756
  }
16757
+ try {
16758
+ registration = parseE2eeMsg1Payload(handshake.payload);
16759
+ } catch (err) {
16760
+ const e = err;
16761
+ json(res, 400, { error: e.message, code: e.code });
16762
+ return;
16763
+ }
16704
16764
  }
16705
16765
  let sealed;
16706
16766
  try {
@@ -16723,8 +16783,9 @@ var StreamerServer = class {
16723
16783
  });
16724
16784
  let device = null;
16725
16785
  try {
16726
- const name = typeof body?.deviceName === "string" ? body.deviceName.slice(0, 100) : null;
16727
- const preset = body?.readOnly === true ? "read-only" : "full";
16786
+ const name = registration ? registration.deviceName : typeof body?.deviceName === "string" ? body.deviceName.slice(0, 100) : null;
16787
+ const readOnly = registration ? registration.readOnly : body?.readOnly === true;
16788
+ const preset = readOnly ? "read-only" : "full";
16728
16789
  device = this.devicesRepo?.register({
16729
16790
  publicKey: clientPublicKey,
16730
16791
  name,
@@ -16747,23 +16808,34 @@ var StreamerServer = class {
16747
16808
  err
16748
16809
  });
16749
16810
  }
16811
+ if (handshake && !device) {
16812
+ this.log.error("[pair] E2EE pairing failed: the device could not be registered", {
16813
+ event: "pair.e2ee_registration_failed",
16814
+ ip
16815
+ });
16816
+ json(res, 500, {
16817
+ error: "Pairing failed: this server could not register the device. Scan a fresh pairing code and try again.",
16818
+ code: "E2EE_REGISTRATION_FAILED"
16819
+ });
16820
+ return;
16821
+ }
16750
16822
  let e2eeResponse = null;
16751
- if (handshake) {
16823
+ if (handshake && device) {
16752
16824
  try {
16753
16825
  const { message2 } = writeMessage2(
16754
16826
  handshake,
16755
- Buffer.from(
16756
- JSON.stringify({
16757
- v: E2EE_EXCHANGE_VERSION,
16758
- deviceId: device?.deviceId ?? null,
16759
- serverVersion: getVersion(),
16760
- // Always true on this path: completing a handshake is what pins
16761
- // the device, and design.md §6.3 says nothing a client sends
16762
- // ever clears it.
16763
- e2eeRequired: true
16764
- }),
16765
- "utf-8"
16766
- )
16827
+ // Every result a new client persists or presents as verified, so it
16828
+ // never has to trust the outer, unauthenticated copy of a credential.
16829
+ // The outer response still carries those copies for released builds;
16830
+ // this is what the new client actually reads.
16831
+ encodeE2eeMsg2Payload({
16832
+ deviceId: device.deviceId,
16833
+ deviceToken: device.deviceToken,
16834
+ capabilities: device.capabilities,
16835
+ publicUrl: this.publicUrl,
16836
+ machineName: (0, import_os14.hostname)(),
16837
+ serverVersion: getVersion()
16838
+ })
16767
16839
  );
16768
16840
  e2eeResponse = { v: E2EE_EXCHANGE_VERSION, noise: message2.toString("base64") };
16769
16841
  } catch (err) {