## ⏰ 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 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). ## 🔴 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 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 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 && 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_/` 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_/` 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 , 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//scripts/.sh` 2. Log in `/var/log/-.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_` in `infrastructure/.env` (o equivalente) - **Dual-mode**: se vault giu, fallback automatico a `.env` esistente (no down). **Verificare wrapper attivo**: ```bash docker logs 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= vault-steward node /tmp/vault-repopulate.js tier1____ ` 2. Registrare app: `docker exec vault-steward node cli/vault-cli.js register-app tier1____*` (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 ` **Limitazioni note**: - `docker exec 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 /docker-compose.yml.bak.20260425-vault /docker-compose.yml && docker compose up -d --force-recreate `. --- ## 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**.