|
|
|
@@ -0,0 +1,99 @@
|
|
|
|
|
# inbox_tidy -- archive stale unread mail out of the Proton inbox.
|
|
|
|
|
#
|
|
|
|
|
# Nothing here deletes anything. The only operation is an IMAP MOVE from INBOX
|
|
|
|
|
# to Archive, recorded in ~/.local/share/inbox-tidy/archived.jsonl so any run
|
|
|
|
|
# can be reversed with `inbox_tidy --undo <run-id>`.
|
|
|
|
|
|
|
|
|
|
# ARMED? Left false until `--check` has been compared against the Proton web
|
|
|
|
|
# UI and `--dry-run` has been read. --check and --dry-run work regardless.
|
|
|
|
|
enabled = true
|
|
|
|
|
|
|
|
|
|
# Unread for longer than this many days is a candidate.
|
|
|
|
|
age_days = 3
|
|
|
|
|
|
|
|
|
|
# Most that will be archived in one run. A backlog drains over several days
|
|
|
|
|
# rather than in one sweep, which also caps how much a bad guard list can move
|
|
|
|
|
# before you notice. `--max` overrides.
|
|
|
|
|
max_per_run = 250
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Interlocks. Both abort the run rather than archiving; both exist because of
|
|
|
|
|
# something actually observed, not as theoretical caution.
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
# Bridge reported 1249 unread in INBOX while the Proton UI showed 646. Resolved
|
|
|
|
|
# 2026-09-09: NOT a flag problem. The initial sync finished at 100% and the
|
|
|
|
|
# count did not move, and the messages were spot-checked in the web UI as
|
|
|
|
|
# genuinely unread and genuinely in the Inbox. Threading was also ruled out
|
|
|
|
|
# (1249 messages -> 1247 RFC thread roots, 1026 distinct subjects; no grouping
|
|
|
|
|
# scheme reaches 648). Most likely Proton's inbox categories showing a subset
|
|
|
|
|
# in the badge. Unresolved, but no longer load-bearing.
|
|
|
|
|
#
|
|
|
|
|
# Backlog drained 2026-09-09 (1180 archived over 5 runs; INBOX went to 14
|
|
|
|
|
# messages / 0 unread). Lowered back from the temporary 1400 now that steady
|
|
|
|
|
# state is one day's mail -- at 1400 this interlock could never fire, and an
|
|
|
|
|
# interlock that cannot fire is not one. 200 is roughly a week of arrivals, so
|
|
|
|
|
# it tolerates a holiday without tripping while still catching a runaway.
|
|
|
|
|
expected_unread_max = 200
|
|
|
|
|
|
|
|
|
|
# The correspondent allowlist is derived from the Sent mailbox. If Sent is
|
|
|
|
|
# nearly empty (as it was mid-sync, at 11 messages) that guard silently does
|
|
|
|
|
# nothing. Refuse to run rather than sweep up mail from people you write to.
|
|
|
|
|
min_correspondents = 25
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Guards. Evaluated in this order, before the archive decision, so anything
|
|
|
|
|
# uncertain resolves to leaving the message where it is.
|
|
|
|
|
#
|
|
|
|
|
# 1. \Flagged or \Answered (no config; always protected)
|
|
|
|
|
# 2. protect_senders
|
|
|
|
|
# 3. anyone in the Sent-derived correspondent list
|
|
|
|
|
# 4. protect_subjects
|
|
|
|
|
# 5. no Message-ID (could not be undone, so is not moved)
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
# An entry containing @ matches that address exactly. An entry without one
|
|
|
|
|
# matches the domain AND its subdomains, so "chase.com" also covers
|
|
|
|
|
# "fraudalert.chase.com" -- which is the case that actually matters.
|
|
|
|
|
protect_senders = [
|
|
|
|
|
# Deliberately empty to start. Add whatever gets archived that you wanted
|
|
|
|
|
# kept; the log names the sender for every move, so this list can be grown
|
|
|
|
|
# from evidence rather than guessed at up front.
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# Case-insensitive regexes tested against the Subject. These are the mails
|
|
|
|
|
# where not noticing has a real cost, so they are held back across every
|
|
|
|
|
# sender rather than relying on protect_senders being complete.
|
|
|
|
|
protect_subjects = [
|
|
|
|
|
'fraud',
|
|
|
|
|
'security alert',
|
|
|
|
|
'suspicious',
|
|
|
|
|
'unauthori[sz]ed',
|
|
|
|
|
'action (needed|required)',
|
|
|
|
|
'verify your',
|
|
|
|
|
'past due',
|
|
|
|
|
'overdue',
|
|
|
|
|
'final notice',
|
|
|
|
|
'court|subpoena|jury',
|
|
|
|
|
'\bIRS\b',
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
[ntfy]
|
|
|
|
|
url = "https://ntfy.rcjohnstone.com"
|
|
|
|
|
# Its own topic, NOT rent-utilities -- that one is a monthly message worth
|
|
|
|
|
# reading, and burying it under a daily archive summary would train you to
|
|
|
|
|
# swipe both away. `ntfy access bot inbox-tidy rw` was granted so delivery can
|
|
|
|
|
# be verified by polling, not just inferred from a 200 (the `bot` user is
|
|
|
|
|
# write-only on `*`, so polling with its token otherwise returns an empty list
|
|
|
|
|
# even when messages are sitting there).
|
|
|
|
|
topic = "inbox-tidy"
|
|
|
|
|
|
|
|
|
|
[imap]
|
|
|
|
|
host = "127.0.0.1"
|
|
|
|
|
port = 1143
|
|
|
|
|
starttls = true
|
|
|
|
|
user = "c@rcjohnstone.com"
|
|
|
|
|
password_env = "PROTON_BRIDGE_PASSWORD"
|
|
|
|
|
sent_mailbox = "Sent"
|
|
|
|
|
# Cap on how much of Sent is read to build the allowlist.
|
|
|
|
|
sent_scan_limit = 3000
|