@bobfrankston/rmfmail 1.2.312 → 1.2.313
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/.commitmsg +19 -36
- package/.llm/trusted-lists.md +5 -2
- package/TODO.md +2 -2
- package/client/app.bundle.js +34 -7
- package/client/app.bundle.js.map +2 -2
- package/client/components/message-viewer.js +55 -9
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +53 -9
- package/client/styles/components.css +3 -0
- package/docs/allowlist.md +7 -5
- package/docs/spam-false-positives.md +1 -1
- package/npmchanges.md +42 -0
- package/package.json +5 -5
- package/packages/mailx-settings/docs/allowlist.md +7 -5
|
@@ -1381,22 +1381,35 @@ export async function showMessage(accountId: string, uid: number, folderId?: num
|
|
|
1381
1381
|
// Offered from the chip's own advice, which is the only
|
|
1382
1382
|
// thing an unflagged list post shows; the banner has its
|
|
1383
1383
|
// own button.
|
|
1384
|
+
//
|
|
1385
|
+
// Offered on every proved post, trusted or not: the write
|
|
1386
|
+
// is a toggle, and once the list is trusted the chip and
|
|
1387
|
+
// banner are gone, so this menu is the only place an
|
|
1388
|
+
// approval can be undone without editing allowlist.jsonc
|
|
1389
|
+
// by hand (Bob 2026-09-09: "how would I undo an approval").
|
|
1384
1390
|
const listSpam = (msg as any).spamScore as { listId?: string; listProved?: boolean; listTrusted?: boolean };
|
|
1385
|
-
if (listSpam?.listProved && listSpam.listId
|
|
1391
|
+
if (listSpam?.listProved && listSpam.listId) {
|
|
1386
1392
|
const listId = listSpam.listId;
|
|
1393
|
+
const untrust = !!listSpam.listTrusted;
|
|
1387
1394
|
items.push({
|
|
1388
|
-
label: `✓ Trust the ${listId} mailing list`,
|
|
1395
|
+
label: untrust ? `✗ Stop trusting the ${listId} mailing list` : `✓ Trust the ${listId} mailing list`,
|
|
1389
1396
|
action: async () => {
|
|
1390
1397
|
const st = document.getElementById("status-sync");
|
|
1391
1398
|
try {
|
|
1392
|
-
//
|
|
1393
|
-
//
|
|
1399
|
+
// A toggle: the answer says which way it went,
|
|
1400
|
+
// and it must match what was asked for — a
|
|
1401
|
+
// mismatch means the write did not land.
|
|
1394
1402
|
const res = await trustSenderOrDomain("list", listId);
|
|
1395
|
-
if (!res
|
|
1396
|
-
listSpam.listTrusted =
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1403
|
+
if (!res || !!res.trusted === untrust) throw new Error("the list did not save");
|
|
1404
|
+
listSpam.listTrusted = res.trusted;
|
|
1405
|
+
if (res.trusted) {
|
|
1406
|
+
document.querySelector(".mv-spamscore")?.remove();
|
|
1407
|
+
document.querySelector(".mv-spamscore-action")?.remove();
|
|
1408
|
+
document.querySelector(".mv-trust")?.remove();
|
|
1409
|
+
}
|
|
1410
|
+
if (st) st.textContent = res.trusted
|
|
1411
|
+
? `Trusting the ${listId} mailing list`
|
|
1412
|
+
: `No longer trusting the ${listId} mailing list — reopen the message to see its score again`;
|
|
1400
1413
|
} catch (e: any) {
|
|
1401
1414
|
// The reader chose an action and must know whether it took.
|
|
1402
1415
|
if (st) {
|
|
@@ -1763,6 +1776,37 @@ export async function showMessage(accountId: string, uid: number, folderId?: num
|
|
|
1763
1776
|
: "");
|
|
1764
1777
|
chip.textContent = `spam score ${spam.score} of ${spam.threshold}`;
|
|
1765
1778
|
bodyEl.appendChild(chip);
|
|
1779
|
+
// The action beside the warning it answers (Bob 2026-09-09: "it
|
|
1780
|
+
// would be nice to have an [approve this list] on the red warning
|
|
1781
|
+
// banner"). An unflagged list post has no banner — the chip IS
|
|
1782
|
+
// the warning — so the button sits next to it. Same write as the
|
|
1783
|
+
// From-menu item and the banner button: trustedLists by List-Id.
|
|
1784
|
+
if (spam.listProved && spam.listId && !spam.listTrusted) {
|
|
1785
|
+
const listId = spam.listId;
|
|
1786
|
+
const b = document.createElement("button");
|
|
1787
|
+
b.className = "mv-trust-action mv-spamscore-action";
|
|
1788
|
+
b.textContent = `Trust the ${listId} list`;
|
|
1789
|
+
b.title = `Delivered by the ${listId} mailing list, which your server proved sent it. Trusting the list hides this score on every post the list delivers.`;
|
|
1790
|
+
b.addEventListener("click", async () => {
|
|
1791
|
+
b.disabled = true;
|
|
1792
|
+
try {
|
|
1793
|
+
// Only ever ADDS, so anything but `trusted: true` means
|
|
1794
|
+
// the write did not land — never hide the chip on a
|
|
1795
|
+
// failed save.
|
|
1796
|
+
const res = await trustSenderOrDomain("list", listId);
|
|
1797
|
+
if (!res?.trusted) throw new Error("the list did not save");
|
|
1798
|
+
spam.listTrusted = true;
|
|
1799
|
+
chip.remove();
|
|
1800
|
+
b.remove();
|
|
1801
|
+
document.querySelector(".mv-trust")?.remove();
|
|
1802
|
+
} catch (e: any) {
|
|
1803
|
+
// The reader pressed a button and must know whether it took.
|
|
1804
|
+
b.disabled = false;
|
|
1805
|
+
b.textContent = `Could not save: ${e?.message || e}`;
|
|
1806
|
+
}
|
|
1807
|
+
});
|
|
1808
|
+
bodyEl.appendChild(b);
|
|
1809
|
+
}
|
|
1766
1810
|
}
|
|
1767
1811
|
|
|
1768
1812
|
const trust = ((msg as any).trust || []) as { id: string; severity: string; text: string; detail: string; via?: string; list?: string }[];
|
|
@@ -3055,3 +3055,6 @@ body.calendar-sidebar-on .calendar-sidebar { display: flex; }
|
|
|
3055
3055
|
/* Scored by bulk-mail or blocklist rules with the sender proved — the number
|
|
3056
3056
|
is real and stays visible, but it is not an identity finding. */
|
|
3057
3057
|
.mv-spamscore-bulk { background: oklch(0.55 0.02 250); color: white; font-weight: 600; }
|
|
3058
|
+
/* "Trust the ‹list› list" beside the chip — an unflagged list post has no banner,
|
|
3059
|
+
so the chip is the warning and the action sits next to it (2026-09-09). */
|
|
3060
|
+
.mv-spamscore-action { margin: 0 0 var(--gap-sm) var(--gap-sm); vertical-align: baseline; }
|
package/docs/allowlist.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# allowlist.jsonc
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **This documentation file (allowlist.md) is owned by rmfmail — do not edit it; it is rewritten every time the app starts.** The data file `allowlist.jsonc` next to it is yours: hand-editing it is fine and is one of the two ways to undo an approval (the other is the same viewer action again, see Notes).
|
|
4
4
|
|
|
5
5
|
## What this file documents
|
|
6
6
|
|
|
@@ -54,9 +54,11 @@ doing; a trusted list then quiets the spam note and hides the score chip. SPF an
|
|
|
54
54
|
failures, and phishing or malware URL listings, are never silenced: a list member can post
|
|
55
55
|
a bad link like anyone else.
|
|
56
56
|
|
|
57
|
-
The viewer offers **"Trust the ‹List-Id› list"** under a spam-verdict banner
|
|
58
|
-
**"Trust the ‹List-Id› mailing
|
|
59
|
-
|
|
57
|
+
The viewer offers **"Trust the ‹List-Id› list"** under a spam-verdict banner and beside the
|
|
58
|
+
spam-score chip (an unflagged post shows only the chip), and **"Trust the ‹List-Id› mailing
|
|
59
|
+
list"** in the From address's right-click menu. Once the list is trusted the chip and banner
|
|
60
|
+
are gone, so the menu item becomes **"Stop trusting the ‹List-Id› mailing list"** — that is
|
|
61
|
+
how an approval is undone from the viewer.
|
|
60
62
|
|
|
61
63
|
## Notes
|
|
62
64
|
|
|
@@ -64,5 +66,5 @@ tooltip says so — an unflagged post shows only the chip).
|
|
|
64
66
|
- Multi-client safe: each device's save merges with the cloud copy (set-union), so adds from any device propagate.
|
|
65
67
|
- Use the message viewer's "allow remote content" button to add a sender/domain to `senders`/`domains` interactively.
|
|
66
68
|
- Use the right-click menu in the viewer to flag a sender/domain — adds to `flaggedSenders`/`flaggedDomains`.
|
|
67
|
-
- "Stop warning about …" under a spam-score note adds to `trustedSenders`/`trustedDomains`; "Trust … as an intermediary" adds to `trustedIntermediaries`; "Trust the … list" adds to `trustedLists`. Each is a toggle: the same action again removes the entry.
|
|
69
|
+
- "Stop warning about …" under a spam-score note adds to `trustedSenders`/`trustedDomains`; "Trust … as an intermediary" adds to `trustedIntermediaries`; "Trust the … list" adds to `trustedLists`. Each is a toggle: the same action again removes the entry. The banner buttons disappear with the banner once trust is written, so to undo a sender or intermediary approval edit `allowlist.jsonc` (remove the entry from its list); a list approval can also be undone from the From address's right-click menu ("Stop trusting the … mailing list").
|
|
68
70
|
- JSONC: `// line comments` and trailing commas are allowed.
|
|
@@ -228,7 +228,7 @@ pass for the Sender:/bounce domain, or DKIM pass for the list domain; ISOC's Mai
|
|
|
228
228
|
and the From-vs-path rules (`LIST_RELAY_RULES`) are neutral: the list is the explanation.
|
|
229
229
|
Trusted + proved silences the verdict and hides the chip, like a trusted intermediary. SPF
|
|
230
230
|
and DMARC failures and URL/phishing listings are never silenced — a list member can post a
|
|
231
|
-
bad link like anyone else. Shipped v1.2.
|
|
231
|
+
bad link like anyone else. Shipped v1.2.312; see `docs/allowlist.md`.
|
|
232
232
|
|
|
233
233
|
## Follow-ups, 2026-08-27 evening
|
|
234
234
|
|
package/npmchanges.md
CHANGED
|
@@ -1712,3 +1712,45 @@ proof (DMARC pass, or DKIM_VALID_AU / ALL_TRUSTED per the existing proof
|
|
|
1712
1712
|
logic); an unproved message with the same links stays flagged. mailx-types
|
|
1713
1713
|
0.1.87.
|
|
1714
1714
|
|
|
1715
|
+
## v1.2.312 — 2026-09-10
|
|
1716
|
+
|
|
1717
|
+
Trusted mailing lists: "how do I say that this is a valid mailing list message"
|
|
1718
|
+
|
|
1719
|
+
Bob 2026-09-09, on an IEEE-CS meeting announcement through MIT's Mailman.
|
|
1720
|
+
The list keeps the poster's own From (gmail.com publishes no DMARC policy,
|
|
1721
|
+
so Mailman does not rewrite it), re-sends from its own host with its own
|
|
1722
|
+
bounce address, and tags the subject and appends a footer — which breaks
|
|
1723
|
+
the gmail.com and mitprod signatures. iecc.com recorded both as dkim=fail,
|
|
1724
|
+
SpamAssassin fired POSSIBLE_GMAIL_PHISHER / FREEMAIL_FORGED_FROMDOMAIN /
|
|
1725
|
+
DKIM_ADSP_CUSTOM_MED, and the chip went red on a talk announcement. The
|
|
1726
|
+
author can never be proved on list mail, so per-sender trust had nothing
|
|
1727
|
+
to hold on to and the banner (correctly) offered no button.
|
|
1728
|
+
|
|
1729
|
+
The LIST is proved: spf=pass for the mit.edu bounce address, the same host
|
|
1730
|
+
as Sender:. New allowlist.jsonc `trustedLists`, keyed on List-Id — the one
|
|
1731
|
+
header identical on every post whoever wrote it. An entry counts only on a
|
|
1732
|
+
message the topmost Authentication-Results proves the list host sent
|
|
1733
|
+
(spf=pass for the Sender:/bounce domain, or dkim=pass for the list domain,
|
|
1734
|
+
which ISOC's Mailman adds). On a proved list, trusted or not, the author's
|
|
1735
|
+
DKIM failure and the From-vs-path rules (LIST_RELAY_RULES in trust.ts) are
|
|
1736
|
+
set aside as the list's doing; trusted + proved silences the verdict and
|
|
1737
|
+
hides the chip, exactly as a trusted intermediary does. SPF and DMARC
|
|
1738
|
+
failures and URL/phishing listings are never silenced — a list member can
|
|
1739
|
+
post a bad link like anyone else.
|
|
1740
|
+
|
|
1741
|
+
Actions: "Trust the ‹List-Id› list" under a spam-verdict banner; "Trust
|
|
1742
|
+
the ‹List-Id› mailing list" in the From address's right-click menu, which
|
|
1743
|
+
the chip's tooltip now points to (an unflagged post shows only the chip).
|
|
1744
|
+
trustSenderOrDomain gains type "list"; TRUST_LIST_KEYS in mailx-types is
|
|
1745
|
+
now the single table mapping trust type → allowlist key for the desktop
|
|
1746
|
+
service and the Android web-service, which each carried their own ternary.
|
|
1747
|
+
Android web-settings DEFAULT_ALLOWLIST gains trustedLists.
|
|
1748
|
+
|
|
1749
|
+
Tests: IEEE-CS headers verbatim (chip kind no longer forgery; trusted hides
|
|
1750
|
+
it; flagged variant cautions naming the list and the action, then goes
|
|
1751
|
+
quiet), ISOC (DKIM-proved list), an unproved List-Id (just a header), a
|
|
1752
|
+
phish listing and an SPF failure on a trusted list (never silenced).
|
|
1753
|
+
mailx-types 0.1.89, mailx-settings 0.1.82, mailx-store 0.1.119,
|
|
1754
|
+
mailx-service 0.1.35, mailx-store-web 0.1.111. docs/allowlist.md and
|
|
1755
|
+
docs/spam-false-positives.md updated; TODO C165.
|
|
1756
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/rmfmail",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.313",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@bobfrankston/iflow-direct": "^0.1.68",
|
|
36
36
|
"@bobfrankston/mailx-host": "^0.1.15",
|
|
37
|
-
"@bobfrankston/mailx-imap": "^0.1.
|
|
38
|
-
"@bobfrankston/mailx-store-web": "^0.1.
|
|
37
|
+
"@bobfrankston/mailx-imap": "^0.1.179",
|
|
38
|
+
"@bobfrankston/mailx-store-web": "^0.1.112",
|
|
39
39
|
"@bobfrankston/mailx-sync": "^0.1.29",
|
|
40
40
|
"@bobfrankston/miscinfo": "^1.0.21",
|
|
41
41
|
"@bobfrankston/msger": "^0.1.431",
|
|
@@ -119,8 +119,8 @@
|
|
|
119
119
|
"dependencies": {
|
|
120
120
|
"@bobfrankston/iflow-direct": "^0.1.68",
|
|
121
121
|
"@bobfrankston/mailx-host": "^0.1.15",
|
|
122
|
-
"@bobfrankston/mailx-imap": "^0.1.
|
|
123
|
-
"@bobfrankston/mailx-store-web": "^0.1.
|
|
122
|
+
"@bobfrankston/mailx-imap": "^0.1.179",
|
|
123
|
+
"@bobfrankston/mailx-store-web": "^0.1.112",
|
|
124
124
|
"@bobfrankston/mailx-sync": "^0.1.29",
|
|
125
125
|
"@bobfrankston/miscinfo": "^1.0.21",
|
|
126
126
|
"@bobfrankston/msger": "^0.1.431",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# allowlist.jsonc
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **This documentation file (allowlist.md) is owned by rmfmail — do not edit it; it is rewritten every time the app starts.** The data file `allowlist.jsonc` next to it is yours: hand-editing it is fine and is one of the two ways to undo an approval (the other is the same viewer action again, see Notes).
|
|
4
4
|
|
|
5
5
|
## What this file documents
|
|
6
6
|
|
|
@@ -54,9 +54,11 @@ doing; a trusted list then quiets the spam note and hides the score chip. SPF an
|
|
|
54
54
|
failures, and phishing or malware URL listings, are never silenced: a list member can post
|
|
55
55
|
a bad link like anyone else.
|
|
56
56
|
|
|
57
|
-
The viewer offers **"Trust the ‹List-Id› list"** under a spam-verdict banner
|
|
58
|
-
**"Trust the ‹List-Id› mailing
|
|
59
|
-
|
|
57
|
+
The viewer offers **"Trust the ‹List-Id› list"** under a spam-verdict banner and beside the
|
|
58
|
+
spam-score chip (an unflagged post shows only the chip), and **"Trust the ‹List-Id› mailing
|
|
59
|
+
list"** in the From address's right-click menu. Once the list is trusted the chip and banner
|
|
60
|
+
are gone, so the menu item becomes **"Stop trusting the ‹List-Id› mailing list"** — that is
|
|
61
|
+
how an approval is undone from the viewer.
|
|
60
62
|
|
|
61
63
|
## Notes
|
|
62
64
|
|
|
@@ -64,5 +66,5 @@ tooltip says so — an unflagged post shows only the chip).
|
|
|
64
66
|
- Multi-client safe: each device's save merges with the cloud copy (set-union), so adds from any device propagate.
|
|
65
67
|
- Use the message viewer's "allow remote content" button to add a sender/domain to `senders`/`domains` interactively.
|
|
66
68
|
- Use the right-click menu in the viewer to flag a sender/domain — adds to `flaggedSenders`/`flaggedDomains`.
|
|
67
|
-
- "Stop warning about …" under a spam-score note adds to `trustedSenders`/`trustedDomains`; "Trust … as an intermediary" adds to `trustedIntermediaries`; "Trust the … list" adds to `trustedLists`. Each is a toggle: the same action again removes the entry.
|
|
69
|
+
- "Stop warning about …" under a spam-score note adds to `trustedSenders`/`trustedDomains`; "Trust … as an intermediary" adds to `trustedIntermediaries`; "Trust the … list" adds to `trustedLists`. Each is a toggle: the same action again removes the entry. The banner buttons disappear with the banner once trust is written, so to undo a sender or intermediary approval edit `allowlist.jsonc` (remove the entry from its list); a list approval can also be undone from the From address's right-click menu ("Stop trusting the … mailing list").
|
|
68
70
|
- JSONC: `// line comments` and trailing commas are allowed.
|