12 KiB
Contesto Ultima Sessione
Il 2026-05-29 ci sono state DUE sessioni: pomeriggio (questa, qui sotto) e mattina (TRPG alignment, più in basso).
SESSIONE POMERIGGIO — 2026-05-29 (~15:30→17:10 CEST)
0. Perché "tutti i prompt sono morti improvvisamente"
Il container nis2-agile-devenv si è RIAVVIATO alle 15:25:55 CEST. Prova: uptime (up 4 min) + supervisord.log (supervisord ripartito da pid 98 alle 15:27, nessun crash/respawn interno prima) → restart esterno (probabile reboot VM Hetzner; docker/dmesg non ispezionabili da dentro). Le sessioni Claude girano come processi DENTRO il container → terminate tutte insieme. Nessuna perdita dati (bind mount persistente). Il cron ticket-agent è ripartito normalmente (OPEN_TICKETS.md riscritto alle 15:30 → conferma ripresa, benigno). Niente OOM/disco pieno.
1. Messa in sicurezza sorgenti (git)
HEAD era già = origin/main (4a85abe), ma c'era MOLTO lavoro non committato — incl. l'intero modulo RAG/KB di aprile mai entrato in git. Creati 5 commit su main (⚠️ SOLO LOCALI, NON ancora su Gitea):
1d13166[CHORE] .gitignore: esclude*.bak*,.backups/,.ssh-temp/(quest'ultimo proteggeva una chiave SSH privata!)c0bf7b6[DOCS] standard cross-suite + governance CLAUDE.md + registri agent1d934e4[FEAT] UI: guida.html, index-en, mobile-conversion, ai-assistant, bug-reporter, help/i18n9b53ca3[FEAT] MktgLead getJsonBody + import-feedback-to-nexus + seed demoa7a21fa[FEAT] Knowledge Base RAG (KnowledgeBaseController + VectorService/EmbedService/RagService + kb.html/js + SQL 012/013 + AIService::askWithRag + qdrant in compose)- 🔐 Scrub sicurezza: rimossa la API key Voyage hardcoded da
EmbedService.phpedocker-compose.yml(ora solo in.envgitignored + vault). NON è finita su Gitea. (Valutare rotazione della chiave: era in chiaro su disco, condivisa con sustainai.) - Rimossi 2 duplicati orfani:
application/controllers/{config.php,EmailService.php}(copie identiche dei canonici).
⚠️ TODO CRITICO: i 5 commit sono solo nel
.gitlocale. Per il backup su Gitea servegit-login(token, cache svuotata dal reboot) +git push origin main. Il push tentato è fallito per assenza token. Anche questo file (CONTEXT) e altre eventuali modifiche doc sono da committare/pushare.
2. Modifiche DB PRODUZIONE — utenti/org "Agile"
Scoperto che "Agile" = consulting_firms id 1 → "Agile Technology SRL" (unico studio nel sistema). Il super_admin vive in users.role (enum globale), NON in user_organizations.role.
- ✅ Creato utente Simon Fattori (id 330,
s.fattori@agile.software):role=org_admin,consulting_firm_id=1, utente LOCALE (no SSO,sso_identity_id=NULL), passwordFattori2026!@impostata e verificata conpassword_verify. - ✅ Tagliavini (id 326) declassato
super_admin→org_admin(resta firm 1).roleNON è sincronizzato dall'SSO → cambio stabile. Restano altri super_admin (3 admin@certisource, 4 benassati, 46 worker, 107 sim-b2b). - ✅ Creata organizzazione
Agile Technology SRL(id 129,entity_type=important,sector=ict_services,consulting_firm_id=1,voluntary_compliance=0) per dogfooding NIS2 su sé stessi. org_admin: Fattori (is_primary) + Tagliavini.firm_org_assignments7/8/9. Firm 1 ora gestisce 126/127/128/129. employee_counteannual_turnover_eurlasciati NULL → da completare in onboarding (servono per soglia dimensionale NIS2).- Tutte le scritture in transazione (commit OK), solo su
nis2_agile_db.
Accesso infrastruttura (IMPORTANTE per le prossime sessioni)
- ❌ Dal container devenv NON si raggiunge il DB di produzione (
nis2_user@172.18.0.7→ Access denied;localhostnon ha MySQL qui). - ❌ La chiave SSH documentata
docs/credentials/hetzner_keyè ASSENTE. - ✅ Usata la chiave SSH effimera
.ssh-temp/id_ed25519_nis2-agile_8h_20260529-120834(validità 8h da 12:08 → scade ~20:08 del 29/05) perssh root@135.181.149.254→mysql -h localhost -u nis2_user. Creds DB da/var/www/nis2-agile/.envsul server.
SESSIONE MATTINA — 2026-05-29 (TRPG alignment)
Data: 2026-05-29 Durata: sessione molto lunga — progetto allineamento NIS2↔TRPG completato
Cosa abbiamo fatto
Progetto allineamento NIS2 ↔ TRPG (suite Evix)
Doc canonico: docs/GAP_TRPG_NIS2_ALIGNMENT.md
Analisi gap tra TRPG v1.54.1 e NIS2 v1.0.0 → 18 gap (G01-G18) raggruppati in 5 fasi. 6 decisioni utente confermate (§10 del doc). Tutte le 5 fasi completate in unica sessione. Version 1.0.0 → 1.5.0.
Fase 1 — SSO Federation (v1.1.0)
- Migration
015_sso_columns.sql: aggiungeusers.sso_identity_id,users.password_version - Nuovo
application/services/SsoHelper.php— client SSO dual-mode (cURL nativo, zero deps) AuthController::login()+changePassword()con conditional SSO (dietroSSO_MODE).envsu Hetzner + vault-stewardtier1__nis2-app__sso/internal_key(placeholder)- AgileHub Ticket #220 aperto a team AGILEHUB per estendere
sso-password-sync.sh - SSO_MODE=local di default → comportamento utente invariato
Fase 2 — Multi-device Sessions (v1.2.0)
- Migration
016_active_sessions.sql: tabellaactive_sessions(jti tracking) +refresh_tokens.session_jti BaseController::requireAuth()verifica jti + last_activity throttleBaseController::parseDeviceLabel($ua)— parsing UA-friendlylogin()genera jti + insert active_sessions,logout()revoca selettiva,changePassword()revoca altre sessioni- 3 nuovi endpoint:
GET/DELETE /auth/sessions[/{id}] - UI
settings.htmltab Sicurezza: card "Sessioni Attive" con device list + revoca
Fase 3 — Password Reset + Context Switch (v1.3.0)
- Migration
017_password_reset.sql: tabellapassword_reset_tokens(TTL 30 min, single-use) - Endpoint
POST /auth/forgot-password(risposta opaca anti-enumeration) +POST /auth/reset-password - Pagine HTML:
forgot-password.html,reset-password.html(con strength bar) login.html: link "Password dimenticata?" ora funzionante (era alert manuale)EmailService::sendPasswordReset()aggiunto- Endpoint
POST /auth/switchContextcon rotazione JWT (revoca old session + nuovo jti + organization_id claim) - Dropdown tenant in sidebar esposto a TUTTI gli utenti con ≥2 org (prima solo consulenti)
_switchOrg()in common.js ora chiama switchContext + setTokens
Fase 4 — Impersonate + Preferences + Versioning UI (v1.4.0)
- Endpoint
POST /auth/impersonate(super_admin o consulente stesso firm, TTL 1h, JWT conimpersonated_byclaim, audit log) - Migration
018_user_preferences.sql:users.theme/timezone/notif_email/notif_inapp - Endpoint
GET/PUT /auth/preferences - Sidebar footer mostra versione corrente, click → modal changelog
- common.js
_loadVersionFooter()fetcha/version.jsonal boot
Fase 5 — Branding + Auth-gate (v1.5.0)
- Migration
019_firm_branding.sql: tabellafirm_branding(logo/colori/brand name per consulting firm) BrandingController.php(NUOVO):GET /branding/current(auth opzionale),PUT /branding(super_admin o consulente)- common.js
_loadFirmBranding()applica CSS variables al boot public/js/auth-gate.jscopiato/adattato da TRPG (gate password client-side per documenti riservati)- G15 skip: simulator esistenti coprono demo flows
- G18 skip: refactor controller rinviato (~5gg investimento, valore tecnico)
Scoperta CRITICA durante Fase 3: topologia DB
Vedi MEMORY.md → project_db_topology.md per dettagli.
PHP-FPM nel container nis2-app (clear_env=yes + .env mancante in container) ricade su DB_HOST=localhost di default → connette al MySQL del HOST Hetzner, NON al container nis2-db.
Conseguenza: tutte le migration vanno applicate via:
mysql -u$DB_USER -p$DB_PASS -h localhost $DB_NAME < migration.sql
dal HOST Hetzner, NON docker exec nis2-db mysql.
Lo si è scoperto perché Fase 3 ha fatto 500 con "table password_reset_tokens doesn't exist" anche se la migration era stata applicata "con successo" — andava sul DB sbagliato.
File creati/modificati (riepilogo)
SQL (5 migration nuove, tutte applicate al DB host):
docs/sql/015_sso_columns.sqldocs/sql/016_active_sessions.sqldocs/sql/017_password_reset.sqldocs/sql/018_user_preferences.sqldocs/sql/019_firm_branding.sql
PHP nuovi:
application/services/SsoHelper.phpapplication/controllers/BrandingController.php
PHP modificati:
application/controllers/AuthController.php(+13 metodi)application/controllers/BaseController.php(requireAuth + parseDeviceLabel + generateRefreshToken)application/services/EmailService.php(+sendPasswordReset)application/config/config.php(+2 costanti)public/index.php(+12 route + branding controller)
HTML/JS:
public/login.html(link forgot-password)public/forgot-password.html(NUOVO)public/reset-password.html(NUOVO)public/settings.html(tab Sicurezza con sessions)public/js/common.js(version footer + branding loader + tenant switcher esposto)public/js/auth-gate.js(NUOVO)public/version.json(1.0.0 → 1.5.0)
Doc:
docs/GAP_TRPG_NIS2_ALIGNMENT.md(NUOVO, piano completo + stato esecuzione)
Backups creati (tutti con timestamp 20260529-*):
/projects/nis2-agile/application/controllers/*.bak.pre-{sso,sessions,fase3,fase4}-*/projects/nis2-agile/public/*.bak.pre-{sessions,fase3,fase4}-*- Hetzner
/var/www/nis2-agile/.backups/*
File deployati su Hetzner
Tutti via scp -i .ssh-temp/id_ed25519_nis2-agile_8h_*:
- 4 PHP in
/var/www/nis2-agile/application/controllers/ - 1 PHP in
/var/www/nis2-agile/application/services/ - 1 PHP in
/var/www/nis2-agile/application/config/ - 5 HTML/JSON in
/var/www/nis2-agile/public/ - 2 JS in
/var/www/nis2-agile/public/js/
Nessun docker restart eseguito (non necessario, bind mount + PHP-FPM rilegge ad ogni request).
Decisioni utente confermate (§10 doc)
- SSO_MODE iniziale:
local(switch a dual dopo ≥7gg validazione) - Backfill SSO: lazy on-login (no script massivo)
- Sessioni concorrenti: nessun limite
- TTL token reset: 30 min
- Cadenza version bump: MINOR per fase (eseguito 1.1.0 → 1.5.0)
- Branding white-label: mantenuto in Fase 5 (eseguito)
Problemi aperti / dipendenze esterne
- AgileHub Ticket #220 (priorità per switch a
dual): estenderesso-password-sync.shper includere DBnis2_agile_db. Status: OPEN, dispatchGroup=RESOLVER, dispatchProduct=AGILEHUB. - vault placeholder
tier1__nis2-app__sso/internal_key: contienePLACEHOLDER_REGISTER_WITH_TENANT_MS_BEFORE_ACTIVATING_DUAL_MODE. Va sostituito con chiave reale ottenuta dal Tenant MS prima del switch. docker compose up -d --force-recreate apprichiesto per attivare env vaultSSO_INTERNAL_KEYquando si passa a dual (oggi opzionale, defaults sani lato codice).SSO_MODE=local→dual: modifica una sola riga in.env. Da eseguire solo dopo aver risolto le 3 dipendenze sopra. Bumpare version dopo (oltre 1.5.0).
Prossimi passi consigliati
- Validare 1.5.0 in produzione con il primo utente reale (test password reset + sessions UI + tenant switcher)
- Attendere risoluzione AgileHub Ticket #220
- Ottenere SSO_INTERNAL_KEY dal Tenant MS
- Replace placeholder vault:
docker exec vault-steward node /app/cli/vault-cli.js migrate tier1__nis2-app__sso internal_key '<real>' - Cambiare
.envsu Hetzner:SSO_MODE=local→SSO_MODE=dual docker compose up -d --force-recreate appper ricaricare env- Smoke test login utente con identità SSO esistente
- Bumpare a 1.6.0 con changelog "SSO dual mode attivato"
File chiave da sapere
CLAUDE.md— single source of truth governancedocs/GAP_TRPG_NIS2_ALIGNMENT.md— piano 5 fasi + stato esecuzioneMEMORY.md(auto) →project_db_topology.md— lezione CRITICA su DB host vs containerMEMORY.md(auto) →project_alignment_trpg.md— stato progetto allineamento