@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.js CHANGED
@@ -9110,7 +9110,10 @@ var ConversationHandlers = class {
9110
9110
  score: r.score,
9111
9111
  matches: Array.isArray(r.matches) ? r.matches.map((m) => ({
9112
9112
  field: m.field,
9113
- snippet: m.snippet
9113
+ snippet: m.snippet,
9114
+ // Offsets into `snippet` for the matched tokens. Absent on metadata
9115
+ // hits, and on scanners older than the one that added them.
9116
+ highlights: m.highlights
9114
9117
  })) : []
9115
9118
  }));
9116
9119
  const page = paginate(applyFilters(adapted, filters), offset, limit);
@@ -11545,6 +11548,55 @@ function parseE2eeRequest(raw) {
11545
11548
  return { version: v, message1 };
11546
11549
  }
11547
11550
 
11551
+ // src/e2ee/pair-payload.ts
11552
+ var MAX_DEVICE_NAME_CHARS = 100;
11553
+ function parseE2eeMsg1Payload(payload) {
11554
+ let raw;
11555
+ try {
11556
+ raw = JSON.parse(payload.toString("utf-8"));
11557
+ } catch {
11558
+ throw new E2eeRequestError("E2EE_MALFORMED", "e2ee message 1 payload is not valid JSON");
11559
+ }
11560
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
11561
+ throw new E2eeRequestError("E2EE_MALFORMED", "e2ee message 1 payload must be an object");
11562
+ }
11563
+ const { v, deviceName, readOnly } = raw;
11564
+ if (typeof v !== "number" || !Number.isInteger(v)) {
11565
+ throw new E2eeRequestError("E2EE_MALFORMED", "e2ee message 1 payload v must be an integer");
11566
+ }
11567
+ if (v !== E2EE_EXCHANGE_VERSION) {
11568
+ throw new E2eeRequestError(
11569
+ "E2EE_VERSION_UNSUPPORTED",
11570
+ `e2ee message 1 payload v ${v} is not supported; this server speaks ${E2EE_EXCHANGE_VERSION}`
11571
+ );
11572
+ }
11573
+ if (typeof readOnly !== "boolean") {
11574
+ throw new E2eeRequestError(
11575
+ "E2EE_MALFORMED",
11576
+ "e2ee message 1 payload readOnly must be a boolean"
11577
+ );
11578
+ }
11579
+ if (deviceName !== void 0 && deviceName !== null && typeof deviceName !== "string") {
11580
+ throw new E2eeRequestError(
11581
+ "E2EE_MALFORMED",
11582
+ "e2ee message 1 payload deviceName must be a string when present"
11583
+ );
11584
+ }
11585
+ return {
11586
+ version: v,
11587
+ deviceName: typeof deviceName === "string" ? deviceName.slice(0, MAX_DEVICE_NAME_CHARS) : null,
11588
+ readOnly
11589
+ };
11590
+ }
11591
+ function encodeE2eeMsg2Payload(fields) {
11592
+ const payload = {
11593
+ v: E2EE_EXCHANGE_VERSION,
11594
+ ...fields,
11595
+ e2eeRequired: true
11596
+ };
11597
+ return Buffer.from(JSON.stringify(payload), "utf-8");
11598
+ }
11599
+
11548
11600
  // src/external-tails.ts
11549
11601
  import { statSync as statSync6 } from "fs";
11550
11602
  var EXTERNAL_TAIL_RECENCY_MS = RESUME_BUSY_WINDOW_MS;
@@ -16655,6 +16707,7 @@ var StreamerServer = class {
16655
16707
  return;
16656
16708
  }
16657
16709
  let handshake = null;
16710
+ let registration = null;
16658
16711
  if (e2eeRequest) {
16659
16712
  try {
16660
16713
  handshake = readMessage1({
@@ -16672,6 +16725,13 @@ var StreamerServer = class {
16672
16725
  });
16673
16726
  return;
16674
16727
  }
16728
+ try {
16729
+ registration = parseE2eeMsg1Payload(handshake.payload);
16730
+ } catch (err) {
16731
+ const e = err;
16732
+ json(res, 400, { error: e.message, code: e.code });
16733
+ return;
16734
+ }
16675
16735
  }
16676
16736
  let sealed;
16677
16737
  try {
@@ -16694,8 +16754,9 @@ var StreamerServer = class {
16694
16754
  });
16695
16755
  let device = null;
16696
16756
  try {
16697
- const name = typeof body?.deviceName === "string" ? body.deviceName.slice(0, 100) : null;
16698
- const preset = body?.readOnly === true ? "read-only" : "full";
16757
+ const name = registration ? registration.deviceName : typeof body?.deviceName === "string" ? body.deviceName.slice(0, 100) : null;
16758
+ const readOnly = registration ? registration.readOnly : body?.readOnly === true;
16759
+ const preset = readOnly ? "read-only" : "full";
16699
16760
  device = this.devicesRepo?.register({
16700
16761
  publicKey: clientPublicKey,
16701
16762
  name,
@@ -16718,23 +16779,34 @@ var StreamerServer = class {
16718
16779
  err
16719
16780
  });
16720
16781
  }
16782
+ if (handshake && !device) {
16783
+ this.log.error("[pair] E2EE pairing failed: the device could not be registered", {
16784
+ event: "pair.e2ee_registration_failed",
16785
+ ip
16786
+ });
16787
+ json(res, 500, {
16788
+ error: "Pairing failed: this server could not register the device. Scan a fresh pairing code and try again.",
16789
+ code: "E2EE_REGISTRATION_FAILED"
16790
+ });
16791
+ return;
16792
+ }
16721
16793
  let e2eeResponse = null;
16722
- if (handshake) {
16794
+ if (handshake && device) {
16723
16795
  try {
16724
16796
  const { message2 } = writeMessage2(
16725
16797
  handshake,
16726
- Buffer.from(
16727
- JSON.stringify({
16728
- v: E2EE_EXCHANGE_VERSION,
16729
- deviceId: device?.deviceId ?? null,
16730
- serverVersion: getVersion(),
16731
- // Always true on this path: completing a handshake is what pins
16732
- // the device, and design.md §6.3 says nothing a client sends
16733
- // ever clears it.
16734
- e2eeRequired: true
16735
- }),
16736
- "utf-8"
16737
- )
16798
+ // Every result a new client persists or presents as verified, so it
16799
+ // never has to trust the outer, unauthenticated copy of a credential.
16800
+ // The outer response still carries those copies for released builds;
16801
+ // this is what the new client actually reads.
16802
+ encodeE2eeMsg2Payload({
16803
+ deviceId: device.deviceId,
16804
+ deviceToken: device.deviceToken,
16805
+ capabilities: device.capabilities,
16806
+ publicUrl: this.publicUrl,
16807
+ machineName: hostname3(),
16808
+ serverVersion: getVersion()
16809
+ })
16738
16810
  );
16739
16811
  e2eeResponse = { v: E2EE_EXCHANGE_VERSION, noise: message2.toString("base64") };
16740
16812
  } catch (err) {