@bobfrankston/rmfmail 1.2.310 → 1.2.312

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 (62) hide show
  1. package/.commitmsg +39 -13
  2. package/.llm/trusted-lists.md +46 -0
  3. package/TODO.md +2 -2
  4. package/client/android-bootstrap.bundle.js +78 -15
  5. package/client/android-bootstrap.bundle.js.map +2 -2
  6. package/client/app.bundle.js +57 -2
  7. package/client/app.bundle.js.map +2 -2
  8. package/client/components/message-viewer.js +82 -1
  9. package/client/components/message-viewer.js.map +1 -1
  10. package/client/components/message-viewer.ts +79 -2
  11. package/client/compose/compose.bundle.js +1 -1
  12. package/client/compose/compose.bundle.js.map +2 -2
  13. package/client/lib/api-client.js +1 -1
  14. package/client/lib/api-client.js.map +1 -1
  15. package/client/lib/api-client.ts +3 -3
  16. package/docs/allowlist.md +25 -2
  17. package/docs/spam-false-positives.md +23 -0
  18. package/npmchanges.md +16 -0
  19. package/package.json +5 -5
  20. package/packages/mailx-imap/package-lock.json +2 -2
  21. package/packages/mailx-imap/package.json +1 -1
  22. package/packages/mailx-service/index.d.ts +3 -1
  23. package/packages/mailx-service/index.d.ts.map +1 -1
  24. package/packages/mailx-service/index.js +4 -2
  25. package/packages/mailx-service/index.js.map +1 -1
  26. package/packages/mailx-service/index.ts +6 -4
  27. package/packages/mailx-service/package.json +1 -1
  28. package/packages/mailx-settings/docs/allowlist.md +25 -2
  29. package/packages/mailx-settings/docs/spam-false-positives.md +23 -0
  30. package/packages/mailx-settings/index.d.ts +1 -0
  31. package/packages/mailx-settings/index.d.ts.map +1 -1
  32. package/packages/mailx-settings/index.js +7 -0
  33. package/packages/mailx-settings/index.js.map +1 -1
  34. package/packages/mailx-settings/index.ts +7 -0
  35. package/packages/mailx-settings/package.json +1 -1
  36. package/packages/mailx-store/package.json +1 -1
  37. package/packages/mailx-store/store.d.ts.map +1 -1
  38. package/packages/mailx-store/store.js +3 -0
  39. package/packages/mailx-store/store.js.map +1 -1
  40. package/packages/mailx-store/store.ts +3 -0
  41. package/packages/mailx-store-web/package.json +1 -1
  42. package/packages/mailx-store-web/web-service.d.ts +3 -2
  43. package/packages/mailx-store-web/web-service.d.ts.map +1 -1
  44. package/packages/mailx-store-web/web-service.js +4 -2
  45. package/packages/mailx-store-web/web-service.js.map +1 -1
  46. package/packages/mailx-store-web/web-service.ts +6 -4
  47. package/packages/mailx-store-web/web-settings.d.ts +1 -0
  48. package/packages/mailx-store-web/web-settings.d.ts.map +1 -1
  49. package/packages/mailx-store-web/web-settings.js +2 -0
  50. package/packages/mailx-store-web/web-settings.js.map +1 -1
  51. package/packages/mailx-store-web/web-settings.ts +2 -0
  52. package/packages/mailx-types/index.d.ts +2 -2
  53. package/packages/mailx-types/index.d.ts.map +1 -1
  54. package/packages/mailx-types/index.js +1 -1
  55. package/packages/mailx-types/index.js.map +1 -1
  56. package/packages/mailx-types/index.ts +2 -2
  57. package/packages/mailx-types/package.json +1 -1
  58. package/packages/mailx-types/trust.d.ts +35 -0
  59. package/packages/mailx-types/trust.d.ts.map +1 -1
  60. package/packages/mailx-types/trust.js +115 -8
  61. package/packages/mailx-types/trust.js.map +1 -1
  62. package/packages/mailx-types/trust.ts +139 -8
@@ -1506,6 +1506,47 @@ export async function showMessage(accountId, uid, folderId, specialUse, isRetry
1506
1506
  items.push({ label: `✓ Trust all senders at ${dom}`, action: () => void approve("domain", dom) });
1507
1507
  items.push({ label: "", action: () => { }, separator: true });
1508
1508
  }
1509
+ // "Trust the ‹list› mailing list" (Bob 2026-09-09: "how do I
1510
+ // say that this is a valid mailing list message"). A list
1511
+ // that keeps the poster's From — MIT's Mailman, for a Gmail
1512
+ // author — leaves nothing per-sender to trust: the author
1513
+ // is unproved on every post, by the list's own doing. The
1514
+ // LIST is proved (its host passed SPF for the bounce
1515
+ // address), so the list is what the reader vouches for.
1516
+ // Offered from the chip's own advice, which is the only
1517
+ // thing an unflagged list post shows; the banner has its
1518
+ // own button.
1519
+ const listSpam = msg.spamScore;
1520
+ if (listSpam?.listProved && listSpam.listId && !listSpam.listTrusted) {
1521
+ const listId = listSpam.listId;
1522
+ items.push({
1523
+ label: `✓ Trust the ${listId} mailing list`,
1524
+ action: async () => {
1525
+ const st = document.getElementById("status-sync");
1526
+ try {
1527
+ // The action only ever ADDS, so anything but
1528
+ // `trusted: true` means the write did not land.
1529
+ const res = await trustSenderOrDomain("list", listId);
1530
+ if (!res?.trusted)
1531
+ throw new Error("the list did not save");
1532
+ listSpam.listTrusted = true;
1533
+ document.querySelector(".mv-spamscore")?.remove();
1534
+ document.querySelector(".mv-trust")?.remove();
1535
+ if (st)
1536
+ st.textContent = `Trusting the ${listId} mailing list`;
1537
+ }
1538
+ catch (e) {
1539
+ // The reader chose an action and must know whether it took.
1540
+ if (st) {
1541
+ st.textContent = `Couldn't trust the ${listId} list — ${e?.message || e}`;
1542
+ st.style.color = "oklch(0.65 0.2 25)";
1543
+ }
1544
+ console.error("[allowlist] trust list failed:", e);
1545
+ }
1546
+ },
1547
+ });
1548
+ items.push({ label: "", action: () => { }, separator: true });
1549
+ }
1509
1550
  }
1510
1551
  if (omitted > 0) {
1511
1552
  items.push({
@@ -1860,7 +1901,13 @@ export async function showMessage(accountId, uid, folderId, specialUse, isRetry
1860
1901
  : "No forgery rule scored. The rules that did are listed below.")
1861
1902
  : level === "high"
1862
1903
  ? "Your mail server scored this at or above its spam threshold and flagged it."
1863
- : "Below your server's spam threshold, but more than halfway to it.") + why;
1904
+ : "Below your server's spam threshold, but more than halfway to it.") + why
1905
+ // The list that provably delivered this, and where the action
1906
+ // lives — the chip is the only thing an unflagged list post
1907
+ // shows, so it has to say how to make it stop (2026-09-09).
1908
+ + (spam.listProved && spam.listId && !spam.listTrusted
1909
+ ? `\n\nDelivered by the ${spam.listId} mailing list, which your server proved sent it. Right-click the From address to trust the list.`
1910
+ : "");
1864
1911
  chip.textContent = `spam score ${spam.score} of ${spam.threshold}`;
1865
1912
  bodyEl.appendChild(chip);
1866
1913
  }
@@ -1941,6 +1988,40 @@ export async function showMessage(accountId, uid, folderId, specialUse, isRetry
1941
1988
  }
1942
1989
  warn.appendChild(actions);
1943
1990
  }
1991
+ // "Trust the ‹list› list" (Bob 2026-09-09: "how do I say that this
1992
+ // is a valid mailing list message"). A finding carries `list` only
1993
+ // when the receiving server proved the list's host sent the
1994
+ // message and the list is not yet trusted; the entry it writes
1995
+ // (`trustedLists`, keyed on List-Id) counts only on such mail.
1996
+ // Not on a danger banner, for the same reason as every other
1997
+ // button here.
1998
+ const lists = worst === "danger" ? [] : Array.from(new Set(trust.map(f => f.list).filter(Boolean)));
1999
+ if (lists.length) {
2000
+ const actions = document.createElement("div");
2001
+ actions.className = "mv-trust-actions";
2002
+ for (const listId of lists) {
2003
+ const b = document.createElement("button");
2004
+ b.className = "mv-trust-action";
2005
+ b.textContent = `Trust the ${listId} list`;
2006
+ b.addEventListener("click", async () => {
2007
+ b.disabled = true;
2008
+ try {
2009
+ const res = await trustSenderOrDomain("list", listId);
2010
+ if (!res?.trusted)
2011
+ throw new Error("the list did not save");
2012
+ warn.remove();
2013
+ document.querySelector(".mv-spamscore")?.remove();
2014
+ }
2015
+ catch (e) {
2016
+ // The reader pressed a button and must know whether it took.
2017
+ b.disabled = false;
2018
+ b.textContent = `Could not save: ${e?.message || e}`;
2019
+ }
2020
+ });
2021
+ actions.appendChild(b);
2022
+ }
2023
+ warn.appendChild(actions);
2024
+ }
1944
2025
  // "Trust ‹service› as an intermediary" (Bob 2026-09-08: "how do I
1945
2026
  // mark zoom and sites like that as trusted intermediaries?"). A
1946
2027
  // finding carries `via` only when a party PROVABLY handled the