# contact-rules.jsonc

> **Owned by rmfmail. Do not edit this file — your changes will be overwritten the next time the app starts.** This is reference documentation. The actual rules ship with the app and update on every release.

## What this file documents

`contact-rules.jsonc` defines the **global** junk-contact filter patterns (noreply, mailer-daemon, gateway domains, one-off-shape locals, etc.). It is *NOT* user-editable — it's part of the app, deployed alongside your data so you can see what's being filtered.

User-specific filters live in `contacts.jsonc` (`denylist` exact match; `denylistPatterns` regex array — TODO).

## Shape

```jsonc
{
  "rulesVersion": "v3-domain-oneoff",   // bump when patterns tighten — triggers one-shot purge
  "junk": {
    "localExact":  "^(no-?reply|do-?not-?reply|noreply|mailer-daemon|...)$",
    "localSuffix": "(-bounces|\\+bounces|-noreply|-no-reply|...)$",
    "localPrefix": "^(no-?reply|noreply|notifications?|alerts?|bounces?|mailer)[-_+]",
    "localOneoff": "^[0-9a-f]{4,}\\.[0-9a-f]{4,}(\\.[0-9a-z]{6,})?$",
    "domain":      "^(txt\\.voice\\.google\\.com|reply\\.facebook\\.com|reply\\.linkedin\\.com)$"
  }
}
```

## How rules apply

Each newly-harvested address is checked against ALL rule patterns; any match drops it. Each rule is a single regex, case-insensitive:

- **localExact** — whole-string match against the local part (before `@`).
- **localSuffix** — local part ends with the pattern.
- **localPrefix** — local part begins with the pattern + a `-` / `_` / `+` separator. Catches `noreply-<random>` style.
- **localOneoff** — full-shape match for per-message gateway addresses (Google Voice SMS uses three dot-separated alphanumeric segments).
- **domain** — whole-string match against the domain (after `@`).

## When rules update

Each rmfmail release ships an updated `contact-rules.jsonc`. On startup, the app compares the bundled `rulesVersion` to the value stored locally. If they differ, the app sweeps the contacts table once and removes rows the new rules now reject — then records the new version so the sweep doesn't repeat.

To add a rule: file an issue, or fork+PR. (Eventually `rules.jsonc` per-user will allow personal additions without forking.)
