# Changelog — advmfr.net anti-spam

## 2026-08-24

- **Conditional whitelists 00-04 created** (Bastien) **and verified** (Claude,
  browser): five rules at the top of the filter list, each
  `From` + `Spam Status contains SPF_PASS` → Stop Processing Rules
  (`00-wl-advmfr-com` domain-wide + 4 company Gmail accounts). Filter list
  order confirmed by screenshot: 00-04 above Executables/EXEC/10/30.
  Filter Test proof, both directions:
  - Internal mail (narin@advmfr.com, SPF_PASS, body containing
    `name="report.xls"`) → whitelist matched, `Finish`, normal delivery —
    the xls did NOT trigger the EXEC rule. The main false-positive source
    (internal Excel attachments) is closed.
  - Spoof (account@advmfr.com, no SPF_PASS, score 12) → all five whitelists
    correctly false, rule 10 fired, delivered to Quarantine only.
- Note: 30-self-spoof (created 08-19 with the and connector) implicitly
  verified in the same trace (evaluated, correctly false for a .com spoof).
- **Now overdue / awaiting input**: rule-10 graduation to Discard (7-day
  window ended 08-21 — need AKE's confirmation the Quarantine stayed clean);
  first DMARC weekly digest (needs the dmarc@ password in the local .env).

## 2026-08-19 (evening)

- **Internal sender list received** (15 addresses that "sometimes get filtered"):
  10 × `@advmfr.com` (the company actively works across both domains) and
  4 company-practice Gmail accounts incl. three `name.advmfr@gmail.com` —
  which retroactively explains the `export.advmfr@gmail.com` watchlist item and
  invalidates filter 32 as originally drafted. Likely main false-positive
  source: internal `.xls` attachments caught by the EXEC rule (nothing lost,
  all in Quarantine). **Design: conditional whitelists `00-04`** (From +
  `SPF_PASS` → Stop Processing Rules, at the very top) — plain From whitelisting
  refused while advmfr.com DMARC is broken. Trade-offs documented (macro-rule
  bypass for whitelisted senders; box574.bluehost.com passes advmfr.com SPF
  until fix step C). KB updated EN+TH, commit `66774af`. Execution pending.

## 2026-08-19 (afternoon)

- **Active phishing wave spoofing advmfr.net itself**, delivered to `hr@`
  ("your WebMail Account will be blocked... verify"), forwarded by Narin.
  Analysis and countermeasures in
  [suspicious-indicators.md](suspicious-indicators.md) item 5: new global
  filter `30-self-spoof` (From @advmfr.net AND Spam Status contains
  SPF_SOFTFAIL → Quarantine), SPF hardening `~all` → `-all`, staff notice.
  Pending execution + verification.

## 2026-08-19

- **Resend channel diagnosed and unblocked** (app `advmfr-knowledge`, Node.js on
  cPanel, sending domain elevaseo.com verified in Resend). Symptom: app health
  check reported "clé API refusée (401)", Resend logs empty. Root cause found by
  curl isolation: the API key is a **"Sending access only"** key (good practice)
  and the health check calls `GET /domains`, which that key type is not allowed
  to use → `restricted_api_key` 401. The key itself is valid: `POST /emails`
  test returned 200 and the mail was sent (Resend log id
  61bae670-9cd2-41df-9b50-edac59b7e1db). Also disproves the "requests cannot
  leave cPanel" theory (a network block cannot produce a 401), and explains the
  empty Resend logs (unauthenticated requests are not logged).
  Fix pending: patch the health check to validate with an empty-body
  `POST /emails` (422 = authenticated, 401 = bad key) instead of `GET /domains`;
  restart via the Node.js selector afterwards.

All changes to the o2switch account `sc6mixq8738`, newest first.
Format: date · actor · change · verification.

## 2026-08-18 (evening)

- **Audit: HR mailing resent one-by-one and vanishing** (report: 16:31 send,
  `hr@` → `apichada01@gmail.com`, 75.68 KB = the same mailing, recipient from the
  discarded list). o2switch side is proven clean: authenticated, DKIM-signed
  (`dkim_remote_smtp`), handed to jabatus, Accepted. Death occurs in the two hops
  Track Delivery cannot see: MailChannels (which fingerprinted this exact content
  yesterday, 550 [CS]) then Gmail (which absorbed yesterday's 225-message burst).
  Blocklist checks: SURBL and Barracuda clean; Spamhaus unreachable from public
  resolvers (verify at check.spamhaus.org if needed). MailChannels insights page
  is JS-gated, no detail extractable. Ranked hypotheses: relay content-block
  (silent for repeat offenders) > Gmail 421 rate-limit with message still in
  relay retry queue (NDR would arrive only after 1-3 days) > Gmail post-accept
  discard. All three share one root cause: this message+sender is burned on this
  route. Closing checks pending: NDRs in hr@'s inbox; ask one first-batch
  recipient (accepted Aug 17 09:22) whether anything ever arrived.
  **Directive restated: zero resends of this mailing from any @advmfr.net
  mailbox; each attempt reinforces the spam fingerprint and now endangers the
  domain's normal 1:1 mail. Send via the Google side.**
- **Audit updated with field data**: most recipients DO receive the resends;
  only 4-5 Gmail addresses get nothing (not even spam). Diagnosis shifts from
  route-wide to per-recipient. Ranked causes: (a) typo'd or invalid addresses
  (the list contains at least one malformed entry, `"choc 64088"@gmail.com`,
  with a space); (b) recipient-side Gmail filters or blocked-sender rules
  auto-deleting; (c) mailbox full or disabled accounts. Causes (a) and (c)
  produce NDRs in hr@'s inbox; cause (b) is silent and is checked from the
  recipient's Gmail (`in:anywhere from:hr@advmfr.net` + Settings → Filters &
  Blocked Addresses). Pending: the exact list of the 4-5 addresses, and hr@'s
  inbox sweep for NDRs.
- Two of the silent addresses identified: `apichada01@gmail.com` (today's 16:31
  resend Accepted through jabatus, then nothing) and
  `waraporn.14121987@gmail.com` (in yesterday's discarded batch; note there are
  TWO Waraporn entries in the list, `waraporn.14121987@` and
  `warapornpromnang@`  possible record mix-up). Both syntactically valid →
  typo hypothesis weakens, recipient-side filter/block/archive hypothesis leads.
  Decisive test issued: each employee sends a mail FROM their Gmail to hr@, and
  HR replies in-thread; if even the reply vanishes, the block is on their Gmail
  (filter, blocked sender, or auto-archive), proven.

## 2026-08-18 (afternoon)

- **advmfr.com fix prepared** ([advmfr-com-dmarc-fix.md](advmfr-com-dmarc-fix.md)),
  based on a full dig snapshot. Key findings: DNS zone still at **Bluehost**
  (ns1/ns2.bluehost.com); 3 conflicting `_dmarc` records confirmed (DMARC void);
  SPF still authorizes `66.147.242.174` = `box574.bluehost.com`, a shared server
  of the abandoned host (spoofing hole); DKIM Google 2048 ✅. Fix: single record
  `v=DMARC1; p=none; rua=mailto:dmarc@advmfr.net; pct=100` (Step A, Bluehost,
  Narin), cross-domain report authorization
  `advmfr.com._report._dmarc.advmfr.net TXT "v=DMARC1"` (Step B, cPanel, AKE),
  SPF cleanup after confirming nothing sends via Bluehost (Step C, Narin).
- **dmarc_report.py upgraded**: per-domain breakdown (one verdict + source table
  per domain), ready for advmfr.net + advmfr.com reports in the same mailbox.
  Self-test passed.
- **Fix doc rewritten name-free** (request: shareable outside the inner circle):
  roles instead of people (DNS operator Bluehost / cPanel operator o2switch /
  Workspace admin), and both reporting options exposed with trade-offs
  (Option 1 centralized to dmarc@advmfr.net, recommended; Option 2 local
  dmarc@advmfr.com mailbox), each with its own step-by-step and verification.

## 2026-08-18

- **Intelligent DMARC designed and approved** (Bastien: enterprise-grade, zero third
  party  no external analyzer). Design: rua → local `dmarc@advmfr.net` mailbox;
  local stdlib-only digester `dmarc_report.py` (IMAP PEEK, weekly Markdown digest in
  `dmarc-reports/`, >= 98% alignment gate); hardening ladder none → quarantine
  (pct 25/50/100) → reject, one step per week, each step logged here. Runbook step 6
  rewritten accordingly; `.env.example` added (real `.env` stays local, never shared).
  Script self-test passed (sample parse, aggregation, rDNS, verdict logic).
  Skills registry searched first: no reputable DMARC skill exists (all candidates
  <600 installs, cold-email oriented)  built in-house.
- **Step 6 executed by AKE, DNS verified** (Claude, dig against both the o2switch
  authoritative servers and public resolvers  identical, already propagated):
  `_dmarc.advmfr.net TXT "v=DMARC1;p=none;sp=none;adkim=r;aspf=r;pct=100;fo=0;
  rf=afrf;ri=86400;rua=mailto:dmarc@advmfr.net"`  exactly the approved design,
  no `ruf`. Done via cPanel's DMARC form (field mapping in runbook 6b).
- Mailbox `dmarc@advmfr.net` existence: reported created by AKE; direct check
  pending (outbound port 25 filtered from Bastien's machine, browser extension
  down at the time). Functional proof expected within 24-48 h anyway: first
  aggregate reports landing in the mailbox. To confirm at next browser session.

## 2026-08-17 (evening)

- **kasikornbank verdict: legitimate** (Claude, verified in chanhom@'s mailbox via
  webmail): 97 genuine KBank trade-finance messages; the `ACCOUNT@` rejections are a
  stale address in the bank's records, not an attack. No filter created (it would only
  have caught real bank mail). [suspicious-indicators.md](suspicious-indicators.md)
  item 3 closed. KBank publishes DMARC `p=reject` + strict SPF.
- **Quarantine has started catching traffic**: system mailbox grew from 0 to ~310 KB.
  Content to be reviewed in the daily check.
- **Documentation refresh pass** (requested by Bastien): AKE guide restructured for
  AKE and Narin with a "Where everything lives" map (Quarantine path, current filter
  table, Track Delivery usage and its 24 h retention / full-address search /
  "Accepted ≠ delivered" caveat, outbound limits); README status snapshot updated
  (Phase 1 live, observation in progress, pending items, key facts).
- **AKE guide follow-up** (questions from the field): FAQ added answering
  "can I just click Not Junk?" (yes, safe, does not train the filter, never applies
  to the central Quarantine); check frequencies made explicit (daily ≈ 2 min at a
  fixed moment, weekly ≈ 5 min on a fixed busy-weekday slot); credentials holder
  corrected to AKE.

## 2026-08-17

- **Outbound incident resolved as legitimate** (Claude, Track Delivery analysis):
  the 2026-08-14-flagged rate-limit event recurred today — `hr@advmfr.net` bulk
  mailing to ~200 employee personal addresses blew the domain-wide 180/h cap;
  beyond 100% o2switch **discards** (permanent loss), including collateral
  `export@advmfr.net` mail to FedEx at 09:57. Not a compromise (recipients match
  workforce names). Full analysis + discarded-recipient lists for re-send:
  [incident-2026-08-17-rate-limit.md](incident-2026-08-17-rate-limit.md).
  Fix to propose: HR bulk mail via ESP (Brevo already in client DNS), batches
  ≤150/h until then; AKE guide extended with the "Message discarded" alert.
- **Same-day escalation**: the HR re-send got content-blocked by the o2switch
  outbound relay (MailChannels `550 5.7.1 [CS] Message blocked`, sender
  `hr@advmfr.net`). Re-sending through the mailbox stopped; decision: move the
  HR mailing to Brevo (setup steps in the incident file). Once traffic resumes,
  test that ordinary 1:1 mail from `hr@` still passes; if not, o2switch ticket
  with the MailChannels txid.
- **Evening verification** (Claude, Track Delivery):
  - `export@` re-sends all **Accepted** at 13:21 (592276@, thclearance3@ fedex.com,
    export.advmfr@gmail.com; more accepted sends at 16:35/17:09). FedEx flow restored. ✅
  - `hr@` re-send attempt at 17:33 (e.g. suree7445@gmail.com) shows **Accepted**
    account-side yet bounced 550 at MailChannels: the relay block is invisible in
    Track Delivery. For this mailing, "Accepted" ≠ delivered; failures only surface
    as NDRs in hr@'s inbox. Confirms the mailing must move off the o2switch route.
- **BEC watchlist opened** ([suspicious-indicators.md](suspicious-indicators.md)):
  `export.advmfr@gmail.com` CC'd on FedEx customs correspondence (HIGH, ownership to
  confirm with the export team), `narinsoda33@gmail.com` inbound look-alike,
  persistent spoofed `kasikornbank.com` probing, finance-alias mapping
  (`account@`/`it@`/`advance@`). AKE guide gained a look-alike-Gmail alert trigger.
- **Bulk-mail channel decision**: Brevo rejected (cost, Bastien). Chosen route:
  the client's existing Google Workspace on advmfr.com — Google Group with the
  ~200 personal addresses (or BCC from `hr@advmfr.com`, Workspace allows
  2,000 recipients/day). Context: the same mailing "worked on Bluehost" because
  Bluehost's outbound policy was laxer; o2switch's 180/h cap + MailChannels
  filtering are not negotiable on shared hosting.

## 2026-08-14

- **Phase 0 baseline audit completed** (Claude, read-only via browser + DNS).
  Findings in [audit-2026-08-14-phase0.md](audit-2026-08-14-phase0.md). Highlights:
  - 87 mailboxes, no forwarders, no autoresponders, BoxTrapper off everywhere,
    antispam + Spam Box enabled. `quarantine@advmfr.net` already existed.
  - SPF/DKIM OK; DMARC `p=none` without `rua` (fix planned, Step 6).
  - 🚨 3 legacy global filters all using "Fail With Message" (backscatter);
    2 of them match on Body (Base64-blind). Remediation planned (Steps 1–3).
- **Documentation created** (README.md, audit, runbook, changelog) and the
  [deployment plan artifact](https://claude.ai/code/artifact/491d250f-a613-4e6e-81fa-5b374dce1f02)
  published (English).

- **Platform constraint discovered** while attempting Step 2: cPanel rejects
  `Redirect to Email` toward a controlled domain in global filters
  ("To prevent recursive email delivery issues…"). Quarantine redesigned as a
  `Quarantine` folder on the **system account** via `Deliver to Folder`
  (deliberate use of the o2switch-documented global-filter behavior).
  Runbook and plan artifact updated accordingly; `quarantine@advmfr.net` mailbox
  no longer used by the filters.
- **client-options.md added**: honest comparison of Option A (global filters +
  quarantine, recommended) vs Option B (BoxTrapper, advised against) vs
  Option C (status quo), for client sign-off.

- **Steps 0–3 executed** (Bastien) and **verified** (Claude, browser):
  - Step 0: `Quarantine` folder created on the system account via Roundcube.
  - Step 1: `X-Spam-Flag: YES` filter deleted — backscatter source removed. ✅
  - Steps 2–3: `Executables & scripts` and `EXEC` converted from `Fail With Message`
    to `Deliver to Folder` → `/.Quarantine`. ✅
  - Verification: filter list shows exactly 2 filters; both edit pages show
    action `save` → `/.Quarantine`; built-in Filter Test with a crafted
    `filename="invoice.exe"` message traced to
    `Deliver message to: "sc6mixq8738+Quarantine"@arome.o2switch.net`,
    "No other deliveries will occur". Regex inventory recorded: executables/scripts
    list + macro-document list (docm/xlsm/pptm…).

- **Step 4 executed** (Bastien) and **verified** (Claude, browser):
  `10-quarantine-score10` created — `Spam Bar` · contains · `++++++++++` →
  `Deliver to Folder` `/.Quarantine`. Filter Test proof, both directions:
  - Simulated spam (`X-Spam-Bar: ++++++++++++`) → condition true →
    `Deliver message to: "sc6mixq8738+Quarantine"`, "No other deliveries will occur". ✅
  - Clean message (`X-Spam-Bar: /`) → all conditions false →
    "Normal delivery will occur". ✅
  **Observation window opens 2026-08-14; earliest graduation to Discard: 2026-08-21**
  (7 consecutive clean days of the Quarantine folder).

- **End-to-end GTUBE test attempted** (Gmail → test@advmfr.net): rejected upstream by
  o2switch at SMTP time (`550 "The mail server detected your message as spam"`), never
  reached the account filters, absent from Track Delivery, Quarantine untouched.
  Conclusion: GTUBE validates the upstream layer, not account filters — runbook updated.
- **Real-traffic evidence found in Track Delivery** (retention: 1 day):
  - 09:49:09 — `john@ueffrlkk.asia` → `imtip@advmfr.net`, spam score **43**, result
    "FAIL with message" (the legacy backscatter filter, 11 minutes before its removal).
    Proves real infection attempts score far above 10 yet pass the upstream layer →
    the new `10-quarantine-score10` rule will capture exactly this traffic.
  - Spam probing non-existent users (`ACCOUNT@advmfr.net`) while spoofing
    `kasikornbank.com` → reminder: never whitelist a bank domain on `From` alone.

- **Client communication prepared** (not sent yet): `email-client-questions.md`
  (whitelist + DMARC mailbox decisions) and `ake-monitoring-guide.md`
  (Quarantine Watch guide for AKE — daily checks, alert template, do-not list).
  Markdown files only, kept internal — no online sharing, per Bastien.

- **🚨 New incident — outbound rate limit hit** (reported by Bastien via a deferral notice):
  `Domain advmfr.net has exceeded the max emails per hour (180/180 (100%))`.
  Deferred recipient: `teekannikar40@gmail.com` (personal Gmail, name matches the
  existing `kannikar@advmfr.net` mailbox). Sending session: authenticated SMTP
  (`esmtpa`) from `27.145.49.135` (asianet.co.th residential, hostname
  `DESKTOP-1LV4VG7`), body ~73 KB. Thai residential IP is consistent with staff
  location (cPanel last-login IP is in the same asianet range), so this is either a
  legitimate bulk send from an employee's PC or a compromised mailbox/PC using stored
  credentials. Deferred mail is queued and retried, not lost.
  **To investigate in Track Delivery (24 h retention):** which sender mailbox filled
  the 180/h quota, and whether recipients are many unknown addresses (compromise) or
  a coherent known list (bulk send). Then: password reset + PC scan if compromised;
  ESP policy (Brevo already in the client's DNS) if legitimate bulk mail.

### Pending

- [ ] Daily (≈2 min): review the system account's `Quarantine` folder for false positives
- [ ] Watch Track Delivery for the next `.asia`-style wave landing in `Quarantine`
      (final end-to-end proof on live traffic)
- [ ] Step 5: create `00-whitelist-critical` — blocked on critical-sender list
- [ ] Step 6: DMARC `rua` — blocked on choice of reporting mailbox
- [ ] Step 7: tests (filter test + GTUBE + control + Track Delivery)
- [ ] Step 8 (Day 7): graduate score-10 filter to Discard
