
Una piattaforma SIEM, EDR e SOAR open-source, self-hosted e basata sull'AI, progettata per le moderne operazioni di sicurezza.
Stack di sicurezza self-hosted per team piccoli/medi che non dispongono di un
SOC dedicato. Installa un agente su ogni endpoint, puntali al server e ottieni
log SIEM, integrità dei file, vulnerabilità dei pacchetti, un esploratore di log
basato su OpenSearch, triage AI autonomo e un motore di playbook SOAR, tutto in
un unico docker compose up.
La parte "AI" è un modello Ollama eseguito localmente (default llama3.2:3b).
I log non lasciano mai la macchina; non c'è chiave OpenAI, né chiave Anthropic,
né comunicazioni verso l'esterno. Se vuoi un modello più intelligente e hai RAM
a disposizione, sostituiscilo in .env.

Raccoglie telemetria dagli agenti Windows e Linux su un singolo canale TCP. Eventi SIEM, alert, FIM, pacchetti, connessioni di rete, porte aperte, attività Docker, frame dello schermo.
Rileva con regole Sigma, sull'endpoint. 23 regole che coprono 23 tecniche
MITRE ATT&CK sono incluse in conf/sigma/builtin/; aggiungi set di regole
della community accanto a esse. Sigma confronta campi nominati anziché
testo, quindi Image|endswith: '\vssadmin.exe' non può essere aggirato dalla
parola "vssadmin" che compare in un messaggio non correlato — e
CommandLine|utf16le|base64offset|contains legge all'interno di un payload
-EncodedCommand in base64, dove la riga di comando in chiaro mostra solo il
wrapper. Misurato sul corpus di valutazione: 9 attacchi su 10 rilevati dal
solo Sigma, 0 falsi positivi su 9 negativi difficili. Questo livello è
deterministico e continua a funzionare quando il modello non è disponibile.
Correla tra eventi, cosa che nessuna regola per singolo evento può fare.
Un singolo accesso fallito è routine; cinque account che falliscono da
un'unica sorgente in quaranta secondi è un password spray, e guardare uno
qualsiasi di quei cinque eventi non te lo dirà mai. Copre spray, brute force,
un successo che arriva dopo ripetuti fallimenti e raffiche di creazione di
account o installazioni di servizi — su entrambe le piattaforme, dagli event
ID di Windows o dalle righe auth.log analizzate.
Viene eseguito da due punti di osservazione, perché vedono attacchi diversi. Per host sull'agente, dove un attacco contro una singola macchina è visibile per intero. E tra host nel percorso di ingestione, dove uno spray che ha percorso un fallimento alla volta su cinquanta macchine emerge — nessun singolo agente vede più di un evento, e questo è l'attacco più competente, poiché spruzzare in modo ampio e superficiale resta sotto sia il lockout per-account che le soglie per-host.
Copertura totale con Sigma: 27 tecniche. Ogni finestra scatta una volta anziché una volta per evento, e ogni contatore è limitato — un contatore basato su un nome utente fornito dall'attaccante è una primitiva di esaurimento della memoria, non un rilevamento.
Mappa i rilevamenti su MITRE ATT&CK, direttamente dalle regole. Le regole
portano tags: attack.t1490, quindi non esiste una tabella di mappatura
mantenuta a mano che possa diventare obsoleta. La pagina di copertura separa
tre stati che un singolo "percentuale di copertura" nasconderebbe:
coperto e visto, coperto e silenzioso, e per niente coperto — quest'ultimo
essendo l'unico in cui il silenzio della console non significa nulla.
Esegue il triage di ogni evento con un LLM locale. Tre worker girano in
parallelo: uno osserva ogni evento in arrivo in tempo reale, uno esegue
scan approfonditi guidati dall'operatore e uno decide se intraprendere
un'azione difensiva (BLOCK_IP, ISOLATE_HOST, KILL_PROCESS, ecc.).
Modalità shadow per il worker difensivo. Attiva AI_SHADOW_MODE=1 e
ogni verdetto autonomo viene messo in attesa di approvazione umana nel SOAR
Hub invece di essere inviato. Utile per ottimizzare il modello sul traffico
reale prima di lasciarlo agire da solo.
Indicizza tutto in OpenSearch così puoi cercare nella tua flotta con query fuzzy / esatte / con prefisso da un unico posto.
Esegue playbook SOAR creati in un piccolo editor visivo con tracciamento dei risultati multi-step e per-nodo, attivabili manualmente o tramite verdetto AI.
Scansione delle vulnerabilità dei pacchetti installati di ogni agente contro OSV (online o tramite un mirror interno).
Recupera feed di threat-intel da abuse.ch (Feodo, ThreatFox, URLhaus) in una tabella di indicatori locale, con pruning per obsolescenza e uno switch per air-gap.
Valida le configurazioni degli agenti prima di inviarle. Parsing YAML, forma strutturale e compilazione delle regex — una regex non valida è YAML valido e disabilita silenziosamente la regola che la contiene.
Desktop remoto integrato tramite streaming JPEG su WebSocket, nessuna installazione VNC separata necessaria sull'endpoint.
La barra laterale raggruppa tutto in tre sezioni: telemetria (dashboard, agenti, alert, asset, FIM, log, AI), automazione della risposta (azioni difensive, playbook, regole di automazione) e amministrazione.
Ogni agente registrato ha la propria pagina con dodici schede. La panoramica mostra misuratori delle risorse in tempo reale, i log SIEM più recenti, i metadati dell'agente e un riepilogo delle minacce:

Gli alert sono tutto ciò che le regole di correlazione dell'agente hanno già segnalato. Colorati per gravità, filtrabili, ricercabili:

La scheda AI Analysis è il lato rivolto all'operatore del LLM locale. Gli scan manuali e automatici arrivano entrambi qui. Ogni insight porta un chip di verdetto, confidenza, indicatori MITRE (quando il modello li restituisce), IOC, passaggi successivi e un pulsante View Source che apre la riga di log esatta che l'AI ha esaminato:

Hardware, software e socket di rete per agente. La scheda Hardware elenca ogni dispositivo PnP, Software elenca i pacchetti installati, Network elenca ogni socket TCP/UDP con il processo proprietario:


Ricerca cross-agente basata su OpenSearch. Scegli un agente, scegli un dataset (eventi SIEM, alert di sicurezza, eventi di processo, rete, FIM, log di audit) e cerca. C'è anche un pulsante per aprire OpenSearch Dashboards (fork di Kibana) per gli utenti avanzati:

Ogni tentativo di accesso alla piattaforma stessa, sia locale che LDAP, con risultato, IP sorgente e timestamp. Utile quando qualcuno è nell'umore "chi-ha-acceduto-quando":

Ti serviranno Docker 24+ con Compose v2 e Python 3.10+ sull'host (solo per il passaggio di build dell'agente una tantum). L'intero stack richiede ~16 GB di RAM, vedi Requisiti di sistema qui sotto.```bash git clone https://github.com/d3vhex/Sentora.git cd Sentora
cp .env.example .env
python scripts/init_secrets.py
cd Sentora ./build_agent.sh # on Linux/macOS/WSL
cd ..
docker compose up --build -d
Open <http://localhost:8000>. Il login predefinito è `admin` / `admin123`.
Cambialo subito in **Users & Roles**.
Ollama scarica automaticamente `llama3.2:3b` al primo avvio. Verifica con:```bash
docker exec sentora-ollama ollama list
Distribuzione di un agente: dalla pagina Deploy Agent, copia il comando one-liner per il sistema operativo desiderato. Sulla macchina target (shell di amministrazione), incollalo. L'installer scarica il binario, crea un file di configurazione, si registra con il server e si configura come attività pianificata / unità systemd.
| Servizio | Porta | Raggiungibile da | Scopo |
|---|---|---|---|
app | :8000 | ovunque | API REST + UI React |
ingest | :5001 | ovunque | Raccolta log TCP (l'agente invia qui) |
db | :3307 | localhost | MySQL 8.0 (3306 all'interno della rete) |
rabbitmq | :5672 / :15672 | localhost | Coda di lavoro + UI di gestione |
ollama | :11434 | localhost | Runtime LLM locale |
opensearch | :9200 | localhost | Ricerca log full-text |
opensearch-dashboards | :5601 | localhost | Explorer in stile Kibana opzionale |
ai-worker-{automation,manual,defensive} | — | Worker di analisi LLM |
Solo app e ingest ascoltano su tutte le interfacce. Gli altri si legano a
BIND_ADDR (default 127.0.0.1), perché nessuno di essi autentica nella
configurazione predefinita — OpenSearch viene eseguito con il plugin di sicurezza disabilitato e
Dashboards è una vista non autenticata di ogni log raccolto. Esponine uno
mettendolo dietro lo stesso reverse proxy e la stessa autenticazione di app, non allargando
BIND_ADDR.
Il pulsante "Open Dashboards" in Log Explorer collega alla porta 5601 sul hostname del server, quindi funziona dall'host stesso; gli operatori remoti necessitano di quel reverse proxy.
| Profilo | CPU | RAM | Disco | Note |
|---|---|---|---|---|
| Laboratorio (≤ 5 agenti) | 4 core | 12 GB | 40 GB SSD | llama3.2:3b, heap OpenSearch a 1 GB |
| Piccolo team (10–50 agenti) | 8 core | 16 GB | 100 GB SSD | I default del compose vanno bene |
| Produzione (50+ agenti) | 16+ core | 32 GB+ | 250 GB+ NVMe | Sposta OpenSearch e Ollama su host dedicati |
Impronta a riposo:
llama3.2:3b): ~3 GB, di più durante l'inferenzaSe hai poca RAM, passa a qwen2.5:1.5b o a un altro modello Ollama piccolo
e riduci l'heap di OpenSearch. Una GPU non è richiesta, ma Ollama
la userà automaticamente se presente.
Quando il verdetto del worker difensivo è ACT con confidenza ≥
AI_AUTO_ACT_CONF (default 0.75) e l'azione raccomandata è nella
lista sicura di seguito, il worker accoda l'azione direttamente nella tabella
automations dell'agente:```
BLOCK_IP KILL_PROCESS RESTART_SERVICE ISOLATE_HOST
DISABLE_USER QUARANTINE_FILE SUSPEND_PROCESS LOGOFF_USER
CONTAINER_ISOLATE CONTAINER_STOP CONTAINER_KILL
### Modalità shadow
Imposta `AI_SHADOW_MODE=1` in `.env` e il worker difensivo smette di eseguire azioni reali. I verdetti che avrebbero innescato una risposta autonoma vengono invece salvati come **proposte**, con `source_file = AI_DEFENSIVE_SHADOW` e `shadow_status = pending`. L'operatore esamina ciascuna di esse da **SOAR Hub > Shadow Queue** e può:
- **Approva** > l'azione SOAR reale (`call_agent_soar`) viene eseguita e la proposta viene marcata come `approved` (con timestamp + nome dell'operatore).
- **Rifiuta** > la proposta viene marcata come `rejected` con una nota opzionale. Non viene intrapresa alcuna azione.
Le proposte non scadono mai; nulla decide al posto tuo. Utile per lasciare che il modello operi sulla telemetria di produzione mentre acquisisci fiducia nei suoi verdetti prima di scatenare veri `BLOCK_IP` / `ISOLATE_HOST` ecc.
---
## Note di sicurezza
### Autenticazione
L'interfaccia utente si autentica con una sessione lato server: il login emette un token opaco in un cookie `HttpOnly`, e `userdb.sessions` è l'autorità. Viene memorizzato solo l'SHA-256 del token, quindi un dump del database non produce nulla di utilizzabile.
Si applicano due orologi, entrambi configurabili in `.env`:
| Impostazione | Predefinito | Significato |
| :--- | :--- | :--- |
| `SESSION_IDLE_MINUTES` | `60` | La sessione scade questo tempo dopo l'ultima richiesta |
| `SESSION_ABSOLUTE_HOURS` | `12` | Limite massimo rigido indipendentemente dall'attività |
| `SESSION_COOKIE_SECURE` | `0` | Imposta a `1` una volta che TLS termina davanti all'app |
| `SESSION_COOKIE_SAMESITE` | `Lax` | `Lax` blocca il POST/XHR cross-site di cui CSRF ha bisogno |
Le sessioni vengono revocate immediatamente al cambio password, al reset della password admin, al cambio di ruolo e all'eliminazione dell'account — un admin che rimuove l'accesso di qualcuno non lascia più funzionante la sua scheda aperta.
Ogni route è **deny-by-default**: senza una sessione valida una richiesta viene rifiutata prima che l'handler venga eseguito. Le eccezioni sono l'endpoint di login, la shell SPA, gli asset statici e gli endpoint rivolti agli agenti, che si autenticano invece con `X-Agent-Key` o un token di enrollment.
`X-User-ID` viene ancora inviato dal frontend, ma non è più identità — il server lo valida rispetto alla sessione e rifiuta una mancata corrispondenza. Poiché i browser non possono allegare header personalizzati a richieste cross-site senza un preflight CORS, richiederlo sulle richieste che modificano lo stato rafforza `SameSite` come secondo controllo CSRF.
La protezione delle route è dichiarata con `@require_permission(...)` e applicata dal middleware tramite un registro, quindi si applica indipendentemente da quale lato di `@app.route` si trovi il decorator. Il log di avvio stampa il totale:```
[Auth] Routes: <n> permission-gated, <n> session-only, <n> public.
Se quella riga riporta 0 permission-gated, RBAC non è applicato — trattalo
come un'interruzione del servizio.
Ogni route deve essere una di tre cose: protetta da permessi, elencata in
_PUBLIC_HANDLERS, oppure nominata in SESSION_ONLY_HANDLERS in
tests/test_auth_wiring.py con un commento che spieghi perché la sola sessione
è sufficiente. Un test lo verifica, quindi una nuova route non può arrivare
silenziosamente senza controllo — ed è così che run_playbook, delete_soar_action e test_ldap_connection
erano finite raggiungibili da qualsiasi account che potesse accedere.
Cinque tentativi falliti per un account, o venti da un singolo indirizzo, entro
quindici minuti, e ulteriori tentativi vengono rifiutati con 429 finché la
finestra non scade. Contati da login_logs, che registrava già ogni fallimento
e che nessuno leggeva — bcrypt era l'unico freno contro l'indovinamento online.
| Impostazione | Default | Significato |
|---|---|---|
LOGIN_MAX_FAILURES_USER | 5 | Fallimenti per account consentiti nella finestra |
LOGIN_MAX_FAILURES_IP | 20 | Fallimenti per indirizzo; più alto, perché un ufficio condivide un unico indirizzo NAT |
LOGIN_LOCKOUT_WINDOW_MIN | 15 | Quanto indietro nel tempo vengono contati i fallimenti |
Il controllo viene eseguito prima del confronto della password, quindi elimina
anche la differenza di tempo tra un nome utente noto e uno sconosciuto. Fallisce
in modalità aperta se login_logs non è raggiungibile: una pagina di login che
non può essere raggiunta perché la tabella di audit è giù è di per sé
un'interruzione del servizio.
X-Forwarded-For viene onorato solo da un peer elencato in TRUSTED_PROXIES
(indirizzi o CIDR separati da virgole, vuoto di default). Con nulla davanti
all'app, l'header è fornito dall'attaccante, quindi crederci incondizionatamente
— che è ciò che questo ha sostituito — permetteva a un chiamante di scrivere
qualsiasi indirizzo nella traccia di audit e di azzerare il proprio limite di
velocità nella stessa richiesta.```
TRUSTED_PROXIES=10.0.0.0/8,192.168.1.5
Lascialo vuoto quando l'app viene raggiunta direttamente.
### L'API dell'agente stesso
L'agente è in ascolto su `0.0.0.0:9099` e gira come SYSTEM o root. Ogni route
richiede `X-Agent-Key`; `/self_destruct` richiede specificamente la chiave di
enrolment di questo stesso agente, quindi un segreto trapelato a livello di
flotta non può disinstallare ogni endpoint in una volta sola. `/health`
risponde alla liveness senza chiave e non rivela altro senza una.
Non esiste un fallback permissivo. Una build precedente accettava qualsiasi
chiave non vuota quando `AGENT_MASTER_SECRET` non era impostato sull'host —
cosa che non veniva mai impostata, quindi era il default ovunque. Un EDR che
fallisce in modalità aperta è peggio di nessun EDR, perché la console riporta
l'endpoint come protetto.
`AGENT_BIND` sposta il listener. È ancora `0.0.0.0` di default perché il
server raggiunge gli agenti via HTTP su questa porta; il binding al loopback
richiede un trasporto sostitutivo, non un cambio di configurazione.
### CORS
`CORS_ORIGINS` è vuoto di default. Nella distribuzione normale questa app
serve la SPA da sola, quindi le richieste sono same-origin e non serve alcuna
voce. Un wildcard viene rifiutato del tutto: i browser rifiutano
`Access-Control-Allow-Origin: *` su qualsiasi richiesta che trasporti cookie.
Le distribuzioni split-origin devono elencare origini esplicite e impostare
`SESSION_COOKIE_SAMESITE=None` con `SESSION_COOKIE_SECURE=1`.
### Segreti di default
`.env.example` viene fornito con segnaposto. Il vero `.env` è git-ignored.
Ruota questi prima di esporre la piattaforma a qualcosa oltre `localhost`:
- `DB_PASSWORD`
- `AGENT_SHARED_SECRET` (fallback di autenticazione agente). Auto-generato al
primo avvio se non impostato.
Il login `admin / admin123` non deve più essere ricordato: l'account seed
viene creato con `must_change_password`, e finché è impostato la sessione non
può raggiungere nulla tranne `/change-password`. Applicato nel middleware
piuttosto che nella UI, perché un flag che il front end è incaricato di
onorare è un suggerimento e l'API risponde anche a curl.
### Certificati TLS
Niente viene fornito con una chiave privata. Una coppia funzionante di
`certs/server.key` e `certs/rootCA.key` era stata committata, il che dava a
ogni distribuzione la stessa identità TLS e la pubblicava: chiunque avesse mai
clonato il repository deteneva la chiave, quindi il certificato non provava
nulla su chi fosse dall'altra parte.
Con `TLS_ENABLED=1` e nessun certificato presente, l'app ne genera uno al
primo avvio. Ogni installazione ottiene la propria chiave e la chiave non
lascia mai la macchina che l'ha creata. `certs/*.key` e `certs/*.crt` sono
git-ignored.
La CA è self-signed, quindi i browser avvisano a meno che tu non la fidi
esplicitamente. Quel avviso è onesto — preferiscilo a un segreto condiviso che
non produce alcun avviso. Per qualsiasi cosa pubblica, punta `TLS_CERT` /
`TLS_KEY` a un certificato reale; quando sono impostati e mancanti, l'app lo
dice piuttosto che sostituirne uno self-signed.
Per rigenerare a mano:```bash
python certs/generate_certs.py --force
Le vecchie chiavi sono ancora nella cronologia di git. Tratta la coppia distribuita prima di questa modifica come compromessa; le nuove installazioni non la usano più.
Il server usa due chiavi Fernet, entrambe generate automaticamente al primo avvio:
| Chiave | Posizione | Protegge |
|---|---|---|
| Chiave agente | data/fernet.key (o FERNET_KEY_PATH) | Telemetria dell'agente; distribuita tramite /api/agents/bootstrap |
| Chiave server | .env FERNET_KEY | Campi interni del server a riposo (es. colonna password) |
chmod 600 su entrambe. Fai un backup. Perderne una rende illeggibili i dati crittografati corrispondenti. Non esiste ancora una rotazione in-place.
Due meccanismi indipendenti, entrambi opzionali:
Arricchimento per-verdict (ai/intel.py). Il worker AI incrocia gli indicatori trovati in un log con AlienVault OTX e VirusTotal. Richiede OTX_API_KEY / VT_API_KEY; se non impostate, non viene effettuata alcuna chiamata esterna.
Feed di indicatori (core/threat_feeds.py). Popola la tabella threat_intel ogni ora da abuse.ch — Feodo Tracker (indirizzi C2 di botnet), ThreatFox (IoC misti con punteggio di confidenza) e URLhaus (URL che distribuiscono malware).
Gli indicatori portano last_seen e vengono eliminati dopo THREAT_INTEL_STALE_DAYS (default 30): un indirizzo che ospitava un C2 lo scorso trimestre di solito ora appartiene a qualcun altro, e mantenerlo produce falsi positivi all'infinito. Ogni feed è limitato a THREAT_INTEL_MAX_PER_FEED righe perché la tabella viene letta sul percorso di alert.
abuse.ch ha spostato i download dietro una chiave account gratuita. Un feed che restituisce 401/403 lo segnala nel log del server; imposta THREAT_INTEL_AUTH_KEY.
Controlla cosa è effettivamente arrivato:```bash docker logs sentora-server | grep ThreatIntel
### Modalità air-gap```ini
OSV_MODE=mirror
OSV_MIRROR_URL=http://osv.internal
THREAT_INTEL_MODE=off
# or serve the feeds internally:
# THREAT_INTEL_FEODO_URL=http://mirror.internal/feodo.json
Con quelle impostazioni, più OTX_API_KEY / VT_API_KEY non impostate, nulla lascia la
rete. I font sono inclusi, Ollama è locale, nessuna CDN viene contattata.
/api/exposure/report conta i pacchetti non aggiornati e gli eventi di integrità dei file
in tutta la flotta, per agente, peggiori per primi. Riporta la propria copertura:
complete: false quando un agente non può essere letto, perché un totale su metà
della flotta non è un totale di flotta.
Non c'è deliberatamente alcun punteggio. L'endpoint in precedenza restituiva
100 - vulns*2 - fim*5 come "punteggio di conformità" — che non corrisponde a nessun framework,
non scala con la dimensione della flotta e si azzera su qualsiasi flotta reale. La classificazione
della gravità è assente per lo stesso motivo: vulnerabilities_report non ha una
colonna di gravità e i suoi campi sono crittografati a riposo, quindi qualsiasi voto dovrebbe
essere inventato.
POST /<agent>/config/<type> valida prima che qualsiasi cosa raggiunga un sensore:
analisi YAML, forma strutturale e — il livello che conta — compilazione delle
regex. Una regex non valida è YAML perfettamente valido e disabilita silenziosamente
la categoria che la contiene, quindi un controllo solo sintattico la spingerebbe direttamente
all'endpoint. L'editor esegue il lint sullo stesso endpoint mentre digiti e
segnala i problemi con numeri di riga cliccabili.
Agent (Win / Linux) │ TCP frames + REST polling ▼ ingest (:5001) ──► RabbitMQ ──► AI worker fleet (3 modes) │ │ ▼ ▼ MySQL (per-agent _db) ai_analysis_results │ ▼ app (Sanic :8000) ──► React UI + REST + WebSocket screen proxy
La versione approfondita (layout per modulo, schema, pipeline AI, autonomia SOAR,
superfici air-gap) è disponibile in
[docs/Sentora_Architecture.md](https://github.com/d3vhex/sentora/blob/HEAD/docs/Sentora_Architecture.md).
Documentazione operativa:
| Documento | Copre |
| :--- | :--- |
| [Architettura](https://github.com/d3vhex/sentora/blob/HEAD/docs/Sentora_Architecture.md) | Layout dei moduli, flusso dei dati, modello di autenticazione, pipeline AI |
| [Deployment di produzione](https://github.com/d3vhex/sentora/blob/HEAD/docs/production-deployment.md) | Dimensionamento, topologia di rete, TLS, backup, monitoraggio, air-gap |
| [Runbook di aggiornamento](https://github.com/d3vhex/sentora/blob/HEAD/docs/update-runbook.md) | Upgrade, rollout degli agent, migrazioni DB, rollback |
| [Rapporto di avanzamento](https://github.com/d3vhex/sentora/blob/HEAD/docs/PROGRESS_REPORT.md) | Cosa è cambiato e perché |
---
## Configurazione di sviluppo```bash
# 1. Database. Only init_userdb.sql — it creates and selects `userdb`.
#
# db/init.sql is NOT a server-init script. It is the per-agent schema
# template, applied by server.create_tables_if_not_exist() after
# connecting to that agent's own database, which is why it contains no
# CREATE DATABASE or USE. Running it standalone fails at line 5 with
# "No database selected" — the same way it broke every first-time
# `docker compose up` while it was mounted into the MySQL init directory.
mysql -u root -p < db/init_userdb.sql
# 2. Backend. requirements.lock pins every version the image is built
# from; requirements.txt is the loose list it was resolved from.
pip install -r requirements.lock
python app.py
# 3. Ingest (separate terminal)
python server.py
# 4. Frontend dev server
cd frontend
npm install
npm run dev
Stesso script, tre ruoli:```bash WORKER_TYPE=automation python ai_worker.py WORKER_TYPE=manual python ai_worker.py WORKER_TYPE=defensive python ai_worker.py
Produzione: lascia che sia `docker-compose.yaml` a occuparsene.
---
## Contributi
I PR sono benvenuti. Prima di aprirne uno:
1. Fork → branch → PR verso `main`.
2. Esegui i controlli:```bash
pytest -ra # no MySQL or RabbitMQ needed
python -m compileall -q app.py core security
cd frontend && npx tsc --noEmit && npm run build && cd ..
# With the stack up — enumerates every route and calls it twice
python scripts/api_smoke_test.py
@require_permission(...). Il log di avvio
stampa il conteggio; se riporta 0 permission-gated, c'è qualcosa che non va
con il cablaggio, non con la tua rotta.Per qualsiasi cosa più grande di una correzione, apri prima un issue così possiamo allinearci sull'approccio.
. ├── app.py # Sanic API + React SPA host ├── server.py # TCP ingest ├── ai_worker.py # AI worker fleet (3 modes) ├── ai/ │ ├── utils.py # LLM helpers, AI cache, SOAR queueing │ └── intel.py # OTX / VT per-verdict enrichment (opt-in) ├── core/ │ ├── mq.py # RabbitMQ publisher │ ├── opensearch.py # OpenSearch index/search │ ├── config_validation.py # Agent YAML validation (parse, shape, regex) │ └── threat_feeds.py # abuse.ch indicator feeds ├── security/ │ ├── session.py # Server-side session store │ └── ssrf.py # Proxy destination rules ├── scanners/ │ └── vuln.py # Server-side OSV scanner ├── scripts/ │ ├── init_secrets.py # Generate the secrets .env needs │ ├── rotate_db_password.py # Rotate the MySQL root password safely │ └── api_smoke_test.py # Exercise every route against a live server ├── tests/ # pytest; no MySQL or RabbitMQ required ├── frontend/ # React 18 + TS SPA │ └── src/lib/ # Shared logic (playbook action catalogue) ├── Sentora/ # Cross-platform agent ├── certs/ # Self-signed dev certs ├── docs/ # Architecture + screenshots └── docker-compose.yaml
---
## Licenza
AGPL-3.0. Vedi [LICENSE](https://github.com/d3vhex/sentora/blob/HEAD/LICENSE).
Usa, modifica, ridistribuisci. La cosa che AGPL aggiunge alla normale GPL:
se esegui una versione modificata su un server di rete con cui altri utenti
interagiscono, devi pubblicare le modifiche anche sotto AGPL.
- Self-hosting per uso interno → nessun obbligo di divulgazione del codice sorgente.
- SaaS pubblico basato su un Sentora modificato → devi pubblicare
le modifiche.
- Vuoi distribuire un derivato closed-source o saltare la clausola di network-copyleft?
È disponibile una deroga di licenza commerciale. Contatta l'autore.
Il nome e il logo "Sentora" sono marchi degli autori del progetto e
non sono coperti da AGPL. Fai pure fork, ma rinomina se ridistribuisci
come prodotto tuo.
---
## Cosa non c'è nella Community Edition
La Community Edition non ha limiti artificiali: nessun limite di agenti, nessun
limite di conservazione, nessun blocco delle funzionalità sul core. Eseguila
quanto il tuo hardware consente.
La distribuzione a pagamento Pro / Enterprise aggiunge funzionalità di integrazione enterprise
(SSO SAML/SCIM, multi-tenancy, report di conformità, HA, audit WORM,
bundle di aggiornamento air-gap firmati, approvazioni SOAR a 4 occhi, forwarder
premium per ticketing/SIEM). La capacità di rilevamento core non viene mai
spostata dietro quel muro.
Se una di queste cose è rilevante per la tua implementazione,
[contattaci](mailto:[email protected]).