Control Plane (MON)
flo-control è il demone di flotta che detiene tutte le chiavi SSH di flotta e risponde
all'API autenticata del control-plane. Gira su Mon, fuori banda rispetto alla flotta
tenant, in un bundle di deploy dedicato (cli/deploy/control/) separato
dalle immagini tenant. I tenant vincolati al lease dipendono da MON per il rinnovo dell'autorità di
scrittura, così un'interruzione di MON può isolarli (fencing) — per questo il demone è volutamente piccolo e
indurito.
Dove gira
- Bundle dedicato su Mon, montato da
~/.flo-control, creato daflo control bootstrap. Contiene solo profili VPS sicuri e una chiave control-plane per target — mai ilmaster.keydell'operatore, l'intero config store, i segreti tenant o le credenziali Cloudflare. - Riutilizza il Traefik esistente dell'host (
flo-seo-traefik) su una rete Docker condivisa; non pubblica alcuna porta host. Ogni byte arriva tramite Traefik. - Indurimento container: rootfs in sola lettura, tutte le capability Linux rimosse,
no-new-privileges, utente non-root corrispondente al proprietario del bundle, limiti CPU/memoria erestart: unless-stopped.
Due porte, un demone
| Porta | Host | Percorso |
|---|---|---|
| Browser (gate Google) | control.useflo.net | Traefik → oauth2-proxy → demone :8787 |
| CLI / macchina | api-control.useflo.net | Traefik → demone (token di sessione opaco validato in-process) |
- La porta browser inoltra l'ID token OIDC Google al demone; il demone
lo scambia in
POST /auth/exchangecon un token di sessione Flo opaco. I token Google sono accettati solo su quella rotta. stripSpoofableHeaderselimina gli header in ingressoX-Forwarded-*/X-Auth-*così un bypass non può falsificare l'identità; le rotte macchina (lease) hanno propri rate limit.- Entrambe le porte applicano l'allow-list operatori (
whitelist.txt): firma Google +aud+email_verified+ email in allow-list. Il sign-in limitato al workspace è una modalità alternativa (FLO_CONTROL_HOSTED_DOMAIN).
Sessioni e token
- Sessioni operatore: emesse da
flo control login(sign-in Google nel browser) e conservate in locale a0600. Scadono 7 giorni dopo il login, non slittano con l'attività, e il logout le revoca immediatamente. - Token in sola lettura:
flo control login --read-only --token-file <path>emette una sessione solo-GET per agent osservatori (bot chat, dashboard da parete). Il demone rifiuta ogni rotta non-GET con403 auth.read_onlyprima di leggere un body; l'unico POST consentito è/auth/logout. I token in sola lettura hanno TTL 30 giorni con rinnovo scorrevole: mentre sono usati, la scadenza avanza (una scrittura al massimo una volta per emivita), così un osservatore attivo non scade mai, mentre un token inutilizzato muore comunque. - Endpoint di lettura utili:
GET /fleet/data.json(snapshot dashboard, ~4 KB gzip),GET /api/history(sparkline 24 h),GET /vps/:name/stats,GET /audit.
Dashboard e chiosco /fleet
flo control dashboard start # dashboard locale full-write su 127.0.0.1
flo control dashboard start --port 4173 --poll-seconds 10
La dashboard locale fa da proxy al demone col token CLI detenuto lato server.
https://control.useflo.net/fleet è una vista chiosco in sola lettura servita dal demone
stesso dietro il gate Google: nessuna azione di scrittura nel DOM, ricaricamento automatico quando i dati
diventano stantii o la sessione scade, tema scuro a token, manifest PWA, cookie oauth2-proxy a 30 giorni
per tablet da parete. Mostra:
- sparkline memoria/disco per host da un ring buffer SQLite (
metrics.db, campionato alla costruzione dello snapshot con throttle a 60 s); - postura DR per tenant (primario/standby, stato lease, incidente attivo/ultimo)
da
dr.db; - freschezza backup con probe R2 in cache con TTL;
- deriva versioni e stati "non in esecuzione" corretti, con filtro persistente dei cloni di test.
Avvisi Telegram
flo control alerts mon # mostra canale + stato
cat token.txt | flo control alerts mon --provision --token-stdin --chat <id> -y
flo control alerts mon --test --token-stdin < token.txt
flo control alerts mon --disable -y
- Il demone valuta le soglie di produzione ogni minuto: host irraggiungibile, disco/memoria, certificati, container di produzione fermi, istanza non in esecuzione, web e backup — con chiavi issue stabili per dedup/cooldown.
- Un messaggio batch per ciclo (CRITICAL/WARNING), una riga di recovery quando un problema rientra, e un cooldown per issue (predefinito 6 h) così un problema attivo non è rinotificato prima della scadenza.
- Supportati chat multiple e bot multipli (
--chatCSV o--targets-stdincon array JSON); basta un target riuscito. Lo stato vive inmetrics.db, così i riavvii non lo perdono. I cloni di test non sono mai avvisati. - Il token bot è letto da stdin (mai da argv) e scritto nel
.envdi MON.
Lettore backup
flo control backup-reader mon # mostra lo stato corrente
flo control backup-reader mon --provision -y
Emette un token R2 in sola lettura limitato al bucket, lo memorizza e inietta FLO_S3_*
nel bundle MON, poi ricrea il demone. La dashboard interroga il bucket backup di ogni tenant
una volta ogni 30 minuti e mostra l'età reale del backup. Senza queste
credenziali la scheda Backup resta semplicemente vuota — mai un falso avviso. Ri-provisiona
dopo aver creato un nuovo bucket così il lettore può vederlo (la forma remota backup schedule
gira di per sé senza chiavi R2 di scrittura).
Audit (WORM su R2)
Ogni scrittura contro il control plane produce una riga di audit concatenata via hash e redatta; la catena è replicata fuori dal box su R2 sotto una regola Bucket Lock (l'equivalente object-lock di R2), così la cronologia non può essere riscritta.
flo control audit --provision -y # crea bucket + token dedicato + lock
flo control audit --apply-lock -y # aggiunge la regola lock a un target esistente
flo control audit # verifica la configurazione
Predefiniti: bucket flo-control-audit, prefisso audit, retention 3650 giorni
(--retention-days). Le credenziali S3 dedicate sono cifrate nel flo config locale
dell'operatore e sono le uniche credenziali audit inviate a Mon. Gli operatori possono
leggere i propri record con GET /audit.
Registry comandi remoti
flo --vps <name> … non ripiega mai su SSH: sono accettate solo le forme registrate,
ed eseguite dal demone.
- Lettura:
vps stats|test,vps journal status,instance ls|test list,instance info|inspect|health|stats <id>,logs app|errors|db|days <id>,logs day <id> <date>,deploy history <id>,config env show/flags show,config ssl status,instance db tables <id>,backup status [<id>],watchdog status,failover status,secrets status <id>. - Scrittura:
deploy <id> --branch <branch> -y,deploy rollback <id> -y,instance start|stop|restart <id>,config env set <id> KEY=VALUE [--restart] -y,backup schedule <id> --bucket … --cron … --run-now -y,vps journal vacuum -y(policy fissa 500M/30 giorni), più i comandi finiti del ciclo di vita DR. - Ogni account in allow-list riceve lo stesso pieno accesso in scrittura a queste azioni.
Il registry non espone shell arbitraria, SQL, trasferimento file o percorsi
locali; usa
--localper un'operazione locale intenzionale. I job DR lunghi hanno un timeout di due ore.
Comandi
| Comando | Descrizione | Opzioni principali |
|---|---|---|
flo control login | Sign-in Google da browser; memorizza il token di sessione | --api, --app, --read-only, --token-file <path> |
flo control logout / whoami | Revoca la sessione locale / mostra identità e sessioni live | |
flo control dashboard start | Dashboard locale full-write in proxy verso il demone | --port, --poll-seconds, --api, --no-open |
flo control snapshot | Snapshot read-model di flotta non-segreto (JSON) | --out <file>, --check-backups |
flo control build | Rende l'HTML statico della dashboard in sola lettura da uno snapshot | --out, --from <file>, --check-backups |
flo control server | Esegue il demone (usato dentro il container) | --port, --bind |
flo control bootstrap [intermediary] | Crea il bundle sicuro + chiavi SSH ristrette per target | --fleet <names> (obbligatorio), --source, --bundle-home, --dry-run, -y |
flo control deploy [vps] | Deploy a freddo di MON con recovery durevole | --recover <id>, --repair-worker, --local-build, --no-build, --force-env, --force-whitelist, --enable/--disable-dr-automation, --maintenance, --shared-network, domini, -y |
flo control doctor [vps] | Triage in sola lettura dell'host di controllo (disco, journal, transazioni, demone) | |
flo control audit | Configura/verifica il target audit WORM | --provision, --apply-lock, --retention-days, --replace, --bucket, --prefix, -y |
flo control alerts [vps] | Mostra/configura gli avvisi Telegram | --provision, --disable, --test, --chat, --token-stdin, --targets-stdin, --dir, -y |
flo control backup-reader [vps] | Mostra/provisiona le credenziali backup R2 in sola lettura | --provision, --dry-run, --dir, -y |
flo control agent-config <tenant> | Prepara un config agent privato; solo l'hash del token raggiunge MON | --node (obbligatorio), --output (obbligatorio), --init, --rotate-token, -y |
flo control command status <job> / attach <job> | Ispeziona o riaggancia un job di comando remoto ancora in esecuzione su MON |
Rollout e recovery
flo vps test mon
flo control audit --provision -y
flo control bootstrap mon --fleet production,production-uk -y
flo control deploy mon --enable-dr-automation -y
- Gate di deploy: ogni deploy richiede tutti i tenant MON completamente manuali, nessun incidente
attivo e nessun lease firmato pendente; il gate rifiuta stato ignoto o non sicuro prima di fermare il demone.
--maintenanceammette una coppia armata live con stato DR inattivo;--disable-dr-automationnon sostituisce un disarmo completo. --local-buildcompila sulla postazione per la piattaforma target e trasferisce l'immagine verificata;--no-buildrichiede la stessa versione engine.- Deploy interrotto: conserva l'ID operazione stampato ed esegui
flo control deploy mon --recover <operation-id> -y. La recovery verifica il nuovo container o ripristina lo snapshot posseduto; non usare maidocker compose downné eliminare i file operazione. - Cambio IP di MON:
flo control bootstrap mon --fleet … --source <new-ip> -ypoiflo control deploy mon --no-build -y(vedi README del bundle incli/deploy/control/README.md). - Prima di ogni mutazione VPS remota esegui il preflight in sola lettura:
flo --vps <name> vps stats.
Vedi Failover e Disaster Recovery per il ciclo di vita DR coordinato da MON, e Backup per i dati di freschezza letti dalla dashboard.