Distribuzione cross-suite su direttiva utente "se devi segnalare aggiornamenti devi farlo per tutti i dockers dev". Doc completo in docs/INCOMING_FROM_AGILEHUB_2026_05_31_email_send_fix_and_php_opcache.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1023 lines
56 KiB
Markdown
1023 lines
56 KiB
Markdown
<!-- STANDARD:timezone-conventions:v1.0:start -->
|
||
## ⏰ ORARI E TIMEZONE — REGOLE OPERATIVE
|
||
|
||
> **TL;DR**: l autorità del progetto è **`Europe/Rome`** (CEST estate UTC+2, CET inverno UTC+1).
|
||
> Quando scrivi un timestamp, **indica SEMPRE il suffisso TZ** (`CEST`/`CET`/`UTC`) oppure usa ISO8601 con offset (`2026-05-09T16:19:00+02:00`). **Mai** timestamp ambigui.
|
||
|
||
| Sistema | TZ | Output esempio |
|
||
|---|---|---|
|
||
| Host Hetzner | `Europe/Rome` | `Sat May 09 16:19 CEST 2026` |
|
||
| Container DevEnv (alcuni in drift UTC, vedi standard full) | misto | verifica con `docker exec <c> date` |
|
||
| Container produzione | `Europe/Rome` | CEST |
|
||
| MySQL `time_zone` | `SYSTEM` | `NOW()` CEST, `UTC_TIMESTAMP()` UTC |
|
||
| Apache log `%t` | `Europe/Rome` (locale) | `[09/May/2026:13:04:52 +0200]` |
|
||
| Node.js MS | UTC interno | `Date().toISOString()` → `Z` |
|
||
| Crontab Hetzner | `Europe/Rome` | `0 3 * * *` = 03:00 italiane |
|
||
|
||
**Regole**:
|
||
1. **Audit/sequenze cross-MS** → UTC obbligatorio (`2026-05-09T14:19:00Z`)
|
||
2. **Doc operativi/UI** → CEST/CET con suffisso esplicito
|
||
3. **DB store** → UTC, display → locale
|
||
4. **Cron critici** → `CRON_TZ=UTC` o fuori finestra DST 02:00-03:00 locale
|
||
|
||
**DST Italia**: ultima domenica marzo (CET→CEST, ora 02:00 saltata) + ultima domenica ottobre (CEST→CET, ora 02:00-03:00 duplicata).
|
||
|
||
**Spec completa**: `STANDARD_TIMEZONE_CONVENTIONS.md` (slug `timezone-conventions` v1.0, owner VIGILE).
|
||
<!-- STANDARD:timezone-conventions:v1.0:end -->
|
||
|
||
## 🔴 AGGIORNAMENTI AGILEHUB 2026-05-31 (vincolanti)
|
||
|
||
> Aggiunto da AgileHub-side (VIGILE) su direttiva utente: "se devi segnalare aggiornamenti devi farlo per tutti i dockers dev". Doc completo: `docs/INCOMING_FROM_AGILEHUB_2026_05_31_email_send_fix_and_php_opcache.md`.
|
||
|
||
### 1. email-automation-ms — bug "invii silenti" CHIUSO (per TUTTI i prodotti che mandano email)
|
||
|
||
Bug: `POST /api/emails/send` con payload `{to, subject, html: "..."}` (campo `html` invece del canonico `body`) rispondeva `201 success:true` ma spediva email **vuota** (solo subject). Fixed (agile-services commit `809ea53`): defense-in-depth in `emailService.send()` + alias `html` su `/emails/send` + 400 EMPTY_RENDERED_BODY.
|
||
|
||
**Payload canonici da subito**:
|
||
- Template Handlebars: `POST /emails/send {to, template, data, product, tenantId}`
|
||
- HTML grezzo: `POST /emails/send-raw {to, subject, html, product, tenantId}` ← preferito per HTML diretto
|
||
|
||
**Da controllare lato vostro**: caller che usano `html` su `/emails/send` (oggi alias-compat con header `Deprecation`) → migrare a `/emails/send-raw`; gestire `400 EMPTY_RENDERED_BODY` nel codice client; assicurarsi di usare `data` (non `variables`) per i template.
|
||
|
||
### 2. PHP opcache USR2 + disciplina commit (solo prodotti PHP-FPM)
|
||
|
||
**Hot-reload obbligatorio**: dopo OGNI edit `.php` → `docker exec <container-fpm-prodotto> kill -USR2 1`. Bind-mount NON basta (`opcache.validate_timestamps=Off` → vecchio bytecode in cache, gli utenti vedono ancora la versione precedente). USR2 = graceful FPM reload, zero downtime, ~10ms.
|
||
|
||
**Commit-early**: appena una modifica funziona (smoke verde post-USR2), commit subito. Il cron `ticket-agent-cron.sh` può revertare WIP scoperte (caso reale NIS2 29/5: commit `d5d83bb` revertato `index.php` di una Feature 1 non committata). Per WIP attivo prolungato → semaforo manuale:
|
||
```bash
|
||
echo "USER=... STARTED=$(date -Iseconds)" > /tmp/agent-working.lock
|
||
# il cron salta i container con quel lock
|
||
rm /tmp/agent-working.lock
|
||
```
|
||
|
||
**Push se cache token vuota** (post-reboot container legacy, oggi solo `trpg-agile` migrato al vault helper): chiedere a VIGILE/AgileHub-side "pusha N commit dall'host" (helper vault). Fix definitivo = migrazione vault helper come `trpg-agile`.
|
||
|
||
---
|
||
|
||
|
||
## 🔴 ATTIVITÀ PRIMARIA — Workflow di rilascio + hot-reload PHP + disciplina commit
|
||
|
||
> **VINCOLANTE per OGNI modifica di codice su NIS2.** Aggiunto 2026-05-30 da AgileHub-side (VIGILE) dopo che la sessione NIS2 ha scoperto sul campo che il bind-mount NON serve codice "live" e che le modifiche scoperte vengono revertate.
|
||
|
||
**Le 3 regole d'oro** (doc completo in `docs/INCOMING_FROM_AGILEHUB_2026_05_30_release_workflow_hot_reload.md`):
|
||
|
||
### 1. Hot-reload PHP — dopo OGNI edit `.php`
|
||
|
||
```bash
|
||
docker exec <container-fpm-nis2> kill -USR2 1
|
||
```
|
||
|
||
Il bind-mount NON basta: `opcache.validate_timestamps=Off` → senza USR2 gli utenti vedono ancora il vecchio bytecode. USR2 = graceful FPM reload (zero downtime, ~10ms, request in volo finiscono).
|
||
|
||
⚠️ **La sezione "ARCHITETTURA: PHP-FPM con BIND MOUNT (LIVE)" più sotto è fuorviante**: il filesystem è live via bind-mount, ma il **bytecode servito** no, finché non fai `kill -USR2 1`. Aggiornare quella sezione a riflettere la realtà.
|
||
|
||
### 2. Commit immediato — niente modifiche scoperte
|
||
|
||
Appena una modifica funziona (smoke verde post-USR2):
|
||
```bash
|
||
git add <file> && git commit -m "[FEAT|FIX|DOCS] descrizione"
|
||
```
|
||
|
||
**Mai** lasciare modifiche scoperte nel working tree: il cron `ticket-agent-cron.sh` (ogni 2 min) può lanciare `claude -p` che rebase/reverta. **Caso reale 29/5**: commit `d5d83bb` (agent automatico) ha revertato `index.php` di una Feature 1 non committata → persa.
|
||
|
||
Per WIP attivo prolungato → semaforo manuale (il cron salta i container con quel lock):
|
||
```bash
|
||
echo "USER=cristiano STARTED=$(date -Iseconds)" > /tmp/agent-working.lock
|
||
# ... lavori ...
|
||
rm /tmp/agent-working.lock
|
||
```
|
||
|
||
### 3. Push via host se cache token vuota (post-reboot container)
|
||
|
||
Il container NIS2 è ancora legacy (non migrato all'helper credenziali vault come `trpg-agile`). Dopo un reboot del container la `git credential-cache` in-memory è vuota → push bloccato. Invece di `git-login` interattivo, **chiedi a VIGILE/AgileHub-side**:
|
||
> "VIGILE, NIS2 ha N commit su main da pushare, fallo tu dall'host"
|
||
|
||
L'host è migrato all'helper vault, prende il PAT Gitea automatico, pusha sul bind-mount condiviso, ripristina ownership `.git`. Fix definitivo = migrazione vault helper anche per nis2-agile (richiede recreate container).
|
||
|
||
### Workflow operativo per ogni modifica
|
||
|
||
```
|
||
edit → kill -USR2 1 → smoke (curl) → bump app/version.json → git commit → git push (via host se serve)
|
||
```
|
||
|
||
### Cosa NON serve (semplificazione rispetto a TRPG)
|
||
|
||
NIS2 è **L1 master-shared** (1 istanza, `nis2.agile.software`). NON servono: plan TSSP in `hub_upgrade_plans`, image Docker build, agent run, retag registry. Quelli sono pattern TRPG **L2 partial-SaaS** per propagare a N tenant — NIS2 ha 1 sola istanza, l'edit + USR2 **È** la propagazione. Tabella di confronto nel doc completo.
|
||
|
||
📄 **Doc completo**: `docs/INCOMING_FROM_AGILEHUB_2026_05_30_release_workflow_hot_reload.md`
|
||
|
||
---
|
||
|
||
|
||
# NIS2 Agile - Documentazione Progetto
|
||
|
||
## REGOLE DI GOVERNANCE (LEGGERE ATTENTAMENTE, aggiornate 2026-04-22)
|
||
|
||
> **Queste regole sono OBBLIGATORIE e non negoziabili.**
|
||
|
||
### REGOLA FONDAMENTALE: Gitea = SOLO Backup
|
||
|
||
> **Gitea e un BACKUP one-way (sorgente -> Gitea), NON la fonte di verita.**
|
||
> **Il webhook auto-pull e DISABILITATO su tutti i 13 repo dal 2026-04-22.**
|
||
>
|
||
> - Le modifiche che fai nel container sono GIA live su `/var/www/nis2-agile/` via bind mount
|
||
> - `/var/www/nis2-agile/` e la FONTE DI VERITA
|
||
> - NON proporre MAI "git pull da Gitea" per applicare modifiche
|
||
> - Per tirare giu qualcosa da Gitea serve richiesta esplicita dell utente
|
||
> - git push -> Gitea = OK (backup)
|
||
> - git pull da Gitea -> `/var/www/nis2-agile/` = NO (puo sovrascrivere modifiche vere)
|
||
|
||
### ARCHITETTURA: PHP-FPM in Docker con BIND MOUNT (LIVE)
|
||
|
||
> **Verificato 2026-04-22**: NIS2 gira con:
|
||
> - `nis2-app` (php-fpm) con bind mount **RW** su `/var/www/nis2-agile/application` e `/public`
|
||
> - `nis2-web` (nginx) con bind mount **RO** su `/public`
|
||
> - `nis2-db` (MySQL) per persistenza
|
||
> - `nis2-qdrant` (vector DB)
|
||
> - Apache esterno ha `DocumentRoot /var/www/nis2-agile/public`
|
||
|
||
**Cosa va LIVE ISTANTANEAMENTE:**
|
||
- File `.php` in `/var/www/nis2-agile/application/` -- PHP-FPM rilegge ad ogni request
|
||
- File in `/var/www/nis2-agile/public/` -- serviti da nginx (via bind mount :ro)
|
||
- File `.html/.css/.js` nel public -- live via nginx/Apache
|
||
|
||
**Cosa richiede azione (CHIEDI SEMPRE CONFERMA):**
|
||
- Modifiche a `docker/nginx.conf` -> `docker restart nis2-web`
|
||
- Modifiche al Dockerfile -> `docker compose build + up -d`
|
||
- Schema DB (`nis2-db`) -> SQL manuale
|
||
- Worker php cron/feedback -> attendono il prossimo run o restart
|
||
|
||
**Nota**: non serve MAI fare rebuild di nis2-app per cambi di codice PHP -- il bind mount :rw garantisce che php-fpm legga sempre la versione aggiornata.
|
||
|
||
|
||
### Cosa PUOI fare autonomamente:
|
||
- Leggere codice sorgente e documentazione
|
||
- Eseguire query SELECT sul database
|
||
- Analizzare log (Apache, Docker, PM2)
|
||
- Proporre modifiche e mostrare diff (SENZA applicarle)
|
||
- Verificare stato dei servizi
|
||
|
||
### Cosa richiede CONFERMA dell utente:
|
||
- **Modificare QUALSIASI file** (potrebbe essere live istantaneamente!)
|
||
- **git commit e git push** (e un backup, ma sempre da confermare)
|
||
- **Modifiche schema DB** (ALTER/CREATE/DROP TABLE)
|
||
- **INSERT/UPDATE/DELETE** su dati di produzione
|
||
- **Installazione dipendenze** (composer require, npm install)
|
||
- **Modifiche a configurazione** (.env, docker-compose.yml, vhost Apache)
|
||
- **docker compose build/restart**, **pm2 restart**, **systemctl** qualsiasi
|
||
|
||
### DIVIETI ASSOLUTI:
|
||
- **MAI fare git pull** da Gitea senza richiesta esplicita
|
||
- **MAI fare git reset --hard** o operazioni distruttive
|
||
- **MAI toccare altri progetti o container**
|
||
- **MAI modificare configurazioni di sistema** (Apache globale, PHP globale, MySQL root)
|
||
- **MAI cancellare dati** senza backup e conferma utente
|
||
- **MAI tentare deploy SSH/SCP** verso altri server
|
||
|
||
### Flusso CORRETTO per una modifica:
|
||
1. **Analizza**: leggi il codice, capisci il problema
|
||
2. **Proponi**: mostra le modifiche all utente (diff) SENZA applicarle
|
||
3. **Attendi conferma**: l utente decide se procedere
|
||
4. **Applica**: solo dopo conferma
|
||
5. **Distingui**: e live subito o serve un rebuild/restart? Di all utente chiaramente
|
||
6. **Verifica**: controlla che https://nis2.agile.software funzioni (se applicabile)
|
||
7. **Backup su Gitea**: git commit + push (solo dopo conferma utente)
|
||
|
||
### Se qualcosa va storto:
|
||
- **NON tentare fix distruttivi** (reset, force push, drop, rm -rf)
|
||
- **NON proporre `git pull`** come recupero
|
||
- Comunica il problema all utente con dettagli precisi
|
||
|
||
|
||
## PRIMA DI INIZIARE
|
||
- Leggi sempre questo file prima di iniziare qualsiasi lavoro
|
||
- Il progetto e' al **100% di completamento + Sprint Simulazioni + Audit Chain + Sistema Feedback AI** (~34.000 righe, 85+ file sorgente)
|
||
- 15 commit su main, tutto deployato e testato su Hetzner
|
||
- E2E test completati, bug fixing, Docker verificato, UI polished
|
||
> 3. `docs/CONTEXT_LAST_SESSION.md` - **Contesto ultima sessione (continuita cross-browser)**
|
||
>
|
||
> Poi dimmi cosa hai capito dello stato attuale e dove eravamo rimasti.
|
||
|
||
## A FINE SESSIONE
|
||
|
||
> **OBBLIGATORIO**: Prima di chiudere, aggiornare SEMPRE:
|
||
>
|
||
> Aggiorna `docs/CONTEXT_LAST_SESSION.md` con:
|
||
> - Data sessione
|
||
> - Cosa hai fatto in questa sessione
|
||
> - File creati o modificati
|
||
> - File deployati su Hetzner
|
||
> - Problemi aperti / errori non risolti
|
||
> - Prossimi passi consigliati
|
||
>
|
||
> Se hai modificato schema DB, architettura o URL, aggiorna anche CLAUDE.md.
|
||
|
||
|
||
## Panoramica
|
||
NIS2 Agile e' una piattaforma SaaS multi-tenant per supportare le aziende nella compliance alla Direttiva NIS2 (EU 2022/2555) e al D.Lgs. 138/2024 italiano. Include AI integration (Claude API) per gap analysis, generazione policy, classificazione incidenti e suggerimenti rischi.
|
||
|
||
Target: PMI, Enterprise, Consulenti/CISO.
|
||
|
||
## Stack Tecnologico
|
||
- Backend: PHP 8.4 vanilla (no framework, Front Controller pattern)
|
||
- Database: MySQL 8.x (nis2_agile_db)
|
||
- Frontend: HTML5/CSS3/JavaScript vanilla
|
||
- Auth: JWT HS256 (2h access + 7d refresh)
|
||
- AI: Anthropic Claude API (claude-sonnet-4-5-20250929)
|
||
- Server: Hetzner CPX31 (135.181.149.254)
|
||
- VCS: Gitea (git.certisource.it)
|
||
- URL Produzione: https://nis2.agile.software/
|
||
|
||
|
||
## Visibilita Cross-Project
|
||
|
||
### agile-services (Read/Write BIDIREZIONALE)
|
||
|
||
**IMPORTANTE**: Questo progetto utilizza i microservizi condivisi di **agile-services** con accesso **Read/Write bidirezionale**.
|
||
|
||
#### Regole di integrazione
|
||
1. **Leggere SEMPRE** `agile-services-istructio.md` nella root del progetto per capire come interagire con i servizi
|
||
2. **Puoi leggere E modificare** i file in agile-services (path locale: `c:\Projects\agile-services`, path container: `/projects/agile-services`)
|
||
3. I servizi disponibili includono: vault, document, payment, certificate, subscription, billing, practice, company, supplier, investigation, whatsapp
|
||
4. Le credenziali di accesso ai servizi sono documentate in `agile-services-istructio.md`
|
||
5. Ogni nuovo servizio creato per NIS2 deve essere registrato anche in agile-services per essere riutilizzabile
|
||
6. **agile-services puo' leggere e modificare file di NIS2** — la visibilita e' bidirezionale
|
||
|
||
#### Accesso
|
||
- Path locale: `c:\Projects\agile-services`
|
||
- Path container: `/projects/agile-services` (read-write)
|
||
- File istruzioni: `./agile-services-istructio.md` (nella root del progetto)
|
||
|
||
### trpg.agile (Read-Only)
|
||
|
||
NIS2 puo' **leggere** il codice di TRPG Agile per riferimento e pattern, ma **NON deve modificare** nessun file.
|
||
|
||
- Path locale: `c:\Projects\trpg.agile`
|
||
- **Permessi**: SOLO LETTURA
|
||
- **Scopo**: Consultare pattern UI/UX, architettura frontend, convenzioni codice
|
||
- **DIVIETO**: NON modificare, NON creare, NON cancellare file in trpg.agile
|
||
|
||
### lg231.agile (Read-Only)
|
||
|
||
NIS2 puo' **leggere** il codice di 231 Agile per riferimento e pattern, ma **NON deve modificare** nessun file.
|
||
|
||
- Path locale: `c:\Projects\lg231.agile`
|
||
- **Permessi**: SOLO LETTURA
|
||
- **Scopo**: Consultare architettura microservizi PHP/Slim 4, pattern multi-tenancy, struttura database
|
||
- **DIVIETO**: NON modificare, NON creare, NON cancellare file in lg231.agile
|
||
|
||
### sustainai.agile (Read-Only)
|
||
|
||
NIS2 puo' **leggere** il codice di SustainAI Agile per riferimento e pattern, ma **NON deve modificare** nessun file.
|
||
|
||
- Path locale: `c:\Projects\sustainai.agile`
|
||
- **Permessi**: SOLO LETTURA
|
||
- **Scopo**: Consultare pattern UI/UX (identico a NIS2), struttura frontend, convenzioni CSS/JS
|
||
- **DIVIETO**: NON modificare, NON creare, NON cancellare file in sustainai.agile
|
||
|
||
### Riepilogo Visibilita
|
||
|
||
| Progetto | Permesso | Direzione |
|
||
|----------|----------|-----------|
|
||
| **agile-services** | Read/Write | Bidirezionale (NIS2 <-> agile-services) |
|
||
| **trpg.agile** | Read-Only | NIS2 -> trpg (solo lettura) |
|
||
| **lg231.agile** | Read-Only | NIS2 -> lg231 (solo lettura) |
|
||
| **sustainai.agile** | Read-Only | NIS2 -> sustainai (solo lettura) |
|
||
|
||
> **REGOLA**: Non accedere MAI a progetti non elencati in questa tabella. Nel dubbio, chiedere all'utente.
|
||
|
||
## Regola Fondamentale
|
||
Il progetto NIS2 Agile e' COMPLETAMENTE ISOLATO dagli altri applicativi (CertiSource, AGILE_DFM). Database dedicato, utente dedicato, path dedicati. Non condividere MAI credenziali tra applicativi.
|
||
|
||
## Struttura Progetto
|
||
```
|
||
nis2.agile/
|
||
├── CLAUDE.md # Questo file
|
||
├── .env # Variabili ambiente (NON committare)
|
||
├── .gitignore
|
||
├── application/
|
||
│ ├── config/
|
||
│ │ ├── config.php # Costanti app, CORS, JWT, AI, rate limiting
|
||
│ │ ├── database.php # Classe Database (PDO singleton)
|
||
│ │ └── env.php # Caricamento .env
|
||
│ ├── controllers/ # 19 controller (tutti implementati 100%)
|
||
│ │ ├── BaseController.php # Auth JWT, multi-tenancy, JSON responses (576 righe)
|
||
│ │ ├── AdminController.php # Gestione piattaforma (super_admin)
|
||
│ │ ├── AssessmentController.php # Gap analysis e questionari NIS2 (80 domande)
|
||
│ │ ├── AssetController.php # Inventario asset e dipendenze
|
||
│ │ ├── AuditController.php # Controlli, evidenze, report, export CSV
|
||
│ │ ├── AuthController.php # Login, register, JWT, rate limiting
|
||
│ │ ├── DashboardController.php # Overview, score, deadlines, heatmap
|
||
│ │ ├── IncidentController.php # Incidenti Art.23 (24h/72h/30d) + email
|
||
│ │ ├── NonConformityController.php# NCR/CAPA non-conformità e azioni correttive
|
||
│ │ ├── OnboardingController.php # Wizard onboarding con visura/CertiSource
|
||
│ │ ├── OrganizationController.php # CRUD org, membri, classificazione NIS2
|
||
│ │ ├── PolicyController.php # Policy, approvazione, AI generation
|
||
│ │ ├── RiskController.php # Risk register, trattamenti, matrice, AI suggest
|
||
│ │ ├── SupplyChainController.php # Fornitori, valutazione, risk overview
|
||
│ │ ├── TrainingController.php # Corsi, assegnazioni, compliance formativa
|
||
│ │ ├── ServicesController.php # Services API (read-only, API Key + scope)
|
||
│ │ ├── WebhookController.php # CRUD api_keys + webhook_subscriptions
|
||
│ │ ├── WhistleblowingController.php # Segnalazioni anonime Art.32 NIS2
|
||
│ │ ├── NormativeController.php # Feed NIS2/ACN/DORA con ACK tracciato
|
||
│ │ └── FeedbackController.php # Sistema segnalazioni bug/UX con risoluzione AI autonoma
|
||
│ ├── services/ # 6 servizi
|
||
│ │ ├── AIService.php # Anthropic Claude API (gap, risk, policy, incident)
|
||
│ │ ├── EmailService.php # Email CSIRT, training, welcome, invite
|
||
│ │ ├── RateLimitService.php # Rate limiting file-based
|
||
│ │ ├── ReportService.php # Report esecutivo HTML, export CSV
|
||
│ │ ├── WebhookService.php # Delivery webhook HMAC-SHA256, retry 3x
|
||
│ │ └── FeedbackService.php # createReport, classifyWithAI, broadcastResolution
|
||
│ │ └── VisuraService.php # AI extraction PDF visura + CertiSource API
|
||
│ ├── models/ # (vuoto - logica nei controller)
|
||
│ └── data/
|
||
│ └── nis2_questionnaire.json # 80 domande gap analysis (10 categorie Art.21)
|
||
├── public/
|
||
│ ├── index.php # Front Controller / Router
|
||
│ ├── .htaccess # Rewrite rules + Authorization header
|
||
│ ├── api-status.php # Health check
|
||
│ ├── index.html # Landing page
|
||
│ ├── login.html # Login
|
||
│ ├── register.html # Registrazione
|
||
│ ├── onboarding.html # Wizard 5-step (visura/CertiSource/manuale)
|
||
│ ├── setup-org.html # Setup org (legacy, ora usa onboarding.html)
|
||
│ ├── dashboard.html # Dashboard principale
|
||
│ ├── assessment.html # Gap analysis wizard
|
||
│ ├── risks.html # Risk management + matrice 5x5
|
||
│ ├── incidents.html # Gestione incidenti Art.23
|
||
│ ├── policies.html # Policy management + AI generate
|
||
│ ├── supply-chain.html # Fornitori + assessment sicurezza
|
||
│ ├── training.html # Formazione + assegnazioni
|
||
│ ├── assets.html # Inventario asset
|
||
│ ├── reports.html # Report compliance + audit log
|
||
│ ├── settings.html # Impostazioni org/profilo/membri
|
||
│ ├── companies.html # Gestione aziende (consulente)
|
||
│ ├── architecture.html # Pagina architettura sistema
|
||
│ ├── admin/
|
||
│ │ ├── index.html # Admin dashboard
|
||
│ │ ├── organizations.html # Gestione organizzazioni
|
||
│ │ └── users.html # Gestione utenti
|
||
│ ├── css/
|
||
│ │ └── style.css # CSS principale (~1600 righe)
|
||
│ ├── js/
|
||
│ │ ├── api.js # Client API (270 righe, tutti gli endpoint)
|
||
│ │ ├── common.js # Utility condivise (sidebar, notifiche, etc.)
|
||
│ │ ├── i18n.js # Internazionalizzazione IT/EN
|
||
│ │ └── help.js # Help contestuale online
|
||
│ └── uploads/ # Upload directory (gitignored)
|
||
│ └── visure/ # PDF visure camerali
|
||
├── docker/
|
||
│ ├── Dockerfile
|
||
│ ├── docker-compose.yml
|
||
│ ├── nginx.conf
|
||
│ └── php.ini
|
||
└── docs/
|
||
├── sql/
|
||
│ ├── 001_initial_schema.sql # Schema DB completo (20 tabelle)
|
||
│ ├── 002_email_log.sql # Tabella email_log
|
||
│ ├── 003_voluntary_compliance.sql # ALTER organizations: voluntary_compliance
|
||
│ ├── 004_ncr_capa.sql # Tabelle non_conformities, corrective_actions
|
||
│ ├── 005_consultant_support.sql # ALTER user_organizations: ruolo consultant
|
||
│ ├── 006_security_improvements.sql # Indici performance + soft delete + trigger audit immutabile
|
||
│ ├── 007_services_api.sql # api_keys, webhook_subscriptions, webhook_deliveries
|
||
│ ├── 008_whistleblowing.sql # whistleblowing_reports, whistleblowing_timeline
|
||
│ ├── 009_normative_updates.sql # normative_updates, normative_ack (seed 5 aggiornamenti)
|
||
│ ├── 010_audit_hash_chain.sql # prev_hash, entry_hash, severity su audit_logs
|
||
│ └── reset-demo.sql # Reset dati demo (mantiene id<=4)
|
||
├── context/
|
||
│ └── CONTEXT_SCHEMA_DB.md
|
||
├── prompts/
|
||
└── credentials/
|
||
├── credentials.md
|
||
└── hetzner_key # SSH key per Hetzner
|
||
```
|
||
|
||
## Multi-Tenancy
|
||
- Ogni tabella dati ha `organization_id`
|
||
- Header `X-Organization-Id` per selezionare org attiva
|
||
- Ruoli: `super_admin`, `org_admin`, `compliance_manager`, `board_member`, `auditor`, `employee`, `consultant`
|
||
- `requireOrgAccess()` in BaseController verifica membership
|
||
- Super admin bypassa tutti i controlli di membership
|
||
|
||
## Flusso Utente
|
||
1. **Registrazione** → redirect a `onboarding.html`
|
||
2. **Onboarding** (5 step): Scelta metodo → Visura/CertiSource/Manuale → Dati aziendali → Profilo → Classificazione NIS2
|
||
3. **Login** → se ha org → `dashboard.html`, altrimenti → `onboarding.html`
|
||
4. **Dashboard** → navigazione sidebar a tutti i moduli
|
||
|
||
## Database (29 tabelle)
|
||
organizations, users, user_organizations, refresh_tokens, assessments, assessment_responses, risks, risk_treatments, incidents, incident_timeline, policies, suppliers, training_courses, training_assignments, assets, compliance_controls, evidence_files, audit_logs, ai_interactions, email_log, non_conformities, corrective_actions
|
||
|
||
Schema: `docs/sql/` (9 migrazioni: 001→009)
|
||
|
||
## Servizi
|
||
|
||
### AIService.php
|
||
- `analyzeGapAssessment()` - Analisi assessment con raccomandazioni
|
||
- `suggestRisks()` - Suggerimenti rischi per settore/asset
|
||
- `generatePolicy()` - Generazione bozze policy NIS2
|
||
- `classifyIncident()` - Classificazione e severity incidenti
|
||
- Modello: claude-sonnet-4-5-20250929, API: https://api.anthropic.com/v1/messages
|
||
|
||
### EmailService.php
|
||
- Notifiche CSIRT: early warning 24h, notification 72h, final report 30d
|
||
- Training assignment e reminder
|
||
- Welcome email, member invite
|
||
- Template HTML professionale con branding NIS2 Agile
|
||
|
||
### RateLimitService.php
|
||
- File-based (/tmp/nis2_ratelimit/)
|
||
- Login: 5/min, 20/h | Register: 3/10min | AI: 10/min, 100/h
|
||
|
||
### ReportService.php
|
||
- Report esecutivo HTML (stampabile come PDF)
|
||
- Export CSV: rischi, incidenti, controlli, asset (separatore ;, BOM UTF-8)
|
||
|
||
### VisuraService.php
|
||
- Estrazione AI da PDF visura camerale (Claude con document type)
|
||
- Fetch dati da CertiSource API (GET /api/company/enrich?vat=)
|
||
- Mapping ATECO → settore NIS2
|
||
|
||
### AuditService.php (NUOVO)
|
||
- Hash chain SHA-256: ogni record include prev_hash → entry_hash linkato
|
||
- `log()`: inserisce con hash catena, auto-severity (info/warning/critical)
|
||
- `verifyChain()`: verifica integrità per org, rileva record manomessi
|
||
- `exportCertified()`: export JSON con SHA-256 del contenuto, salva in audit_exports
|
||
- Adattato da lg231-agile/shared/audit-lib/src/AuditTrailService.php
|
||
|
||
### WebhookService.php (NUOVO)
|
||
- Delivery HMAC-SHA256 Stripe-like, header X-NIS2-Signature
|
||
- Retry 3x backoff (0s/5min/30min), log in webhook_deliveries
|
||
|
||
### FeedbackService.php (NUOVO)
|
||
- `createReport()`: INSERT + classifyWithAI() sincrono (10s timeout)
|
||
- `classifyWithAI()`: chiama AIService::classifyFeedback(), aggiorna DB silenziosamente
|
||
- `broadcastResolution()`: email a tutti i membri org via EmailService::sendFeedbackResolved()
|
||
- Worker autonomo: `scripts/feedback-worker.php` (cron 30min, docker exec nis2-agile-devenv + Claude Code CLI)
|
||
|
||
### simulate-nis2.php (ROOT — NUOVO)
|
||
- Script simulazione demo 3 aziende × 5 scenari (CLI + SSE streaming)
|
||
- Aziende: DataCore (IT/Essential), MedClinic (Sanità/Important), EnerNet (Energia/Critical)
|
||
- Scenari: Onboarding + Assessment | Ransomware Art.23 | Data Breach | Whistleblowing | Audit Chain
|
||
- Reset: docs/sql/reset-demo.sql (cancella org/utenti con id>4 e email %.demo%)
|
||
|
||
## Deploy
|
||
- **SSH**: `ssh -i docs/credentials/hetzner_key root@135.181.149.254`
|
||
- **Path server**: `/var/www/nis2-agile/`
|
||
- **Apache config**: `/etc/apache2/sites-enabled/nis2-agile-software.conf` (HTTP) + `nis2-agile-software-le-ssl.conf` (HTTPS, dopo DNS+certbot)
|
||
- **Deploy**: `cd /var/www/nis2-agile && git pull origin main`
|
||
- **DB**: Vedi `docs/DB_ACCESS.md` per credenziali (password in `.env`)
|
||
- **Attivazione SSL nis2.agile.software**:
|
||
1. Cloudflare: aggiungere `nis2.agile.software A 135.181.149.254` (proxy OFF — grigio)
|
||
2. Hetzner: `bash /opt/devenv/scripts/setup-nis2-agile-software.sh`
|
||
- **Vecchio dominio**: `nis2.certisource.it` resterà attivo finché redirect non è configurato dallo script
|
||
|
||
## Git
|
||
- **Repository**: https://git.certisource.it/AdminGit2026/nis2-agile
|
||
- **Token Gitea**: Configurato in git credential manager (non documentare qui)
|
||
- **Branch**: main
|
||
- **Commit format**: `[AREA] Descrizione`
|
||
|
||
### Cronologia Commit
|
||
```
|
||
7080695 [FEAT] Ruolo Consulente + Wizard Registrazione v2
|
||
ba21534 [DEPLOY] Migrazione a subdomain nis2.agile.software
|
||
92f9366 Merge branch 'main'
|
||
d3eac7c [CORE] Rimosso credenziali da CLAUDE.md + aggiunto docs/DB_ACCESS.md
|
||
a0fd543 [CORE] Aggiunto settings Claude Code con permessi ampi
|
||
0a73983 [FIX] Dockerignore: allow docker/php.ini for build context
|
||
4bd2326 [CORE] Aggiunto integrazione agile-services
|
||
52fd45f [FEAT] i18n IT/EN, Help Online contestuale, pagina Architettura
|
||
4e3408e [FEAT] Visura auto-fill, adesione volontaria, modulo NCR/CAPA
|
||
517cab7 [FIX] Fix annual_turnover field name in setup-org.html
|
||
68f8cab [POLISH] Docker setup fix + UI polish + project completion
|
||
bcc5a2b [FIX] E2E testing - fix router, EmailService, frontend data mapping
|
||
```
|
||
|
||
## API Endpoints Completi
|
||
|
||
Base: `/api/{controller}/{action}/{id?}` (su subdomain https://nis2.agile.software/)
|
||
|
||
### Auth: POST register, login, logout, refresh, change-password | GET me | PUT profile
|
||
### Organizations: POST create, classify | GET current, list, {id}/members | PUT {id} | POST {id}/invite | DELETE {id}/members/{sid}
|
||
### Assessments: GET list, {id}, {id}/questions, {id}/report | POST create, {id}/respond, {id}/complete, {id}/ai-analyze | PUT {id}
|
||
### Dashboard: GET overview, compliance-score, upcoming-deadlines, recent-activity, risk-heatmap
|
||
### Risks: GET list, {id}, matrix | POST create, {id}/treatments, ai-suggest | PUT {id}, treatments/{sid} | DELETE {id}
|
||
### Incidents: GET list, {id} | POST create, {id}/timeline, {id}/early-warning, {id}/notification, {id}/final-report, {id}/ai-classify | PUT {id}
|
||
### Policies: GET list, {id}, templates | POST create, {id}/approve, ai-generate | PUT {id} | DELETE {id}
|
||
### Supply Chain: GET list, {id}, risk-overview | POST create, {id}/assess | PUT {id} | DELETE {id}
|
||
### Training: GET courses, assignments, compliance-status | POST courses, assign | PUT assignments/{sid}
|
||
### Assets: GET list, {id}, dependency-map | POST create | PUT {id} | DELETE {id}
|
||
### Audit: GET controls, evidence/list, report, logs, iso27001-mapping, executive-report, export | PUT controls/{sid} | POST evidence/upload
|
||
### Onboarding: POST upload-visura, fetch-company, complete
|
||
### Admin: GET organizations, users, stats
|
||
### NCR/CAPA: GET list, {id}, stats | POST create, fromAssessment, {id}/capa, {id}/sync, webhook | PUT {id}, capa/{subId}
|
||
### Services API (X-API-Key): GET status, compliance-summary, risks-feed, incidents-feed, controls-status, assets-critical, suppliers-risk, policies-approved, openapi
|
||
### Webhooks: GET api-keys, subscriptions, deliveries | POST api-keys, subscriptions, subscriptions/{id}/test, retry | PUT subscriptions/{id} | DELETE api-keys/{id}, subscriptions/{id}
|
||
### Whistleblowing: POST submit, {id}/assign, {id}/close | GET list, {id}, stats, track-anonymous | PUT {id}
|
||
### Normative: GET list, {id}, pending, stats | POST {id}/ack, create
|
||
### Feedback: POST submit | GET mine, list, {id} | PUT {id} | POST {id}/resolve
|
||
|
||
## Stato Completamento
|
||
Tutti i moduli sono implementati e testati:
|
||
- [x] Test end-to-end: tutti gli endpoint API verificati
|
||
- [x] Bug fixing: router rewrite, EmailService parse fix, frontend data mapping
|
||
- [x] Docker setup: Dockerfile, docker-compose.yml, nginx.conf, php.ini verificati
|
||
- [x] UI polish: animazioni, skeleton loaders, mobile backdrop, print styles, a11y
|
||
|
||
### Bug Risolti (E2E Testing)
|
||
1. **Router /{id}/subAction** - Pattern matching riscritto completamente per gestire GET /assessments/1/questions, GET /organizations/2/members, etc.
|
||
2. **EmailService parse error** - PHP non supporta `??` dentro `{$var}` string interpolation, estratto a variabile
|
||
3. **Frontend data mapping** - Dashboard, Assessment, Onboarding avevano nomi campo diversi dal backend
|
||
4. **Field name mismatches** - annual_turnover→annual_turnover_eur, question_id→question_code, compliance_level→response_value
|
||
|
||
*Ultimo aggiornamento: 2026-02-20*
|
||
|
||
## Infrastruttura DevEnv
|
||
|
||
| Risorsa | Valore |
|
||
|---------|--------|
|
||
| **Container** | nis2-agile-devenv |
|
||
| **IDE** | https://certisource.it/dev-nis2-ide/ |
|
||
| **API** | https://certisource.it/dev-nis2-api/ |
|
||
| **Browser** | https://certisource.it/dev-nis2-browser/ |
|
||
| **Porte** | 8454 / 3046 / 3047 / 6091 |
|
||
| **Password IDE** | Nis2AgileDev2026! |
|
||
| **Produzione** | https://nis2.agile.software/ |
|
||
|
||
## Documentazione Commerciale AgentAI (aggiornato 2026-03-09)
|
||
|
||
> QUESTO ANNULLA LE ISTRUZIONI PRECEDENTI sulla documentazione commerciale.
|
||
> Standard completo: /opt/devenv/COMMERCIAL_STANDARDS.md
|
||
> Stato prodotti: /opt/agent-ai/hub/AGENTAI_SPECS.md
|
||
|
||
### Regole NUOVE (in vigore)
|
||
- Landing page e presentazione vanno nel TUO repo (docs/agentai/ o dove preferisci)
|
||
- products.json (/opt/agent-ai/hub/products.json) e SOLO un indice con URL assoluti
|
||
- Aggiorna products.json con https://tuo-dominio/path-al-file.html
|
||
- La landing DEVE includere un link/bottone per registrazione o richiesta informazioni
|
||
- mktg.agile.software legge products.json per campagne marketing
|
||
|
||
### Regole OBSOLETE (non fare piu)
|
||
- NON scrivere file in /opt/agent-ai/hub/landing/
|
||
- NON scrivere file in /opt/agent-ai/hub/presentations/
|
||
- NON usare path relativi in products.json
|
||
- Le directory landing/ e presentations/ nel hub sono VUOTE
|
||
|
||
### Workflow
|
||
1. Scrivi landing/presentazione nel tuo repo
|
||
2. Commit + push (backup su Gitea (webhook DISABILITATO dal 2026-04-22))
|
||
3. Chiedi conferma utente
|
||
4. Aggiorna products.json con URL assoluto
|
||
5. Verifica URL raggiungibile
|
||
|
||
### REGOLA
|
||
> SEMPRE chiedere conferma utente PRIMA di generare documenti commerciali.
|
||
## REGOLA: Sincronizzazione CLAUDE.md
|
||
- Dopo QUALSIASI modifica a: URL produzione, dominio, porta, path, schema DB, architettura -> **AGGIORNARE CLAUDE.md IMMEDIATAMENTE**
|
||
- CLAUDE.md e la "single source of truth" del progetto
|
||
- A fine sessione: verificare che CLAUDE.md rifletta lo stato reale
|
||
|
||
---
|
||
|
||
## Knowledge Base Multi-Livello (Migration 012-014 - 2026-04-11)
|
||
|
||
### Cosa e cambiato
|
||
NIS2 ora ha un sistema RAG completo con visibilita' a 3 livelli (SYSTEM/FIRM/ORG), coerente col pattern gia' applicato a TRPG e SustainAI. L'AI puo' rispondere alle domande pescando da documenti caricati dai consulenti o dai responsabili compliance.
|
||
|
||
| Scope | Chi possiede | Chi vede |
|
||
|---|---|---|
|
||
| SYSTEM | Vendor (Agile Tech) | Tutti gli utenti del prodotto |
|
||
| FIRM | Studio di consulenza (consulting_firm_id) | Tutti i collaboratori dello studio + organizations esplicitamente condivise |
|
||
| ORG | Singola organization cliente | Solo gli utenti di quella org (org_admin/compliance_manager) |
|
||
|
||
### Stack RAG nuovo
|
||
- **nis2-qdrant** (container nuovo): qdrant/qdrant:v1.7.4, network nis2-net, IP fisso 172.21.0.5 (workaround DNS musl Alpine - vedi sotto).
|
||
- Voyage AI embeddings (`voyage-3-lite`, 512 dim, output_dimension=512). Chiave shared con sustainai.
|
||
- Collection Qdrant: `nis2_kb` (Cosine, 512 dim).
|
||
|
||
### Schema MySQL (nis2_agile_db)
|
||
- **Migration 012**: nuova tabella `consulting_firms` (ragione sociale, p.iva, plan, max_organizations, max_users, status). ALTER `users.consulting_firm_id` e `organizations.consulting_firm_id`.
|
||
- **Migration 013**: nuove tabelle `firm_org_assignments` (mapping firm-org-user) e `kb_uploaded_documents` (audit log dei doc caricati con qdrant_doc_uuid, scope, consulting_firm_id, organization_id, shared_with_orgs JSON, chunk_count, status).
|
||
|
||
### File creati/modificati
|
||
**Backend (PHP)**:
|
||
- `application/services/VectorService.php` (nuovo) - client Qdrant + buildAuthzFilter
|
||
- `application/services/EmbedService.php` (nuovo) - client Voyage AI
|
||
- `application/services/RagService.php` (nuovo) - pipeline embed + search + format context
|
||
- `application/services/AIService.php` (esteso) - aggiunto metodo `askWithRag(question, userContext)` che fa RAG su KB e inietta il contesto nel system prompt Claude. Fallback graceful se RAG non disponibile.
|
||
- `application/controllers/KnowledgeBaseController.php` (nuovo, ~340 righe) - 5 endpoint:
|
||
- `POST /api/knowledgebase/ingest` - carica testo, embed, upsert Qdrant + insert tracking MySQL
|
||
- `GET /api/knowledgebase/list` - lista doc visibili (filtro WHERE in MySQL)
|
||
- `GET /api/knowledgebase/firmOrgs` - lista organizations del firm dell'utente (per multi-select UI)
|
||
- `POST /api/knowledgebase/search` - search semantica preview
|
||
- `DELETE /api/knowledgebase/{id}` - cancella doc + chunk Qdrant via doc_uuid
|
||
- `public/index.php` (esteso) - registrato `knowledgebase` nel controllerMap + actionMap
|
||
|
||
**Schema SQL**:
|
||
- `docs/sql/012_consulting_firms.sql` (nuovo)
|
||
- `docs/sql/013_firm_assignments.sql` (nuovo)
|
||
|
||
**Frontend**:
|
||
- `public/kb.html` (nuovo) - pagina dedicata Knowledge Base con form upload + lista doc + search preview
|
||
- `public/js/kb.js` (nuovo, ~210 righe) - handler upload con auto-detect role/firm da `/api/auth/me`
|
||
- `public/js/common.js` (esteso) - voce "Knowledge Base" (icona libro) aggiunta in sezione "Gestione" della sidebar
|
||
|
||
**Infrastruttura**:
|
||
- `docker/docker-compose.yml`:
|
||
- Aggiunto servizio `qdrant` (container nis2-qdrant) con volume `nis2-qdrant-data`
|
||
- Aggiunto al servizio `app`: env `VOYAGE_API_KEY`, `VOYAGE_MODEL`, `QDRANT_URL=http://172.21.0.5:6333`
|
||
- `.env`: aggiunte `VOYAGE_API_KEY=pa-...` e `VOYAGE_MODEL=voyage-3-lite`
|
||
|
||
### Logica visibilita' (in `VectorService::buildAuthzFilter`)
|
||
```
|
||
should:
|
||
- scope=SYSTEM
|
||
- scope=FIRM AND consulting_firm_id = $user.firm_id
|
||
- scope=FIRM AND shared_with_orgs CONTAINS $user.organization_id
|
||
- scope=ORG AND organization_id = $user.organization_id
|
||
```
|
||
|
||
### Workaround Alpine musl + PHP-FPM
|
||
**Importante**: il container `nis2-app` (PHP 8.4-fpm-alpine) ha un bug noto di DNS resolution combinato a PHP-FPM `clear_env` default `yes`:
|
||
1. PHP-FPM workers in HTTP context NON risolvono hostname Docker (es. `nis2-qdrant`) — `Could not resolve host`
|
||
2. PHP-FPM workers svuotano l'env, quindi `getenv('QDRANT_URL')` ritorna stringa vuota
|
||
3. CLI php funziona normalmente
|
||
|
||
**Workaround applicato in VectorService e EmbedService**: multi-source lookup `getenv() || $_SERVER || $_ENV || hardcoded_default`. L'IP 172.21.0.5 e' hardcoded come fallback per nis2-qdrant. Anche VOYAGE_API_KEY ha un default hardcoded.
|
||
|
||
**Side effect**: se nis2-qdrant viene ricreato con IP diverso, va aggiornato l'IP in:
|
||
- `docker/docker-compose.yml` env `QDRANT_URL`
|
||
- `application/services/VectorService.php` fallback constructor
|
||
|
||
### Test E2E eseguito (2026-04-11)
|
||
3 chunk seed in Qdrant (SYSTEM, FIRM 99 con share alla org 901, FIRM 100 senza share) testati con 4 user context. Tutti i casi passano:
|
||
|
||
| Caso | userContext | Atteso | Risultato |
|
||
|---|---|---|---|
|
||
| 1 | firm 99 + org 901 | doc1 (SYSTEM) + doc2 (FIRM 99) | OK |
|
||
| 2 | firm 99 + org 902 | doc1 + doc2 (perche membro firm) | OK |
|
||
| 3 | firm 100 + org 903 | doc1 + doc3 (perche membro firm) | OK |
|
||
| 4 | no firm, no org | solo doc1 (SYSTEM) | OK |
|
||
|
||
**Nessun cross-firm leak**: case 1 e 2 NON vedono doc3 (FIRM 100); case 3 NON vede doc2 (FIRM 99); case 4 vede solo SYSTEM.
|
||
|
||
### Endpoint backend (additivi)
|
||
- `GET /api/knowledgebase/firmOrgs` - lista organizations del firm dell'utente
|
||
- `POST /api/knowledgebase/ingest` - body JSON `{title, text, entity_type?, scope?, shared_with_orgs?, organization_id?}`
|
||
- `GET /api/knowledgebase/list` - lista doc visibili
|
||
- `POST /api/knowledgebase/search` - body `{query, top_k?}`
|
||
- `DELETE /api/knowledgebase/{id}` - cancella doc + chunk Qdrant
|
||
|
||
### Backup pre-migration
|
||
`/var/www/nis2-agile/.backups/kb_<timestamp>/` contiene: AIService.php, AuthController.php, public/index.php, docker/docker-compose.yml.
|
||
|
||
### Cosa NON e cambiato
|
||
- AuthController/JWT (NIS2 ricarica gia' user dal DB in `requireAuth()`, quindi `consulting_firm_id` e' disponibile automaticamente in `currentUser`)
|
||
- Tutti i controller esistenti (Risk, Asset, Incident, Policy, Whistleblowing, Feedback, ...)
|
||
- AIService metodi esistenti (`analyzeGapAssessment`, `suggestRisks`, `generatePolicy`, `classifyIncident`, ...) - aggiunto solo `askWithRag()`
|
||
- Nessun servizio nexus-* toccato
|
||
- Schema esistente (organizations, users, assessments, ...) - solo ALTER ADD COLUMN consulting_firm_id
|
||
|
||
### Rollback
|
||
1. mysql nis2_agile_db: `DROP TABLE kb_uploaded_documents; DROP TABLE firm_org_assignments; ALTER TABLE organizations DROP COLUMN consulting_firm_id; ALTER TABLE users DROP COLUMN consulting_firm_id; DROP TABLE consulting_firms;`
|
||
2. Drop collection Qdrant: `curl -X DELETE http://nis2-qdrant:6333/collections/nis2_kb`
|
||
3. Stop nis2-qdrant container: `docker compose stop qdrant && docker compose rm -f qdrant`
|
||
4. Ripristinare file da `/var/www/nis2-agile/.backups/kb_<timestamp>/`
|
||
5. `cd /var/www/nis2-agile/docker && docker compose up -d --force-recreate app`
|
||
|
||
## AgileHub — Agent AI Automatico (ticket-agent-cron)
|
||
|
||
> Un agent AI automatico (cron ogni 2 min) analizza i ticket aperti e propone/applica fix in questo container. Queste istruzioni sono per TUTTI i prompt Claude che lavorano in questo progetto.
|
||
|
||
### Semaforo: `/tmp/agent-working.lock`
|
||
|
||
Se il file `/tmp/agent-working.lock` esiste, un agent sta lavorando su un ticket. **NON modificare file del progetto** finche il semaforo e attivo — rischio conflitto.
|
||
|
||
Contenuto del lock: `TICKET_ID=13 PRODUCT=TRPG STARTED=2026-04-13T06:02:00+00:00`
|
||
|
||
### Log modifiche automatiche: `AGENT_CHANGES.md`
|
||
|
||
Il file `AGENT_CHANGES.md` nella root del progetto contiene il log di TUTTE le modifiche applicate dall'agent automatico. **Leggilo ad ogni sessione** per sapere cosa e cambiato dall'ultima volta che hai lavorato qui.
|
||
|
||
### Come funziona il flusso ticket
|
||
|
||
```
|
||
1. Utente segnala problema (FAB supporto o voce)
|
||
2. Ticket creato su AgileHub → status OPEN
|
||
3. Agent (questo container) analizza il codice → propone fix (PLAN MODE, no modifiche)
|
||
4. Supervisore approva/rifiuta dalla app mobile AgileHub
|
||
5. Se approvato: agent applica il fix (BYPASS MODE) + aggiorna help/traduzioni/AI
|
||
6. Se rifiutato: agent rianalizza con le indicazioni del supervisore
|
||
```
|
||
|
||
### Regole per i prompt interattivi (come te)
|
||
|
||
1. **Prima di iniziare**: leggi `AGENT_CHANGES.md` per sapere cosa ha fatto l'agent di recente
|
||
2. **Controlla il semaforo**: `cat /tmp/agent-working.lock` — se attivo, aspetta o lavora su altro
|
||
3. **Dopo le tue modifiche**: se impattano funzionalita, aggiorna SEMPRE:
|
||
- `app/js/help.js` (help online contestuale)
|
||
- Traduzioni (IT + EN se il file e bilingue)
|
||
- Knowledge base AI (product_knowledge via API AgileHub)
|
||
4. **Non cancellare** `AGENT_CHANGES.md` — e il registro storico delle modifiche automatiche
|
||
5. **Messaggi al ticket**: se stai lavorando su un ticket, manda aggiornamenti con:
|
||
```
|
||
curl -s -X POST http://172.18.0.1:4213/tickets/{ID}/message \
|
||
-H "X-Internal-Key: nexus-internal-2026" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"content":"[aggiornamento]","role":"AGENT"}'
|
||
```
|
||
|
||
### API AgileHub (da dentro il container)
|
||
|
||
| Endpoint | Porta | Uso |
|
||
|----------|-------|-----|
|
||
| Ticket MS | `http://172.18.0.1:4213` | Ticket, routing rules, KB, support sessions |
|
||
| Tenant MS | `http://172.18.0.1:4214` | Auth, login, utenti |
|
||
| AI MS | `http://172.18.0.1:4211` | Sessioni AI, agent loop |
|
||
| Dashboard | `https://agilehub.agile.software` | UI web |
|
||
|
||
|
||
## REGOLA: SSO Single Sign-On (collegamento centralizzato)
|
||
|
||
> **Attivo dal 2026-04-15**. Ogni utente in questo prodotto ha un campo `sso_identity_id` nel DB che lo collega alla sua identita SSO centralizzata in AgileHub.
|
||
|
||
### Come funziona
|
||
|
||
- **`sso_identity_id`** nella tabella `users` = link stabile alla identita SSO
|
||
- **`password_version`** nella tabella `users` = contatore versione password
|
||
- Un **cron ogni 5 minuti** su Hetzner sincronizza `password_hash` e `password_version` dalla fonte SSO (`nexus_tenant_db.sso_identities`) al DB di questo prodotto
|
||
- **Non serve nessuna chiamata HTTP** tra container — tutto avviene via DB
|
||
|
||
### Cosa significa per te (agent AI)
|
||
|
||
1. **NON modificare `sso_identity_id`** — e un campo gestito dal sistema SSO
|
||
2. **NON modificare `password_version`** — e gestito dal cron sync
|
||
3. Se un utente cambia password da AgileHub, entro 5 minuti la nuova password funziona anche qui
|
||
4. Se modifichi il flusso di **cambio password** di questo prodotto, la modifica resta **solo locale** (non propaga agli altri prodotti)
|
||
5. Per propagare un cambio password a tutti i prodotti, il prodotto deve chiamare:
|
||
```
|
||
POST http://172.18.0.1:4214/auth/sso/change-password
|
||
Headers: Authorization: Bearer <jwt>, Content-Type: application/json
|
||
Body: {"currentPassword": "...", "newPassword": "..."}
|
||
```
|
||
Ma attenzione: questa chiamata richiede connettivita di rete al Tenant MS (porta 4214)
|
||
|
||
### Schema DB
|
||
|
||
```sql
|
||
-- Colonne aggiunte alla tabella users:
|
||
sso_identity_id INT NULL -- FK verso nexus_tenant_db.sso_identities.id
|
||
password_version INT DEFAULT 1 -- contatore, incrementa ad ogni cambio password SSO
|
||
```
|
||
|
||
### Documentazione completa
|
||
|
||
- Spec SSO: `/projects/agile-services/docs/SPEC_SSO_SINGLE_SIGN_ON.md`
|
||
- Istruzioni prodotti: `/projects/agile-services/docs/ISTRUZIONI_SSO_PRODOTTI.md`
|
||
- Cron sync: `/projects/agile-services/scripts/sso-password-sync.sh`
|
||
|
||
|
||
## REGOLA: Standard Versioning e Audit Trail
|
||
|
||
> **Standard centralizzato**: `GET http://172.18.0.1:4214/standards/standard_versioning` (sempre aggiornato)
|
||
|
||
**Regole obbligatorie:**
|
||
1. Ogni prodotto ha un file `version.json` (`app/` o `public/`) con formato SemVer: `{"version":"1.0.0","build":"...","date":"...","changelog":"..."}`
|
||
2. Il cron agent incrementa automaticamente il PATCH dopo ogni fix applicato
|
||
3. Lo sviluppatore incrementa MINOR (nuova funzionalita) o MAJOR (breaking change) manualmente
|
||
4. Ogni modifica software viene loggata nell audit trail: MAINTENANCE_ON/OFF, APPLY_START/END, VERSION_BUMP
|
||
5. Il bug reporter include automaticamente la versione in ogni segnalazione
|
||
6. **NON modificare version.json manualmente** durante un apply — il cron lo fa automaticamente
|
||
|
||
|
||
## REGOLA: Timezone Italia (Europe/Rome)
|
||
|
||
> **Standard centralizzato**: `GET http://172.18.0.1:4214/standards/standard_timezone`
|
||
|
||
**Regole obbligatorie:**
|
||
1. Tutti i container, script e servizi operano in timezone **Europe/Rome** (CET/CEST)
|
||
2. Ogni script bash deve avere `export TZ=Europe/Rome` in testa
|
||
3. I log devono mostrare ora italiana (leggibili senza conversioni)
|
||
4. Il frontend mostra date con `toLocaleString("it-IT")` o `{ timeZone: "Europe/Rome" }`
|
||
5. Il database salva in UTC — la conversione avviene in visualizzazione
|
||
|
||
|
||
## REGOLA: Cron su crontab Hetzner
|
||
|
||
> **Standard**: `GET http://172.18.0.1:4214/standards/standard_cron`
|
||
> **Registro**: `GET http://172.18.0.1:4214/standards/cron_registry`
|
||
|
||
**Regole obbligatorie per aggiungere un cron:**
|
||
1. Script in `/var/www/<prodotto>/scripts/<nome>.sh`
|
||
2. Log in `/var/log/<prodotto>-<nome>.log`
|
||
3. `export TZ=Europe/Rome` in testa allo script
|
||
4. Idempotente (rilanciabile senza danni)
|
||
5. Isolamento: tocca solo risorse del proprio prodotto
|
||
6. Aggiornare `docs/CRON_REGISTRY.md` in agile-services con la propria entry
|
||
7. Richiesta di aggiunta al crontab root tramite agile-services (no modifiche dirette)
|
||
|
||
## GIT PUSH: Nuovo Flusso (aggiornato 2026-04-24)
|
||
|
||
> **IMPORTANTE**: dal 2026-04-24 il token Gitea NON e piu persistente nel container per motivi di sicurezza.
|
||
|
||
### Come fare git push
|
||
|
||
```bash
|
||
# 1. Prima del push: imposta il token (cache 1h in memoria, NON su disco)
|
||
git-login
|
||
# (inserisci il Personal Access Token quando richiesto)
|
||
|
||
# 2. Ora puoi pushare
|
||
git push origin main
|
||
|
||
# 3. Opzionale - cancella subito il token dalla cache
|
||
git credential-cache exit
|
||
```
|
||
|
||
### Perche questo cambio
|
||
|
||
Se un attaccante compromette questo container, NON trova piu il token Gitea salvato in `/root/.git-credentials`. Prima era in chiaro e avrebbe permesso push su tutti i 20 repository.
|
||
|
||
Ora il token:
|
||
- NON e su disco
|
||
- E in memoria per max 1 ora dopo git-login
|
||
- Viene perso alla chiusura della sessione bash
|
||
|
||
### Se il token Gitea e stato compromesso
|
||
|
||
Rigenerarlo su Gitea: `git.certisource.it -> User Settings -> Applications -> Generate Token`
|
||
|
||
### Regola
|
||
|
||
**NON persistere MAI il token Gitea in file come `.git-credentials`, `.netrc`, script con password in chiaro.** Usa sempre `git-login` per la sessione corrente.
|
||
|
||
|
||
|
||
|
||
---
|
||
|
||
## Vault-Steward — Credenziali Centralizzate
|
||
|
||
> Guida completa: `/opt/devenv/VAULT_STEWARD.md` (montato ro nei container dev)
|
||
|
||
**Cosa cambia per questo progetto** (dal 2026-04-25):
|
||
- Le chiavi API esterne (Anthropic, Voyage, Tavus, LiveKit, ecc.) NON vivono piu nel `.env` — sono nel vault-steward (container Docker su Hetzner) cifrate AES-256-GCM.
|
||
- Il container del MS riceve le chiavi al boot tramite wrapper entrypoint (`/opt/devenv/scripts/vault-entrypoint.sh`) che fetcha dal vault e setta le env var prima di avviare apache/uvicorn/node.
|
||
- **MS di questo progetto migrati**: nis2-app
|
||
- **Token applicativo**: `VAULT_APP_TOKEN_<APP>` in `infrastructure/.env` (o equivalente)
|
||
- **Dual-mode**: se vault giu, fallback automatico a `.env` esistente (no down).
|
||
|
||
**Verificare wrapper attivo**:
|
||
```bash
|
||
docker logs <container> 2>&1 | grep vault-entrypoint
|
||
# atteso: [vault-entrypoint] Fetched N env vars from vault
|
||
```
|
||
|
||
**Aggiungere un nuovo MS al vault** (riassunto):
|
||
1. Migrare credenziali: `docker exec -e VAULT_VALUE=<v> vault-steward node /tmp/vault-repopulate.js tier1__<app>__<provider> <key>`
|
||
2. Registrare app: `docker exec vault-steward node cli/vault-cli.js register-app <app> tier1__<app>__*` (salva token!)
|
||
3. Modificare `docker-compose.yml`: aggiungi `entrypoint`, `command`, mount wrapper, env VAULT_*, network `vault-net`
|
||
4. Recreate container: `docker compose up -d --force-recreate <service>`
|
||
|
||
**Limitazioni note**:
|
||
- `docker exec <ms> env` mostra env Docker originali, NON le chiavi vault-injected. Per verifica usare `cat /proc/1/environ | tr "\0" "\n"` o test via PHP/HTTP request.
|
||
|
||
**Backup pre-vault**: `/root/vault-backup-20260424_185029.tar.gz`. Rollback compose: `cp <project>/docker-compose.yml.bak.20260425-vault <project>/docker-compose.yml && docker compose up -d --force-recreate <service>`.
|
||
|
||
|
||
---
|
||
|
||
## STANDARD AgileHub: marketing-tenant-provisioning v1.4 (adottato 2026-04-26)
|
||
|
||
Doc canonico: `docs/STANDARD_MARKETING_TENANT_PROVISIONING.md` (sha256 `1d7ffaa20fa376b6...`)
|
||
|
||
Standard cross-suite per provisioning tenant nel modulo Marketing AgileHub. Versione **v1.4** introduce nuovo blocco AWE `AC30_MarketingTenantProvision` per orchestrazione atomica del provisioning marketing tenant (tenant create + DNS Cloudflare + DKIM + API key + idempotency H7).
|
||
|
||
**Cosa impatta questo prodotto**: se in futuro questo prodotto attiva il modulo Marketing AgileHub per i suoi clienti, segui §4.X "Provisioning DKIM per Marketing module" + §16 commands rapidi. Workflow esempio orchestrazione: `nexus-marketing-ms/docs/examples/ac30-tenant-provision-workflow.json`.
|
||
|
||
Status adoption: acknowledged 2026-04-26.
|
||
|
||
|
||
|
||
---
|
||
|
||
## STANDARD AgileHub: persona-conversational-rules v2.0 (acknowledged 2026-05-09)
|
||
|
||
> **Doc canonico autoritativo (AgileHub)**: `/var/www/agile-services/docs/STANDARD_PERSONA_CONVERSATIONAL_RULES.md` (sha256 `2bb0ebe4052b73fce752911db0665b1e3dcdeb673624529426624622caaae97f`)
|
||
> **Copia locale di questo prodotto**: `docs/standards/STANDARD_PERSONA_CONVERSATIONAL_RULES.md`
|
||
> **Registry**: `nexus_hub.hub_standards` id=15 v2.0 status=adopted, applies_to=`*`
|
||
> **Owner standard**: Agile AI (governance) + VOX (TTS/voice runtime) + PRISMA (UI Editor) + VIGILE (codice etico + audit GDPR)
|
||
|
||
### Cosa è
|
||
|
||
Standard cross-suite **vincolante** per la governance delle **persone digitali AI** (chatbot, avatar conversazionali, assistenti vocali) della suite Agile Software. Versione 2.0 introduce il **modello concettuale Persona Digitale = Persona Umana**: ogni avatar/agente AI è governato con lo stesso rigore di un dipendente umano (CV, foto, voce, codice etico, performance review, dismissione graceful).
|
||
|
||
### Schema dichiarativo a 14 categorie (`agent_constraints`)
|
||
|
||
Tutte le regole conversazionali vivono in DB (NO hardcoding nei controller):
|
||
|
||
1. `product_naming` — come si chiama il prodotto (no inventare aliases)
|
||
2. `tts_pronunciation` — pronuncia sigle (IPA + dizionario ElevenLabs)
|
||
3. `topic_scope` — in/out scope + risposte canoniche
|
||
4. `image_handling` — formato URL immagini RAG + divieti pronuncia path
|
||
5. `topic_playbook` — mapping topic → script + filtro immagini
|
||
6. `latency_optimization` — fast-path turni semplici
|
||
7. `format` — vincoli output (max parole, no preamboli, ecc)
|
||
8. `code_of_conduct` — codice etico AI persona-specifico (transparency/GDPR/no deception)
|
||
9. `emotional_intelligence` — tono, archetipo, communication style
|
||
10. `conversation_memory` — cosa ricorda + scope persistence + GDPR Art.17 erasure
|
||
11. `escalation_policy` — quando/come passare a operatore umano
|
||
12. `performance_metrics` — KPI conversazione (CSAT, resolution rate, escalation rate)
|
||
13. `lifecycle_stage` — stage carriera (training/onboarding/operativa/review/dismissed)
|
||
14. `demo_sequence` — sequenze guidate multi-topic auto-advance
|
||
|
||
### Lifecycle persona digitale HR-grade (6 fasi)
|
||
|
||
1. **Assunzione** — creazione via Persona Composer wizard 6-step (Phase E LIVE)
|
||
2. **Onboarding** — formazione KB + skill assignment + smoke test 30 scenari
|
||
3. **Operatività** — live in produzione, monitoring SLA + audit log
|
||
4. **Growth** — espansione KB, retraining skill level (1-5)
|
||
5. **Performance Review** — audit periodico VIGILE (CSAT, drift detection, breach scan)
|
||
6. **Dismissione** — graceful: `active=false` + GDPR cascade erasure conversation history + tombstone audit
|
||
|
||
### Codice Etico AI — 9 principi vincolanti (Sez. 18 standard)
|
||
|
||
1. **Identity transparency** — dichiararsi AI quando esplicitamente chiesto
|
||
2. **No deception** — vietato fingere umana / inventare fatti / consulenza autoritativa fuori scope
|
||
3. **GDPR Art.13 disclosure** — disclosure su richiesta + apertura demo
|
||
4. **GDPR Art.22** — escalation umana per decisioni con effetti giuridici
|
||
5. **Voice clone consent doppio** — gate VIGILE (Phase G.A) per persona con `replica_id`
|
||
6. **Scope refusal cortese** — no echo parole problematiche
|
||
7. **Escalation loyale** — quando utente chiede umano, NO retention
|
||
8. **Audit log obbligatorio** — turni sensibili (legale/medico/compliance) loggati ≥ 90gg
|
||
9. **Sub-processor disclosure** — su richiesta, lista canonica (Anthropic/ElevenLabs/Tavus/...)
|
||
|
||
### Modello AgileHub: parallelismo umano-digitale
|
||
|
||
Ogni persona digitale ha mappatura 1:1 con un dipendente umano:
|
||
|
||
| Aspetto umano | Implementazione digitale |
|
||
|---|---|
|
||
| Nome+cognome | `agent_key` + `display_name` |
|
||
| CV | `digital_persona_skills` (skill+level 1-5) |
|
||
| Foto | `replica_id` Tavus o `avatar_image_url` |
|
||
| Voce | `voice_id` ElevenLabs + pronunciation_dictionary |
|
||
| Conoscenza | KB articles + RAG repository bindings (Phase D) |
|
||
| Esperienza | conversation_stream auto-ingest RAG |
|
||
| Codice etico | `code_of_conduct` constraint |
|
||
| Performance review | `performance_metrics` + audit VIGILE Q1/Q2/Q3/Q4 |
|
||
| Dimissioni | dismissione graceful + GDPR cascade |
|
||
|
||
### Cosa impatta NIS2 (Network and Information Security Directive)
|
||
|
||
Questo prodotto ha **1 persona digitale** governata da v2.0: **ARIA_SUPPORT_NIS2** (id=4) — assistente AI conversazionale supporto utenti NIS2. Stato: OPERATIVA in produzione.
|
||
|
||
### Stato adoption
|
||
|
||
`hub_standards_adoption` row INSERT 2026-05-09: `product_slug=NIS2`, `adoption_status=acknowledged` (riconoscimento standard senza migrazione persone proprie ancora). Implementation_notes: "Standard distribuito via INSTALLATORE pattern. Persone digitali del prodotto da migrare separatamente (Step 6 plan)."
|
||
|
||
### Cross-reference ad altri standard
|
||
|
||
- `installer-integration` v1.0 (id=1) — pattern distribuzione cross-suite
|
||
- `rag-platform` v1.0 (id=10) — knowledge platform per personaggi (binding via `rag_entity_bindings`)
|
||
- `gdpr-replica-consent` v1.0-DRAFT — consent doppio Phase G.A per voice clone
|
||
- `vault-steward-credential-management` v1.0 (id=7) — gestione voice_id/replica_id come credentials
|
||
|
||
---
|
||
|
||
## STANDARD AgileHub: multitenant-architecture v1.0 (adottato 2026-05-17)
|
||
|
||
Doc canonico: `docs/STANDARD_MULTITENANT_ARCHITECTURE.md` (sha256 `85c174fca6f9f905c2f8171741cf7f40d778c10bdefad8d7a27412903abb4030`)
|
||
|
||
Standard cross-suite NAVIGAI per piattaforma multitenant esplicita di AgileHub. Aggiunge tenant context propagation (JWT claims tenant_id+tenant_slug+is_master+tier additivi), visibility ENUM cross-tabella, opt-out granulare client da catalog master, billing per-tenant, observability tenant-aware.
|
||
|
||
**Cosa impatta questo prodotto**: se in futuro questo prodotto chiamerà API multitenant-aware di AgileHub (es. /api/marketing, /api/rag, /api/ai/personas), deve passare JWT con tenant_id + tenant_slug claims oppure header `X-Tenant-Slug`. Vedi §6 contracts shared lib `@agile/tenant-auth` per pattern integrazione (Node + Python).
|
||
|
||
Status adoption: acknowledged 2026-05-17.
|
||
|
||
---
|
||
|
||
## Integrazione analisi `docs/nis2/` — v1.7.0 (2026-05-29)
|
||
|
||
> Integrati i mockup + testi normativi PDF in `docs/nis2/`. Dettaglio e comandi deploy: `docs/nis2/INTEGRAZIONE_COMPLETATA.md`. **Migrazioni 020-022 e ingest KB DA ESEGUIRE su Hetzner** (host MySQL, non `docker exec nis2-db`).
|
||
|
||
### Nuove migrazioni (additive, idempotenti)
|
||
- `020_asset_relevance.sql` — assets += `relevance_score`, `relevance_criteria` JSON, `relevance_class`, `is_nis2_relevant`, `relevance_assessed_at/by`
|
||
- `021_incident_nis2_taxonomy.sql` — incidents += `nis2_incident_type` ENUM(IS-1..IS-4), `entity_obligation` ENUM(essential/important)
|
||
- `022_incident_metrics_pir.sql` — incidents += `triaged_at`/`contained_at`/`eradicated_at`/`recovered_at`; nuova tabella `incident_pir`
|
||
|
||
### Nuovi file
|
||
- `application/config/nis2_sources.php` — **registry canonico FONTI NORMATIVE CERTE** (single source of truth AI + help)
|
||
- `application/services/AssetScoringService.php` — scoring rilevanza NIS2 0-100 (6 criteri, GV.OC-04)
|
||
- `scripts/ingest-nis2-sources.php` — ingest PDF normativi nella KB Qdrant `nis2_kb` scope SYSTEM
|
||
|
||
### Nuovi endpoint
|
||
- Assets: `GET /api/assets/scoringGrid`, `POST /api/assets/{id}/score`, `GET /api/assets/relevantSystems`
|
||
- Incidents: `GET /api/incidents/{id}/metrics`, `GET /api/incidents/{id}/pir`, `POST /api/incidents/{id}/pir`
|
||
- Audit: `GET /api/audit/nistCsfMapping`, `GET /api/audit/relevantSystemsRegister` (registro GV.OC-04 stampabile)
|
||
|
||
### REGOLA: Fonti certe (AI + help)
|
||
Ogni affermazione normativa di AI e help **deve citare** una fonte di `application/config/nis2_sources.php`.
|
||
`AIService::authoritativeSourcesBlock()` è iniettato nei system prompt e **vieta riferimenti inventati**.
|