# STANDARD AgileHub — Email Relay centralizzato (canonico)

> **Autorità**: AgileHub (single source of truth per la suite Agile Software).
> **Versione**: 1.0 — 2026-04-21
> **Stato**: ADOTTATO. Vincolante per tutti i prodotti della suite.
> **Destinatari**: team TRPG, SustainAI, NIS2, TAXAI (AllTax), LG231, DFM, MKTG, ALLRISK, WMS, MADEBYCLOUD, CertiSource.
> **Registry DB**: record master in `nexus_hub.hub_standards` (slug=`email-relay`, version=`1.0`).

---

## 1. Principio

**Tutte le email generate dalla suite AgileHub passano da UN SOLO punto**: il microservizio `email-automation-ms` (PM2 id 21, porta 4004).

Nessun prodotto, microservizio, container DevEnv, cron o agent AI può accedere direttamente a:
- Postfix host Hetzner (`172.18.0.1:25`)
- Server Gmail (`smtp-relay.gmail.com`, `smtp.gmail.com`)
- Altri provider SMTP (SendGrid, Mailgun, AWS SES, ecc.)

---

## 2. Architettura

```
┌──────────────────┐    HTTPS + X-Internal-Key
│ Prodotto cliente │──────────────┐
│ (TRPG/SustAI...) │              │
└──────────────────┘              ▼
                         ┌──────────────────────┐
┌──────────────────┐     │ email-automation-ms  │      SMTP (internal)
│ Microservizio    │────▶│   porta 4004         │──────────┐
│ AgileHub         │     │                      │          ▼
│ (ticket/lead/..) │     │ Rate limit + Template│   ┌──────────────┐
└──────────────────┘     │ Audit DB + Retry     │   │ Postfix host │
                         └──────────────────────┘   │ 172.18.0.1:25│
                                                    └──────┬───────┘
                                                           │ SASL auth
                                                           ▼
                                                    ┌──────────────┐
                                                    │ Gmail        │
                                                    │ smtp-relay   │
                                                    │ .gmail.com   │
                                                    └──────────────┘
```

**Relay chain**:
- Prodotto → `email-automation-ms:4004` (via Apache vhost `agilehub.agile.software/api/emails/*`)
- `email-automation-ms` → Postfix `172.18.0.1:25` (container→host, senza auth perché `mynetworks` include `172.18.0.0/16`)
- Postfix → Gmail SMTP relay `:587` (richiede SASL auth service-account Workspace)

---

## 3. Contratto API

### Endpoint canonico

```
POST https://agilehub.agile.software/api/emails/send
Headers:
  X-Internal-Key: <INTERNAL_EMAIL_KEY>       (shared via /etc/agilehub/internal-keys.env)
  Content-Type: application/json

Body:
{
  "to": "user@example.com",                  // string o array
  "from_tenant_id": 5,                        // int — attribuzione log
  "template": "demo-registration-verify",     // nome template in email-automation-ms/templates/
  "variables": { "name": "Mario", ... },      // placeholder per il template
  "product": "TRPG",                          // slug prodotto (audit)
  "reply_to": "support@trpg.agile.software",  // opzionale
  "priority": "transactional"                 // "transactional" | "marketing" | "system"
}
```

### Response

```json
{
  "success": true,
  "message_id": "<agilehub-7c3f-20260421@smtp-relay.gmail.com>",
  "queued_at": "2026-04-21T15:30:00.000Z"
}
```

### Log audit

Ogni invio viene registrato in `nexus_email_db.email_log`:
- `id, message_id, to, from, subject, template, product, tenant_id, status, sent_at, smtp_response, retry_count`

### Status delivery

- `queued` — accettato dal service, in coda retry
- `sent` — accettato dal Postfix host (risposta 250 OK)
- `delivered` — accettato dal Gmail relay (webhook o check log mail.log)
- `bounced` — Gmail ha rifiutato (bad recipient, spam, auth failure)
- `failed_permanent` — 5xx, no retry
- `failed_transient` — 4xx, retry backoff (max 3)

---

## 4. VIETATO

Qualsiasi alternativa al percorso `email-automation-ms`. In particolare:

❌ **Connessione SMTP diretta** a `172.18.0.1:25` da container prodotto o DevEnv.
❌ **Provider cloud** (SendGrid, Mailgun, SES, Postmark) — se necessari, integrarli DENTRO `email-automation-ms` come alternative transport.
❌ **Librerie SMTP client** in codice prodotto:
- JS: `nodemailer`, `emailjs`, `smtp-connection`
- PHP: `PHPMailer`, `Swift_Mailer`, `mail()` built-in, `mb_send_mail()`
- Python: `smtplib` usato con credenziali cleartext
- Ruby, Go, ecc.: equivalenti
❌ **Binari di sistema** in container: `sendmail`, `mail`, `mutt`, `msmtp`.
❌ **File .env con `SMTP_*`** in qualsiasi servizio diverso da `email-automation-ms`.

### Deroghe

Solo via ticket AgileHub tag `email-bypass-exception`. Motivazioni accettabili:
- Integrazione legacy in dismissione con data di fine (es. mktg-agile oggi)
- Volume massimo > 100k/mese che giustifica provider dedicato (richiede integrazione in email-automation-ms, non bypass)
- Test E2E isolati (usa container mailpit dedicato, non produzione)

Deroghe hanno scadenza obbligatoria (max 90gg). AgileHub tiene registro in `nexus_hub.hub_standards_adoption.exemption_reason/exemption_expires_at`.

---

## 5. Come integrare

### Lato prodotto (TRPG, SustainAI, ecc.)

1. Rimuovere ogni `SMTP_*` dal `.env` del prodotto
2. Aggiungere `INTERNAL_EMAIL_KEY=<value>` al `.env` (coordinato con team AgileHub)
3. Sostituire chiamate dirette con `curl`/`fetch`/`requests` verso `https://agilehub.agile.software/api/emails/send`
4. Rimuovere dipendenze SMTP (`nodemailer`, `PHPMailer`, ecc.) dal manifest (`package.json`, `composer.json`)

### Lato AgileHub

1. `email-automation-ms` espone `/send` + `/templates/list` + `/webhooks/bounce`
2. Gestisce rate limit per tenant (`nexus_email_db.email_quota`)
3. Tiene in vita la connessione SMTP a Postfix (connection pool)
4. Retry exponential backoff su 4xx (1min, 5min, 30min), drop su 5xx
5. Webhook bounce da Gmail (future) → aggiorna `status=bounced` + notifica tenant admin

---

## 6. Stato attuale (2026-04-22 — FIX APPLICATO)

### ✅ Operativo end-to-end

- Architettura centralizzata implementata e funzionante
- IP allowlist Workspace attiva per `135.181.149.254` (configurata da Massimo Tagliavini)
- `sendmail` locale: funziona (cron watchdog, claude-auth-check, ticket-agent)
- **`email-automation-ms` canonical path**: funziona dopo fix 2026-04-22
- Endpoint protetti da `X-Internal-Key` (auth middleware aggiunto)
- Env vars propagate in `/etc/agilehub/internal-keys.env` + `.env` di 5 prodotti produzione

### Fix applicato al transport Nodemailer

File: `email-automation-ms/src/services/emailService.js:25-40`
```javascript
transporter = nodemailer.createTransport({
  host,     // da SMTP_HOST=127.0.0.1 (era 172.18.0.1)
  port,
  secure,
  name: process.env.SMTP_HELO_NAME || 'agile.software', // HELO esplicito
  ...
});
```

File: `email-automation-ms/.env`
```
SMTP_HOST=127.0.0.1   # era 172.18.0.1 (docker bridge) → Postfix vedeva connessione "esterna"
INTERNAL_SERVICE_KEY=nexus-internal-2026  # alias INTERNAL_EMAIL_KEY
```

### Auth endpoint

File: `email-automation-ms/src/index.js:27-55` — middleware `requireInternalKey`:
- Protegge: `/emails/send`, `/emails/send-raw`, `/sequences/*`, `/emails/templates/db` (POST/PUT/DELETE)
- Legge la chiave da `INTERNAL_EMAIL_KEY` (preferita) o `INTERNAL_SERVICE_KEY` (retrocompat cron)
- Senza header → 401 UNAUTHORIZED
- Con header valido → passa al controller
- Se nessuna chiave configurata → warn + pass (fallback legacy)

### Test E2E superati (2026-04-22)

| Test | Risultato |
|------|-----------|
| POST `/emails/send-raw` senza X-Internal-Key | 401 ✅ |
| POST `/emails/send-raw` con X-Internal-Key (locale) | 201 + messageId + SENT in DB ✅ |
| POST HTTPS `https://agilehub.agile.software/api/emails/send-raw` da container TRPG | 201 + SENT in DB, product=TRPG ✅ |
| Consegna su Gmail `250 2.0.0 OK` via `smtp-relay.gmail.com:587` | ✅ confermato in `/var/log/mail.log` |

### ❌ Bypass noti

- `/var/www/mktg-agile/.env` contiene ancora `SMTP_*` — tollerato perché prodotto in dismissione Fase C (spegnimento 2026-04-21+)

---

## 7. Obblighi prodotti della suite

Per essere conforme a `email-relay` v1.0 ogni prodotto **DEVE**:

1. ✅ Rimuovere ogni `SMTP_*`, credenziali Gmail, API key mailer-cloud dal proprio `.env`
2. ✅ Eliminare dipendenze mailer (nodemailer, PHPMailer, ecc.) dal manifest
3. ✅ Sostituire ogni invio con POST verso `https://agilehub.agile.software/api/emails/send`
4. ✅ Ottenere `INTERNAL_EMAIL_KEY` da AgileHub team (via ticket tag `email-key-provision`)
5. ✅ Aggiungere `INTERNAL_EMAIL_KEY` al `.env` istanza con chmod 600
6. ✅ Usare template definiti in `email-automation-ms/templates/` (aprire PR per nuovi template)
7. ✅ Loggare `message_id` ricevuto nella response per debug

Un prodotto non conforme entra in `pending` adoption nel registry. Deroghe formali con scadenza obbligatoria.

---

## 8. Riferimenti

- Microservizio: `/var/www/agile-services/email-automation-ms/` (Hetzner) + `/projects/agile-services/email-automation-ms/` (container)
- Entry point: `email-automation-ms/src/index.js:45` (SMTP transport config)
- Template repo: `email-automation-ms/templates/` (Handlebars-like .hbs)
- Log DB: `nexus_email_db.email_log` (MySQL host Hetzner)
- Postfix config: `/etc/postfix/main.cf` (Hetzner host)
- Gmail Workspace Admin Console: account `admin@agile.software`

---

## 9. Changelog

### v1.2 — 2026-04-22 (fix applicato — operativo)
- Fix transport Nodemailer: `SMTP_HOST=127.0.0.1` (era `172.18.0.1` docker bridge) + `name: 'agile.software'` HELO esplicito
- Aggiunto middleware `requireInternalKey` su `/emails/send*`, `/sequences/*`, `/emails/templates/db` mutazioni — chiave `INTERNAL_SERVICE_KEY=nexus-internal-2026` (alias `INTERNAL_EMAIL_KEY`)
- Registrato `INTERNAL_EMAIL_KEY` in `/etc/agilehub/internal-keys.env`
- Propagato `INTERNAL_EMAIL_KEY` + `EMAIL_MS_URL` al `.env` di 5 prodotti produzione (trpg-agile, sustainai-agile, nis2-agile, mktg-agile, allrisk-agile). Altri 5 prodotti (trpg-pro-agile, taxai-agile, lg231-agile, dfm-pro-agile, wms-agile) non hanno `.env` in produzione — useranno la registry centrale.
- Test E2E passati: invio da container TRPG via HTTPS verso `agilehub.agile.software/api/emails/send-raw` → consegnato Gmail 250 OK.

### v1.1 — 2026-04-22 (correzione narrativa, pre-fix)
- Correzione narrativa sezione 6: SMTP NON è bloccato. Il path `sendmail` funziona (Massimo ha configurato IP allowlist da tempo). Il bug reale era in `email-automation-ms` transport.
- Rimosso "fix A/B Postfix SASL" (errore di analisi del 2026-04-21 — non serviva).

### v1.0 — 2026-04-21
- Versione iniziale — formalizza architettura esistente
- Distribuita a 11 prodotti suite via DB registry `hub_standards`
- Richiesta conformità prodotti: rimuovere SMTP diretti dove presenti

---

**Firma autorità**: AgileHub. Violazioni vanno in `hub_standards_distribution_log` con `result='non_compliant'`.
