# 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 `. # 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