nis2-agile/docs/STANDARD_EMAIL_RELAY.md
DevEnv nis2-agile c0bf7b6c15 [DOCS] Standard cross-suite AgileHub + governance CLAUDE.md + registri agent
- CLAUDE.md: TZ, SSO, vault-steward, versioning, persona v2.0, multitenant, KB RAG
- docs/standards: persona-conversational-rules v2.0
- docs/STANDARD_*: installer-integration, email-relay, AI-prodotto, marketing-tenant, multitenant
- AGENT_CHANGES.md + OPEN_TICKETS.md (registri agent automatico)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 15:41:54 +02:00

247 lines
11 KiB
Markdown

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