AI On-Premise • Architettura di Sicurezza • SaaS Svizzero
Logo a scudo di Swiss Core Systems

SWISS CORE
SYSTEMS

Un assistente AI on-premise per gli studi professionali svizzeri, costruito perché un modello linguistico compromesso non possa comunque far fare nulla al server.

Workspace AI single-tenant per studi legali, banche private, fiduciarie, gestori patrimoniali e notai del Ticino — dove i dati dei clienti sono coperti dal segreto professionale (art. 321 CP), dalla vigilanza FINMA e dalla nuova LPD, e l'AI in cloud non è un'opzione.

Single-tenant per installazione Persistenza dei soli metadati Bilingue IT / EN Beta — funnel demo attivo

Ruolo

Unico architetto e sviluppatore

Prodotto, backend, frontend, sicurezza, infrastruttura, documentazione di conformità

Tempistiche

~7 settimane

29 giu → 15 ago 2026, sviluppo attivo

Stato

Beta, in produzione

Demo per i prospect attiva su demo.swisscoresystems.com

Mercato

Studi ticinesi

Legale, banca privata, fiduciario, gestione patrimoniale, notarile

Numeri verificati

Entrambi i controlli di qualità sono stati rieseguiti durante la stesura di questo caso studio: pytest → 878 superati · vite build → 1755 moduli, 234 KB gzip. Nessun test tocca la rete.

878

test backend superati

75

endpoint API

52

connettori dati revisionati

42

task AI dichiarativi in 8 settori

29.5k

righe di codice backend

22.7k

righe di codice frontend

12.6k

righe di codice di test

12

tabelle database · 11 migrazioni

Il problema

Gli studi professionali svizzeri custodiscono esattamente il tipo di dati che rende impraticabile l'AI in cloud: fascicoli coperti dal segreto professionale, dati bancari sotto vigilanza FINMA, dati personali sotto la nuova LPD e il GDPR. Uno studio legale di Lugano non può incollare il mandato di un cliente in ChatGPT — non perché il risultato sarebbe scadente, ma perché farlo è già una divulgazione.

Il prodotto vende prima sicurezza e discrezione, poi funzionalità. È l'inversione dell'ordine abituale, e si vede ovunque nel codice: un difetto di sicurezza o di onestà dei dati conta più di una funzione mancante. Tre vincoli hanno guidato ogni decisione:

01

Single-tenant per installazione

Ogni studio ha la propria istanza. Nessun database condiviso, nessun percorso di query tra clienti diversi in cui poter sbagliare.

02

Persistenza dei soli metadati

Il testo dei documenti caricati vive in un dizionario in memoria e non tocca mai il disco. I contenuti delle chat vengono salvati solo se un operatore attiva esplicitamente il logging con scadenza TTL.

03

Nessuna fiducia al modello

Né su una destinazione, né su un permesso, né su un fatto. Al componente non fidato l'autorità è negata strutturalmente — dal codice, non da una policy.

Il cuore ingegneristico

Il modello non sceglie mai dove il server invia una richiesta

La funzione dashboard esiste perché un gestore patrimoniale vuole un desk di mercato. Il design esiste perché quella funzione mette in mano a un modello linguistico la capacità di generare richieste dentro una rete privata. Quattro confini lo impediscono — ciascuno imposto dal codice:

01

Authoring — un linguaggio di selezione, non di espressione

L'unico output machine-readable del modello è una specifica di dashboard validata fail-closed. Nessun campo può contenere URL, hostname, porte, percorsi, header, credenziali o codice — e non esistono aritmetica, template string o formule. Una prompt injection perfetta produce una dashboard sbagliata ma inerte, mai una richiesta.

02

Connettori — le destinazioni sono costanti in codice revisionato

Una specifica nomina un connettore per id di registro e passa valori tipizzati. Host, percorso, metodo, header e credenziali sono costanti in Python revisionato. Se il modello vuole una fonte che non esiste, può solo inoltrare una richiesta in una coda amministrativa — solo una persona aggiunge una fonte dati.

03

Runtime — aprire una dashboard non esegue alcun LLM

Carica la specifica → valida → scarica → normalizza → renderizza. I payload scaricati non rientrano mai in un prompt, quindi una risposta upstream ostile non può diventare prompt injection. La docstring avverte esplicitamente il prossimo ingegnere di non aggiungere un "riassumi questa tile" senza rileggerla.

04

Visibilità — un predicato SQL, mai un filtro a posteriori

Proprietà e condivisione sono imposte nella query su ogni percorso — elenco, ricerca, lettura, modifica, cancellazione e contesto passato al modello. Un utente che jailbreaka completamente l'assistente non può comunque leggere il lavoro di un collega, perché non è l'assistente a decidere.

"Al modello non viene mai affidata autorità. Né una destinazione, né un permesso, né un fatto."

Difesa in profondità, etichettata onestamente

La reale forza di ogni controllo è scritta nel codice — compresi quelli che sono solo tripwire, e l'unico rischio residuo che resta aperto per decisione documentata.

Un tripwire che si dichiara tale

Rilevamento regex delle prompt injection la cui docstring recita: "serve ad allertare, non a prevenire." Registra a audit il caso realistico; a proteggere il server è il confine strutturale.

Ogni risposta upstream è ostile

Una allowlist non dice nulla su ciò che un host approvato restituisce. La struttura del payload è limitata (profondità, chiavi, lunghezze, tetto di 256 KB), i tipi dichiarati sono imposti, i campi di prosa vengono escapati mentre quelli numerici rifiutano il markup all'istante.

Link pubblici congelati

Condividere fuori dallo studio congela i dati dietro un token non indovinabile. Aprirlo è una SELECT — nessun fetch, nessun connettore, nessun modello — così un visitatore anonimo non può mai causare chiamate in uscita dalla rete di uno studio.

Una guardia SSRF portante

Dove l'URL è davvero fornito dall'utente (arricchimento dei prospect), ogni indirizzo risolto deve essere globalmente instradabile, i redirect vengono seguiti a mano ricontrollando ogni salto, e 46 test dedicati verificano che un bersaglio rifiutato non abbia prodotto alcuna richiesta — non solo nessun risultato.

Onestà dei dati come regola dura

Una tile è ok, stale (in grigio, con l'età del dato) o error. Mai uno zero, mai un vuoto, mai interpolata, mai finta — regola imposta anche su snapshot ed export PDF, perché un documento sopravvive allo schermo da cui è nato.

Un contratto d'incertezza

Qui un'allucinazione è un evento di responsabilità civile, quindi un unico contratto — una procedura, non un'esortazione — è scritto una volta e importato in ogni prompt: chiedi quando sei incerto, non inventare mai cifre o citazioni, distingui fonte, conoscenza e assunzione. "Non lo so" è una risposta corretta.

Dentro il prodotto

Il workspace come lo vede uno studio — catturato dalla demo live. Si noti l'etichetta di onestà su ogni cifra di mercato: indicativo, possibile ritardo, con l'ora esatta del rilevamento.

Dashboard desk di mercato di Swiss Core Systems: tile dell'indice SMI con intervalli giornaliero e a 52 settimane e grafico a un mese, tile di quotazione UBS e Nestlé, e notizie di mercato — ogni tile etichettata come indicativa con l'ora del rilevamento

Una dashboard da desk di mercato — SMI con intervalli giornaliero e a 52 settimane, tile di quotazione UBS e Nestlé, notizie di mercato curate. L'intestazione recita "Versione 3": ogni modifica è passata da proponi → rivedi → salva, e un click esporta il desk in PDF o Word.

Chat AI nel settore banca privata che rende un grafico a ciambella della composizione del portafoglio e un grafico dell'andamento come blocchi UI strutturati, con i task rapidi sopra la conversazione

La chat nel settore banca privata, con i blocchi di portafoglio — ciambella di composizione e grafico dell'andamento disegnati dal renderer SVG fatto a mano, con storico scaricato e metriche calcolate lato server, mai dal modello. Sopra i task rapidi; sotto, la dicitura "i messaggi non vengono salvati".

Libreria documenti con un modello di comunicazione di modifica delle condizioni: segnaposto dichiarati, form di compilazione e anteprima a foglio A4 con i segnaposto non compilati evidenziati

La libreria documenti — un modello di modifica delle condizioni con {{segnaposto}} dichiarati, compilazione lato server ("i campi lasciati vuoti vengono segnalati, mai inventati") e l'anteprima a foglio A4 che va a capo dove andrà a capo la stampa.

Che cosa riceve davvero uno studio

Un workspace completo — 8 settori professionali, 42 task AI dichiarativi, dashboard di mercato in tempo reale, una libreria documenti e un funnel commerciale integrato.

Conversazioni ad albero

Modificare un messaggio inviato crea un ramo fratello invece di sovrascrivere la cronologia — entrambe le risposte sopravvivono e l'utente passa dall'una all'altra. Ogni percorso di lettura filtra sul proprietario in SQL.

Un protocollo di blocchi UI, tre renderer

Le risposte dell'assistente contengono blocchi JSON rigorosi — tabelle, grafici, statistiche, form, ticker — resi in SVG fatto a mano nel browser, in docx/pptx sul server e in PDF. Nei documenti i grafici sono seguiti dalle loro cifre come testo reale.

Dashboard dal linguaggio naturale

"Lo SMI e USD/CHF ogni mattina" diventa una specifica validata, con ciclo di riparazione e proponi → rivedi → salva su ogni modifica. Griglia a mosaico, drag-and-drop, cronologia versioni append-only, snapshot del mattino nei giorni feriali.

52 connettori dati revisionati

Banca Nazionale Svizzera, BCE, notizie FINMA, Yahoo Finance, stampa finanziaria svizzera e italiana, GDELT — più indicatori calcolati e cifre ponderate di portafoglio, computate lato server, mai dal modello.

Due archivi documenti, mai mescolati

Gli allegati della chat vivono solo in RAM e muoiono al riavvio; la libreria conserva ciò che l'utente ha archiviato esplicitamente. L'AI propone bozze, l'utente salva — nulla viene persistito senza che qualcuno lo abbia deciso. I controlli deterministici nLPD/GDPR girano sempre.

Export fedeli allo schermo

Un'unica pipeline rende risposte di chat e dashboard in docx, pptx e PDF — con un'anteprima a foglio A4 impaginata per misura reale, così l'anteprima va a capo dove andrà a capo la stampa.

Un funnel commerciale completo, integrato

Richieste demo self-service, arricchimento del sito web con guardia SSRF, punteggio sul cliente ideale, approvazione umana, demo pubbliche per settore dietro identità guest, ed email di outreach con default dry-run e clausola di opt-out LCSl aggiunta automaticamente.

Audit, retention, offboarding

Traccia di audit JSONL append-only, logging dei contenuti opt-in con TTL, politica di retention come dato, purghe ordinate per dipendenza e offboarding degli utenti di prima classe — più un watchdog cron che manda un'email quando il servizio degrada.

Cura del frontend

Un design system piatto inchiostro-su-carta con contrasto AA misurato, ~650 stringhe UI bilingui, grafici SVG costruiti a mano senza librerie, e un tour guidato in 11 passi che porta il browser sulla pagina di ogni funzione.

Architettura di deployment

Disegnata nel linguaggio grafico del prodotto stesso: raggio zero, bordi da 2 px, nessuna ombra. L'assunzione del worker singolo è esplicita — il codice dichiara in quattro punti che, se mai cambiasse, tutte le cache locali al processo migrano a Redis insieme.

nginx :443

TLS · HSTS · CSP · rate limiting · serve il frontend compilato · reverse proxy su /api — origine unica, volutamente nessun CORS da nessuna parte

FastAPI (uvicorn, --workers 1)

Migrazioni Alembic all'avvio · JWT in cookie httpOnly SameSite con sessioni revocabili su database · RBAC come un'unica mappa revisionata (28 azioni × 4 ruoli) · livello LLM astratto per provider (OpenAI · mock · on-premise)

PostgreSQL 16

12 tabelle · solo metadati per default · cronologia versioni delle dashboard append-only

cron
  • · snapshot del mattino — feriali alle 08:45, prima che arrivi il desk
  • · purghe di retention notturne
  • · watchdog di salute ogni 5 min, allerta via email
  • · script di backup e restore, testati e documentati

Cron esterno, ogni job eseguibile a mano — debuggabile alle 09:00 quando un desk segnala uno snapshot mancante.

Stack tecnico

Backend

  • Python 3.12 + FastAPI — API asincrona, architettura a fette verticali
  • SQLAlchemy 2.0 + Alembic — ORM asincrono, migrazioni come unica fonte di verità
  • Pydantic v2 — validazione fail-closed a ogni confine
  • PostgreSQL 16 — database di produzione (SQLite in sviluppo)
  • Livello LLM astratto per provider — OpenAI, mock deterministico, stub on-premise
  • Resend — email transazionali, dry-run per default

Frontend

  • React 18 + Vite 5 — SPA con job in background che sopravvivono alla navigazione
  • Tailwind CSS v4 — token semantici, theming a leva unica, dark mode
  • React Router 6 — workspace + superfici demo pubbliche
  • Zero librerie di componenti — grafici SVG fatti a mano: linee, barre, candele, donut
  • ~650 stringhe UI bilingui — italiano e inglese ovunque

Pratica ingegneristica

Testing

878 test su SQLite in memoria con LLM simulato — nessuna rete. Copertura concentrata sui confini: 46 test solo sulla guardia SSRF.

Configurazione

47 impostazioni tipizzate con default sicuri: email in dry-run, logging dei contenuti spento, e rifiuto di avviarsi in produzione con il segreto JWT di fabbrica.

Documentazione come deliverable

Guida al deployment, runbook, incident response, DPIA su LPD svizzera/GDPR, piano di continuità operativa con RPO/RTO — ciascuno prodotto anche in italiano.

Limiti dichiarati

Nominati di proposito — un portfolio che elenca solo vittorie non è un portfolio, e il codice li nomina da sé.

  • Nessuna certificazione formale. Progettato sugli obblighi di nLPD e GDPR e corredato di DPIA — ma non certificato ISO 27001 o SOC 2, e non lo dichiara.
  • Beta, ed etichettato come tale nell'interfaccia.
  • Nessuna suite di test frontend — la build di produzione è l'unico controllo automatico lato client.
  • Worker singolo per scelta, con il percorso di migrazione a Redis documentato anziché costruito.
  • La finestra TOCTOU dello scraper resta aperta per decisione documentata — chiuderla romperebbe la SNI sull'hosting condiviso usato dalla maggior parte degli studi svizzeri; la rivalidazione dei redirect rende costoso un DNS rebinding.
  • La fonte dei dati di mercato è un endpoint non ufficiale — adeguata per una demo, e il passaggio a un provider con chiave tocca esattamente un modulo: era il motivo per cui è stata isolata.

Che cosa dimostra questo progetto

Architettura di sicurezza sotto un modello di minaccia reale

Non "sanifichiamo gli input" — un sistema progettato in cui al componente non fidato l'autorità è negata strutturalmente, in cui la reale forza di ogni controllo è messa per iscritto, e in cui un rischio residuo accettato è documentato accanto al codice che lo porta.

Giudizio su dove appartiene un confine

Lo stesso problema di HTTP in uscita è risolto in due modi diversi nello stesso codice, perché in un caso la destinazione è una costante e nell'altro è l'input di uno sconosciuto. Riconoscere che servono controlli diversi è la competenza.

Visione di prodotto, non solo implementazione

Funzioni filtrate per settore, perché una dashboard irrilevante è una prima impressione peggiore di nessuna. Un tour che naviga, perché spiegare una pagina da un'altra pagina non funziona. Un funnel, perché una demo che nessuno raggiunge non vende nulla.

Proprietà full-stack

Dal modello dati alle migrazioni, API, streaming, visualizzazioni fatte a mano, design system, testi bilingui, Docker, nginx, cron, backup, incident response, DPIA e continuità operativa — per un prodotto con un deployment reale e un funnel reale alle spalle.

Stai costruendo qualcosa dove la sicurezza conta davvero?

Ottieni una piattaforma AI su misura, un deployment on-premise o un prodotto SaaS completo, ingegnerizzato con la stessa disciplina — dal modello di minaccia agli script di backup.