- 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>
247 lines
11 KiB
Markdown
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'`.
|