Piattaforma multi-tenant per dispositivi indossabili di sicurezza. Un orologio al polso rileva un evento — una caduta, un pulsante premuto, un'immobilità prolungata — e il sistema avvisa le persone giuste, nell'ordine giusto, finché qualcuno non prende in carico.
Il sistema notifica. Non soccorre. La catena di avvisi nasce e termina dentro l'organizzazione del cliente: nessun servizio di emergenza viene contattato automaticamente, nessuna centrale opera sotto nostro contratto. Il soccorso resta del datore di lavoro, dove la legge già lo mette. La nostra responsabilità è la consegna, ed è tracciata tentativo per tentativo.
| SecurMIA | Casa MIA | |
|---|---|---|
| Chi protegge | lavoratori | persone fragili a domicilio |
| Dove | cantieri, siti produttivi | abitazioni |
| Chi riceve l'avviso | preposto, datore, console operativa | familiari, caregiver |
I due prodotti condividono l'intero motore. La differenza sta in un verticale —
un pacchetto con sei punti di innesto — oppure nella configurazione del database.
Mai in un if dentro il Core.
Questo vincolo non è una preferenza di stile: è verificato in CI. Un'analisi dell'albero sintattico fallisce la pipeline se il Core nomina un prodotto, e un terzo verticale fittizio esiste apposta per dimostrare che aggiungerne uno non richiede di toccare il motore.
Sei milestone chiuse su dodici. Il backend è la parte matura; la consegna degli avvisi e l'interfaccia collegata non ci sono ancora.
| Cosa | Stato | |
|---|---|---|
| M0–M3 | Modelli, API di lettura, matrice di visibilità, multi-tenancy | fatte |
| M4 | Gateway di ingestione firmato HMAC, due corsie, deduplica, consumer | fatta |
| M5 | Ciclo di vita dell'allarme, cinque segnali derivati, SOS silenzioso, escalation | fatta |
| M6 fase A | Perimetro: catena interna all'azienda, armamento del sito | fatta |
| M6 fase B | Consegna vera: SMS, chiamata, prova immutabile | prossima |
| M7–M11 | Albero dei siti e 2FA, separazione dei prodotti, sicurezza e GDPR, frontend collegato, prova sul campo | da fare |
Un messaggio firmato entra dal gateway, viene deduplicato, ordinato per l'ora del dispositivo e non per quella di arrivo, e finisce su una coda a due corsie: quella critica non cede il passo a niente. Le regole derivate valutano immobilità, anomalia fisiologica, uomo a terra, dispositivo non indossato e geofence, dentro finestre di monitoraggio ricavate dal turno. Un allarme nasce, si verifica, si inoltra lungo una catena a più passi, e si chiude solo con una presa in carico tracciata. Chi vede quale dato è deciso da una matrice di visibilità configurabile: i valori numerici li vede soltanto la persona protetta.
- Nessun avviso parte davvero. Il sistema decide chi avvisare e prepara il messaggio, ma SMS e chiamate non sono ancora collegati: è il M6 fase B.
- L'interfaccia è un mockup statico, non collegata al server (M10).
- Nessuna prova con l'orologio vero: le capacità del dispositivo nel seed sono provvisorie (M11).
- mTLS è predisposto e non funzionante (M9).
L'elenco completo del debito dichiarato, senza sconti, è in backend/docs/STATE.md.
Servono Docker e Docker Compose. Nient'altro.
cd backend
make up # PostgreSQL con TimescaleDB, Redis, API, worker, consumer
make migrate
make seed # scenario dimostrativo: mostra una password, una volta sola
make simulate # telemetria realistica, attraverso il gateway veroL'API risponde su http://localhost:8000, lo schema navigabile su
/api/v1/docs. Il gateway di ingestione sta su http://localhost:8081
(/healthz per lo stato), l'interfaccia statica su http://localhost:8080.
make help elenca il resto. La password degli utenti dimostrativi è generata a
ogni seed e non è scritta da nessuna parte: se si perde, si rigenera con
python manage.py demo_password --user <nome>.
Django 5.2 LTS e DRF per il dominio e le API; un gateway FastAPI separato per l'ingestione, perché parla un protocollo diverso e regge un carico diverso; PostgreSQL 16 con TimescaleDB per la telemetria; Redis Streams fra i due; Celery per il lavoro differito.
I dati viaggiano a tre velocità: la telemetria ad alto volume finisce su hypertable con ritenzione breve, lo stato corrente sta su una riga per dispositivo, e gli eventi — pochi, importanti — si conservano a lungo. I registri di transizione, audit e accesso ai dati sono immutabili per vincolo del database, non per disciplina del codice.
25.000 righe di Python, 360 test verdi, 29 file di test. Ogni commit passa ruff,
import-linter, l'analisi di purezza del Core, l'inventario dei modelli, un
cancello sul vocabolario, pip-audit, bandit e una scansione dei segreti su
tutta la cronologia.
git config core.hooksPath .githooksInstalla l'hook pre-push, che rifiuta i push forzati e le cancellazioni su
main e cerca segreti prima che escano. Serve perché la protezione del ramo di
GitHub non è disponibile sui repository privati del piano gratuito: il divieto
esiste come regola, non come vincolo tecnico. L'hook è una cintura, non un
cancello — protegge solo la copia su cui è installato.
Il sistema non è un dispositivo medico e non è un servizio di soccorso. Non sono sfumature: sono due qualifiche normative che non abbiamo e non vogliamo. Un test in CI rifiuta il linguaggio diagnostico e le promesse di soccorso ovunque, codice e documenti compresi — perché se qualcuno si facesse male contando su una promessa scritta in una brochure, sarebbe la promessa a essere contestata.
La documentazione estesa — dodici documenti per la direzione, per chi entra nel progetto e per la conformità — arriva a breve su un ramo dedicato. Nel frattempo:
- backend/docs/STATE.md — stato per milestone, decisioni prese con la data, debito dichiarato, cancelli di qualità
- backend/docs/PLAN.md — architettura e piano
- backend/docs/compliance/ — destinazione d'uso, Statuto dei lavoratori, ripartizione delle responsabilità
Repository privato. © ECLIPSE MN