# contacts.jsonc

> **Owned by rmfmail. Do not edit this file — your changes will be overwritten the next time the app starts.** This is reference documentation. To change contact data, edit `contacts.jsonc` itself.

## What this file documents

`contacts.jsonc` is the **curated** part of your address book — the entries you (or the app on your behalf) explicitly keep. It lives in your shared `.rmfmail` folder on Google Drive and syncs across devices.

Auto-collected addresses (the *discovered* set) are **not** stored in this file — they are a per-device local cache. See [Discovered addresses](#discovered-addresses) below.

## Shape

```jsonc
{
  "preferred": [
    { "name": "David Reed", "email": "dpreed@example.com", "organization": "Deep Plum" }
  ],
  "denylist": [
    "spammer@example.com"
  ],
  "groups": {
    "Joes": ["joe@example.com", "jo@example.com"],
    "Family": ["alice@example.com", "Joes"]
  }
}
```

## Tiers

- **preferred** — manually-curated, top-ranked in autocomplete. Add people you actually want to reach quickly.
- **denylist** — addresses (lowercased) excluded from autocomplete entirely.
- **groups** — mailing lists. Type the group name in To/Cc/Bcc and compose expands it into the member addresses on send. Each value is an array of either email addresses (`"a@b.com"` or `"Name <a@b.com>"`) or other group names (recursive aliasing — cycles are detected, depth capped at 10). Group lookup is case-insensitive on the key. The two `Joes` example above plus the `Family` example show flat membership and one group nested inside another.

Other keys may also appear (e.g. `priorityDomains`) — rmfmail merges into the existing file rather than overwriting it, so hand-added keys survive a flush.

## Discovered addresses

Addresses seen in your sent/received mail are collected automatically into a **discovered** set. This set:

- is a **local cache** held in each device's database — it is *not* written to `contacts.jsonc` and *not* synced to Google Drive;
- drives autocomplete ranking via `useCount` × recency;
- drops addresses matching the junk patterns below, both at insertion and on a startup sweep.

Keeping it local keeps `contacts.jsonc` a few KB of curated data instead of a multi-megabyte blob, and avoids cross-device clobbering on every use-count tick. Each device builds its own discovered set from its own copy of your mail.

## Global junk filter

Addresses matching any of these are dropped at insertion AND swept on startup:

- Whole local part: `noreply`, `no-reply`, `do-not-reply`, `mailer-daemon`, `postmaster`, `bounces`, etc.
- Local-part prefix: `noreply-<random>`, `notifications-<id>`, `alerts-<x>`, `bounces-<x>`, `mailer-<x>`
- Local-part suffix: `*-bounces`, `*+bounces`, `*-noreply`, `*-notifications`
- Multi-segment one-off shape: `16179691997.15082868100.nfblcbll1x` (per-message gateway addresses)
- Domains: `txt.voice.google.com` (Google Voice SMS gateway), `reply.facebook.com`, `reply.linkedin.com`

The full pattern set lives in `contact-rules.jsonc` (shipped with the app). Bumping `rulesVersion` in that file triggers a one-shot purge of historical rows the new rules now reject.

## Notes

- JSONC: `// line comments` and trailing commas are allowed.
- Removing a `preferred` entry doesn't add it to `denylist` — it just demotes back to the discovered set (or drops if not seen in mail).
- `preferred` and `denylist` are the only tiers this file owns; `groups` and other keys are preserved on flush but otherwise hand-managed.
