# 🚢 Manifesto della Flotta Autonoma v5.1 *Autore: Mauro* *Architetto di Sistema: Mauro & Assistente AI* *Versione: 5.1 (Aggiornato al 17 Ottobre 2025)* *Stato Progetto: **Fase 4 (Maturità Operativa) - Sistema stabile e in produzione*** --- ## ⚡ Quick Access Cheat Sheet ### **Avamposto (VPS @ Hetzner)** - **IP Pubblico:** `168.119.114.146` - **Accesso SSH:** `ssh mauro@168.119.114.146` - **Password Utente `mauro` (per `sudo`):** `FlottaAutonoma2025!` - **IP Tunnel (WireGuard):** `10.0.0.1` ### **Nave (Mini PC @ Casa)** - **IP Locale (Statico):** `192.168.1.51` - **IP Tunnel (WireGuard):** `10.0.0.2` - **Plancia di Comando (Portainer):** `https://192.168.1.51:9443` (o `https://localhost:9443` dalla Nave) - **Accesso Remoto Desktop (Guacamole):** `https://desktop.privcloud.dev/guacamole/` - **Jarvis Ops Hub:** `/mnt/ssd/Jarvis` – centro operativo curato dall'assistente AI (diagnostica, inventario, automazioni) ### **Identità Amministrativa (Chiavi SSH)** - **File Chiave Privata (sulla Nave):** `~/.ssh/id_ed25519` - **Chiave Pubblica (installata sull'Avamposto):** `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDQXFHGc1n2q/AV8Atoy382gJ8oc1C7LlaXMw5P/VFV+ mauro@flotta-autonoma` ### **Dominio e Servizi Pubblici (`privcloud.dev`)** - **Nextcloud:** `https://nextcloud.privcloud.dev` - **Jellyfin (Media):** `https://jellyfin.privcloud.dev` - **Navidrome (Musica):** `https://music.privcloud.dev` - **Heimdall (Dashboard):** `https://home.privcloud.dev` - **Uptime Kuma (Stato):** `https://status.privcloud.dev` - **Desktop Remoto:** `https://desktop.privcloud.dev/guacamole/` - **Paperless (Documenti):** `https://paperless.privcloud.dev` --- ## 1. Visione e Filosofia Il progetto "Flotta Autonoma" crea un'infrastruttura di servizi personali **resiliente, sicura, indipendente e automatizzata**. L'architettura è una flotta distribuita composta da: - **La Nave (Mini PC @ Casa):** Il cuore operativo (`10.0.0.2`), un "magazzino" sicuro basato su Linux Mint. Ospita i container Docker e tutti i dati, con **esposizione diretta a Internet pari a ZERO**. - **L'Avamposto (VPS @ Hetzner):** La nostra "vetrina" pubblica (`168.119.114.146`), un server Ubuntu con IP statico che agisce come reverse proxy e punto di ingresso unico. - **Il Corridoio Blindato (VPN):** Un tunnel **WireGuard** permanente e criptato che collega in modo sicuro la Nave all'Avamposto. - **L'Hub Dati (SSD Esterno):** Il cervello operativo della flotta, montato sulla Nave in `/mnt/ssd`. Contiene tutte le configurazioni, i dati e gli script. - **La Cassaforte Dati (HDD Esterno 5TB):** L'archivio di sicurezza, montato in `/mnt/backup-hdd`. Ospita i backup versionati e criptati dell'Hub Dati, garantendo la recuperabilità in caso di disastro. --- ## 2. Architettura Tecnica Dettagliata ### 2.1. L'Avamposto (VPS Hetzner) - **Hardware:** CX22 (2 vCPU, 4GB RAM, 40GB SSD) - **OS:** Ubuntu 24.04 LTS - **Hardening:** `fail2ban` attivo; accesso SSH solo tramite chiave per l'utente `mauro`. - **Firewall `ufw`:** Attivo. Porte aperte: `22/tcp`, `80/tcp`, `443/tcp`, `51820/udp`. #### Configurazione `/opt/caddy/Caddyfile` (Reverse Proxy): ```caddyfile # --- Flotta Autonoma: privcloud.dev --- jellyfin.privcloud.dev { reverse_proxy 10.0.0.2:8096 } home.privcloud.dev { reverse_proxy 10.0.0.2:8081 } status.privcloud.dev { reverse_proxy 10.0.0.2:3001 } music.privcloud.dev { reverse_proxy 10.0.0.2:4533 } nextcloud.privcloud.dev { request_body max_size 16G header Strict-Transport-Security max-age=31536000; redir /.well-known/carddav /remote.php/dav 301 redir /.well-known/caldav /remote.php/dav 301 reverse_proxy 10.0.0.2:8080 { header_up Host {host} header_up X-Real-IP {remote_ip} header_up X-Forwarded-For {remote_ip} header_up X-Forwarded-Host {host} header_up X-Forwarded-Proto {scheme} } } desktop.privcloud.dev { reverse_proxy 10.0.0.2:8082 } ``` --- ### 2.2. La Nave (Mini PC GMKtec G3) - **Hardware:** 16GB RAM, 512GB SSD + **1TB SSD Esterno (Hub Dati)** + **5TB HDD Esterno (Cassaforte Dati)** - **OS:** Linux Mint #### Struttura Hub Dati (`/mnt/ssd`): ``` /mnt/ssd/ ├── Jarvis/ # Centro operativo AI (diagnostica, inventario, automazioni, script) ├── config/ # Configurazioni persistenti e file docker-compose.yml └── data/ # Dati "umani" (media, documenti, etc.) ``` #### Configurazioni di Sistema Critiche (`/etc/fstab`): - **Hub Dati:** `UUID=... /mnt/ssd ext4 defaults,nofail 0 2` - **Cassaforte Dati:** `UUID=... /mnt/backup-hdd ext4 defaults,errors=remount-ro 0 2` #### Dipendenze di Sistema: - **Docker:** Configurato per avviarsi solo dopo il montaggio di `/mnt/ssd` tramite override systemd in `/etc/systemd/system/docker.service.d/override.conf` - **DNS Docker:** Configurato in `/etc/docker/daemon.json` per usare DNS esterni (`8.8.8.8`, `8.8.4.4`) --- ### 2.3. Gestione Dominio e DNS - **Dominio:** `privcloud.dev` - **Registrar:** Porkbun - **Configurazione DNS:** Due record "A" su Porkbun puntati a `168.119.114.146`: 1. `@` (dominio nudo) 2. `*` (wildcard per tutti i sottodomini) --- ### 2.4. Jarvis Ops Hub (Assistente AI) - **Percorso principale:** `/mnt/ssd/Jarvis` - **Contenuti chiave:** `flotta-diagnostics/`, `inventario/`, `config-backups/`, `logbook/`, `scripts/`, `bootstrap/`, `flotta-autonoma-unified.md` - **Ruolo:** punto di controllo gestito dall'assistente Jarvis per diagnostica, backup, inventory e automazioni. - **Accesso rapido:** `cd /mnt/ssd/Jarvis` ## 3. Servizi Operativi e Sistemi di Supporto ### 3.1. Container Docker Attivi sulla Nave Tutti i servizi sono deployati come "Stacks" da Portainer e sono in modalità "Limited". | Servizio | Porta Interna | Porta Esterna | Stato | |----------|---------------|---------------|-------| | **Portainer** | 9443 | 9443 | ✅ Attivo | | **Nextcloud** (app) | 80 | 8080 | ✅ Attivo | | **Nextcloud** (db PostgreSQL) | 5432 | - | ✅ Attivo | | **Jellyfin** | 8096 | 8096 | ✅ Attivo | | **Navidrome** | 4533 | 4533 | ✅ Attivo | | **Uptime Kuma** | 3001 | 3001 | ✅ Attivo | | **Heimdall** | 80 | 8081 | ✅ Attivo | | **Guacamole** (web) | 8080 | 8082 | ✅ Attivo | | **Guacamole** (guacd) | 4822 | - | ✅ Attivo | | **Guacamole** (db PostgreSQL) | 5432 | - | ✅ Attivo | | **WebDAV** (serve via rclone) | 80 | - | ✅ Attivo | | **Paperless-ngx** | 8000 | 8010 | ✅ Attivo | | **Jarvis Gateway** (FastAPI bridge) | 8710 | 8710 (proxy `jarvis.privcloud.dev`) | 🟡 Da attivare | > Monitoraggio: lo script `flotta-diagnostics/sentinel.py` considera `webdav` tra i container essenziali e segnala eventuali stop o riavvii. ### 3.2. Sistema di Accesso Remoto - **x11vnc:** Configurato come systemd service (`x11vnc-guacamole.service`) - Porta: `5901` - Avvio automatico: ✅ Abilitato - Display: `:0` - Autenticazione: Password file in `~/.vnc/passwd` - **Guacamole:** Stack completo per accesso web al desktop remoto tramite `https://desktop.privcloud.dev/guacamole/` - **WebDAV (rclone serve):** container dedicato che espone l'Hub Dati via protocollo WebDAV per client esterni (WinSCP / rclone remoti). Credenziali e mapping porte sono gestiti nel relativo `docker-compose.yml` salvato in `/mnt/ssd/config/webdav/`. - **Paperless-ngx:** gestione documentale self-hosted raggiungibile su `https://paperless.privcloud.dev` (reverse proxy via Avamposto) o `http://192.168.1.51:8010` in LAN. Configurazione docker-compose in `/mnt/ssd/config/paperless/` con file `.env` (permessi `600`) contenente credenziali Postgres e account admin (`admin` / password `maugag`). Archivio indicizzato in `/mnt/ssd/data/documenti/paperless/archive/`, inbox in `/mnt/ssd/data/documenti/paperless/incoming/`; gli export generati dal sistema vengono depositati in `/mnt/ssd/data/documenti/paperless/.system/export/`. - **Jarvis Gateway:** servizio FastAPI residente in `/mnt/ssd/Jarvis/webapp/` che espone l'assistente tramite API (`POST /api/message`) e WebSocket (`/ws`). Autenticazione obbligatoria via Bearer token definito in `.env`. Systemd unit: `jarvis-gateway.service` → espone `0.0.0.0:8710`, proxy pubblico tramite Caddy su `https://jarvis.privcloud.dev`. Conserva la history in `webapp/data/conversations.db` (SQLite, retention 400 messaggi con backup automatico in `webapp/data/backups/`) e integra il manifesto completo all'interno del prompt iniziale. **ATTENZIONE:** il bridge invoca `codex exec` in modalità `danger-full-access` senza approvazione: proteggere il token come credenziale critica. La webapp è installabile come PWA da Safari (manifest e icone incluse). ### 3.3. Inventario percorsi critici - Script di riferimento: `python /mnt/ssd/Jarvis/inventario/inventory.py` - Uso rapido (dalla cartella `inventario/`): - Vista leggibile: `python inventory.py` - Output JSON: `python inventory.py --json` - Ad ogni esecuzione viene aggiornato automaticamente `inventario/snapshots/inventory_latest.txt`. - Salvataggio personalizzato: `python inventory.py --json --output snapshot.json` (crea o sovrascrive il file indicato, generando cartelle intermedie se necessario). - Lo script restituisce lo stato (esistenza) di tutti i percorsi sensibili: macro-aree dell'Hub Dati (`media/`, `SAFEBOX/`, `SERVER/`), configurazioni Docker, volumi persistenti (Nextcloud, Jellyfin, Guacamole, ecc.), repository Restic e cache (`~/.cache/restic`), configurazioni rclone, credenziali/TLS Portainer, script operativi (backup, sync rclone) e configurazioni di sistema (`/etc/fstab`, `/etc/crontab`, etc.). - Aggiornare l'elenco modificando la funzione `_default_sections()` nello script quando si aggiungono nuovi servizi, volumi, automazioni o file di configurazione critici. - **Snapshot configurazioni:** nella cartella `config-backups/` è disponibile lo script `snapshot_configs.py` che crea archivi compressi delle configurazioni più importanti basandosi su `targets.json`. Uso consigliato: ```bash cd /mnt/ssd/Jarvis python config-backups/snapshot_configs.py # esecuzione reale python config-backups/snapshot_configs.py --dry-run ``` Gli snapshot (tar.gz + metadata JSON) vengono salvati in `config-backups/snapshots/` con retention di default pari a 30 file. - **Automazione snapshot:** cron utente `mauro` esegue ogni giorno alle 02:30 `config-backups/snapshot_configs.py`, scrivendo log in `config-backups/snapshot.log`. - **Verifica rapida:** - `python config-backups/check_last_snapshot.py` mostra l'ultimo archivio creato e gli eventuali percorsi saltati. - `python config-backups/verify_snapshot.py --strict` termina con errore se trova “permission denied”, utile per integrazioni future. - **Mirroring pCloud:** cron utente `mauro` esegue alle **01:15** `/mnt/ssd/Jarvis/scripts/pcloud_backup.sh`, creando un mirror completo su `pcloud_backup:Flotta-Mirror/ssd` (log in `scripts/logs/pcloud_mirror.log`). Lo script segue i symlink (`--copy-links`) ed esclude cestino, log della console (`Jarvis/scripts/logs/`, `Jarvis/logbook/pcloud_mirror.log`) e file lock SQLite (`Jarvis/webapp/data/*.lock`). - **Logbook giornaliero:** `python /mnt/ssd/Jarvis/logbook/collect_logs.py` (cron utente `mauro` alle **05:45**) raccoglie gli esiti dei log principali e produce report in `logbook/reports/` (`latest.txt` + `report_YYYYMMDD.txt`). - **Idea tracker:** file `todo.md` nella root dell'hub per annotare backlog e note operative emerse durante IDE/webapp. - **Permessi aggiuntivi:** impostate ACL di sola lettura per `mauro` su `/mnt/ssd/config/portainer`, `/mnt/ssd/config/nextcloud/app`, `/mnt/ssd/config/nextcloud/database`, `/mnt/ssd/config/paperless/postgres` (incluso `pg_logical`) e `/mnt/ssd/config/paperless/redis`, oltre che su `/etc/wireguard` e `/etc/wireguard/wg0.conf`. Dopo reinstallazioni ripetere i comandi `setfacl`. I file mancanti `/etc/docker/daemon.json` e `/etc/systemd/system/docker.service.d/override.conf` sono intenzionalmente non presenti. - **Snapshot Avamposto (VPS):** ```bash cd /mnt/ssd/Jarvis python config-backups/backup_avamposto.py # scarica archivi via SSH+sudo python config-backups/verify_avamposto_snapshot.py # riepilogo ultimo archivio ``` - Configurazione in `config-backups/avamposto_targets.json`, password `sudo` salvata (permessi 600) in `config-backups/avamposto_sudo_password.txt`. - Archivi in `config-backups/avamposto_snapshots/`, retention 14. - Cron utente `mauro` alle **02:45** aggiorna automaticamente gli snapshot scrivendo log in `config-backups/avamposto_snapshot.log`. ### 3.4. Sistema di Backup Versionato con Restic - **Tecnologia:** `restic` - **Sorgente:** L'intero Hub Dati (`/mnt/ssd`) - **Destinazione:** Repository in `/mnt/backup-hdd/flotta-backup` - **Script:** `/mnt/ssd/Jarvis/scripts/backup_completo.sh` - **Esclusioni:** File `/mnt/ssd/Jarvis/scripts/backup_completo_exclusions.txt` - **Automazione:** Schedulato via **cron** alle **3:00 AM** ogni giorno (eseguito come utente `mauro`) ```cron 0 3 * * * /mnt/ssd/Jarvis/scripts/backup_completo.sh ``` - **Strategia di Conservazione:** 7 giornalieri, 4 settimanali, 12 mensili - **Credenziali:** password salvata in `/mnt/ssd/Jarvis/scripts/secrets/restic_password` (permessi `600`); lo script la esporta come variabile temporanea. Dopo ogni run le variabili d'ambiente vengono ripulite. - **Verifica integrità:** lo script esegue `restic check` ogni domenica al termine del backup. - **Permessi repository:** assicurarsi che `/mnt/backup-hdd/flotta-backup` e gli snapshot generati restino di proprietà di `mauro:mauro`, altrimenti i comandi Restic/diagnostica falliscono con `permission denied`. --- ## 4. Procedure Operative Chiave ### 4.1. Modifica della Configurazione di un Servizio (Procedura Corretta) A causa di un problema di configurazione precedente, gli stack esistenti in Portainer non sono modificabili direttamente tramite l'editor web. La gestione della configurazione deve quindi avvenire tramite file `docker-compose.yml` sul filesystem. #### Per modificare un servizio esistente: 1. **Ricostruire il `docker-compose.yml`:** - Sulla Nave, usare `docker inspect NOME_CONTAINER` per recuperare tutte le informazioni (volumi, variabili d'ambiente, reti, porte). - Creare un file `docker-compose.yml` nella cartella di configurazione del servizio (es. `/mnt/ssd/config/nextcloud/docker-compose.yml`) che replichi la configurazione attuale. 2. **Rimuovere il Vecchio Stack da Portainer:** - Andare in Portainer, sezione **Stacks**. - Selezionare lo stack da aggiornare e rimuoverlo. - **⚠️ ATTENZIONE:** I dati persistenti su `/mnt/ssd` non verranno toccati, ma è una procedura delicata. 3. **Deploy del Nuovo Stack da Riga di Comando:** - Sulla Nave, posizionarsi nella cartella contenente il nuovo file `docker-compose.yml`. - Eseguire il comando: `docker-compose up -d`. Da questo momento in poi, per qualsiasi modifica futura a quel servizio: - Modificare il file `docker-compose.yml` salvato. - Eseguire di nuovo `docker-compose up -d` dalla stessa cartella per applicare le modifiche. --- ## 5. Ambiente di Sviluppo ### 5.1. Configurazione - **IDE:** Visual Studio Code - **Linguaggio Principale:** Python - **Gestione Dipendenze:** `venv` per progetto ### 5.2. Progetti Esistenti - **Flotta Diagnostics:** - `/mnt/ssd/Jarvis/flotta-diagnostics/diagnostics.py` - `/mnt/ssd/Jarvis/flotta-diagnostics/installer.py` --- ## 6. Note Tecniche Importanti ### 7.1. Gestione Nextcloud - **Problema noto:** Le modifiche ai file nelle cartelle esterne (es. via WebDAV/WinSCP) potrebbero non essere viste immediatamente da Nextcloud. - **Soluzione:** Eseguire periodicamente `docker exec nextcloud_app php occ files:scan --all` per sincronizzare il database con lo stato reale dei file. ### 6.2. Sicurezza - **SSH Avamposto:** Accesso `root` disabilitato, solo autenticazione tramite chiave per utente `mauro`. - **Fail2ban:** Attivo sull'Avamposto per protezione da attacchi brute-force. - **Esposizione Nave:** **ZERO esposizione diretta a Internet**, tutto il traffico passa attraverso il tunnel WireGuard. --- *Documento aggiornato e verificato al 17 Ottobre 2025*