# Bucket & Code Server IDE: guida utente

Bucket & Code Server IDE (QPKG `bucketvs`) conserva codice sorgente e file su un NAS QNAP e vi compila software. Porta
con sé un proprio server web, propri strumenti e propri account.

Questa guida descrive il pacchetto così come funziona sul NAS.

## Indice

- [Che cosa gira sul NAS](#che-cosa-gira-sul-nas)
- [Installare il pacchetto](#installare-il-pacchetto)
- [Primo accesso](#primo-accesso)
- [Lavorare nel pannello](#lavorare-nel-pannello)
- [Account](#account)
- [La cartella dei dati](#la-cartella-dei-dati)
- [Rete](#rete)
- [Dietro un reverse proxy (Plesk)](#dietro-un-reverse-proxy-plesk)
- [Nomi host dei moduli](#nomi-host-dei-moduli)
- [Pubblicare su internet (Cloudflare Tunnel)](#pubblicare-su-internet-cloudflare-tunnel)
- [Moduli](#moduli)
- [Git](#git)
- [Runner CI](#runner-ci)
- [SVN e Bucket](#svn-e-bucket)
- [Editor e strumenti IA](#editor-e-strumenti-ia)
- [Toolchain e ambienti](#toolchain-e-ambienti)
- [App Builder](#app-builder)
- [Repository dei pacchetti](#repository-dei-pacchetti)
- [Officina del kernel](#officina-del-kernel)
- [Job cron e operazioni](#job-cron-e-operazioni)
- [Backup](#backup)
- [Notifiche](#notifiche)
- [Log e pacchetto diagnostico](#log-e-pacchetto-diagnostico)
- [Aggiornamenti](#aggiornamenti)
- [Il comando bvs](#il-comando-bvs)
- [File e cartelle](#file-e-cartelle)

## Che cosa gira sul NAS

| Parte | Che cosa fa | Dove ascolta |
|---|---|---|
| nginx | l’unica porta sulla LAN: serve il pannello e passa ogni percorso al suo modulo | 9950 (HTTP), 9951 (HTTPS) |
| bvsd | avvia e sorveglia tutti gli altri processi, tiene account e impostazioni, esegue job e job cron, risponde al pannello | due socket Unix in `/usr/bin/bucketvs/var/run/` |
| Git (Forgejo) | hosting Git con un’interfaccia web | un socket Unix in `/usr/bin/bucketvs/var/run/forgejo/`, e 9952 per Git via SSH |
| SVN e Bucket (httpd) | repository Subversion e cartelle WebDAV con cronologia | 127.0.0.1:9961, o la porta libera successiva quando un altro programma la occupa |
| Editor (code-server) | VS Code nel browser, un’istanza per account | un socket Unix per account in `/usr/bin/bucketvs/var/run/ide/` |
| CI | il runner di Forgejo Actions | nessuna porta propria; nginx ascolta per lui su 127.0.0.1:9960 finché il modulo è attivo, o sulla porta libera successiva quando un altro programma occupa 9960 |

Il nucleo (nginx, bvsd, il pannello, i job cron, le toolchain) è sempre attivo. Git, CI, SVN e Bucket e l’Editor sono
moduli che un admin attiva e disattiva. Su ogni indirizzo del NAS i moduli rispondono sotto i loro percorsi: `/git/`,
`/svn/`, `/bucket/`, `/ide/`. Il pannello è su `/`, e questa guida su `/guide/`.

Mentre il pacchetto è in esecuzione, i suoi programmi sono in `/usr/bin/bucketvs/bin`. Tutto ciò che il pacchetto
conserva (account, impostazioni, repository, ambienti, log) sta in una sola cartella su un volume dati: la
[cartella dei dati](#la-cartella-dei-dati).

## Installare il pacchetto

Il pacchetto richiede QTS 6.0 o QuTS hero 6.0 o successivi, su un NAS x86_64 o ARM a 64 bit; c’è un pacchetto per
architettura (`bucketvs_<versione>_x86_64.qpkg`, `bucketvs_<versione>_arm_64.qpkg`). È firmato: installalo da App
Center (da un repository che lo elenca, o con **Installazione manuale** e il file scaricato). App Center verifica la
firma; non serve consentire le app senza una firma valida.

L’installazione crea l’utente di servizio `bucketvs` (per Forgejo e httpd), il collegamento `/usr/bin/bucketvs` alla
cartella di installazione, e avvia bvsd. I log di sistema indicano la cartella dei dati.

### Dove nasce la cartella dei dati

Al primo avvio la cartella dei dati è `BucketVS` sul volume su cui è installato il pacchetto, per esempio
`/share/CACHEDEV1_DATA/BucketVS` (QTS) o `/share/ZFS2_DATA/BucketVS` (QuTS hero). In seguito può essere spostata su un
altro volume dal pannello ([Spostare la cartella dei dati](#spostare-la-cartella-dei-dati)).

Il pacchetto ricorda la sua cartella dei dati in `/etc/config/bucketvs-data-root`, un file che sopravvive al pacchetto.
Rimuovere il pacchetto lascia la cartella dei dati e questo file, quindi un’installazione dopo una rimozione riparte sugli
stessi dati e sugli stessi account. Un avvio non dipende mai da una cartella che non c’è. Quando la cartella dei dati non
c’è più (eliminata, su un disco sostituito, o su un volume offline in quel momento), l’avvio crea `BucketVS` sul volume
del pacchetto, una cartella nuova a meno che ce ne sia già una con quel nome, e in una cartella nuova il [primo
accesso](#primo-accesso) imposta la password dell’amministratore. Vale dopo una reinstallazione come per un pacchetto
installato, anche all’accensione del NAS. I log di sistema riportano un avviso, e i file `.prev` conservano il vecchio
percorso: `/etc/config/bucketvs-data-root.prev` e, per un pacchetto installato, anche `etc/data-root.prev` nella
cartella di installazione. Se la vecchia cartella era su un volume che in quel momento era solo offline, rimetti online
il volume, ferma il pacchetto, copia quel file sopra `etc/data-root` nella cartella di installazione (per esempio
`/share/CACHEDEV1_DATA/.qpkg/bucketvs/etc/data-root`) e avvia il pacchetto: riparte sui vecchi dati.

### Aprire il pannello da App Center

**Apri** sul pacchetto in App Center apre il pannello sulla porta 9950 (HTTP) o 9951 (HTTPS). Quando le porte vengono
cambiate nel pannello ([Porte](#porte)), il pacchetto comunica le nuove ad App Center al suo avvio successivo.

### Avvio automatico da una sessione SSH

Finché l’interruttore del pacchetto in App Center è attivo, il NAS avvia il pacchetto all’accensione. In una sessione
SSH sul NAS, come admin, lo script di servizio imposta lo stesso interruttore:

```sh
/etc/init.d/bucketvs.sh disable   # il prossimo avvio viene rifiutato, anche all’accensione del NAS
/etc/init.d/bucketvs.sh enable    # il pacchetto torna ad avviarsi all’accensione
```

Ciascuno dei due comandi scrive solo l’interruttore (`Enable` in `/etc/config/qpkg.conf`), stampa che cosa ha
impostato e scrive la stessa riga nei log di sistema. Dopo `disable` un pacchetto in esecuzione continua a girare, ed
`enable` non avvia nulla: lo fanno `stop` e `start`, e `start` rifiuta finché il pacchetto è disattivato. `status`
dice se bvsd è in esecuzione.

## Primo accesso

Apri il pannello su `https://<indirizzo del NAS>:9951/` o `http://<indirizzo del NAS>:9950/`. L’indirizzo HTTPS usa
all’inizio un certificato autofirmato, quindi il browser mostra un avviso finché il dispositivo non se ne fida; la
sezione [Il certificato](#il-certificato) mostra come confrontarne l’impronta.

Una nuova installazione non ha alcun account. Finché il primo amministratore non ha una password, il pannello si apre
su un’unica schermata, “Imposta la password dell’amministratore”, per l’account `administrator`. Digita una password
due volte: almeno 10 caratteri e al massimo 72 byte. Salvarla crea l’account e ti fa accedere.

```mermaid
flowchart TD
    start["Il pacchetto si avvia<br/>senza alcun account"] --> screen["Imposta la password dell’amministratore<br/>60 minuti, solo rete locale"]
    screen -->|"salvata"| panel["administrator connesso"]
    screen -->|"passati 60 minuti"| restart["Riavvia il pacchetto in App Center:<br/>una nuova finestra di 60 minuti"]
    restart --> screen
```

- La schermata accetta una password per 60 minuti dopo l’avvio del pacchetto. Poi dice che la finestra di
  configurazione si è chiusa; riavviare Bucket & Code Server IDE in App Center apre una nuova finestra di 60 minuti.
- Accetta una password solo dalla rete locale: dal NAS stesso, o da un indirizzo di un intervallo privato
  (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `fc00::/7`, `fe80::/10`).
- Mentre la schermata è aperta, i log di sistema di QTS mostrano una voce informativa per ogni indirizzo LAN del NAS,
  per esempio:

  ```text
  bucketvs: no administrator yet: open https://192.168.1.20:9951/ within 60 minutes to set the administrator's password
  ```

- Rifiuta una richiesta arrivata tramite un proxy (una che porta `X-Forwarded-For`, `X-Forwarded-Host`,
  `X-Forwarded-Proto`, `X-Real-IP` o `Forwarded`), anche da un proxy sulla LAN: apri il pannello direttamente da un
  computer della rete locale per impostare la password.

Da una sessione SSH sul NAS, `/usr/bin/bucketvs/bin/bvs user password administrator` crea l’account quando non ce n’è
ancora nessuno e stampa una password casuale; il pannello chiede poi una tua password al primo accesso ([Quando un
admin è bloccato fuori](#quando-un-admin-è-bloccato-fuori)).

Dopo il primo accesso la Panoramica dice che è attivo solo il nucleo. Il riquadro di ogni modulo dice che cosa offre e
ha un pulsante che lo attiva, per esempio **Attiva Git**: attiva quelli che ti servono ([Moduli](#moduli)).

## Lavorare nel pannello

Il pannello funziona come un editor di codice: ogni pagina si apre in una scheda, e una tavolozza dei comandi
raggiunge le pagine e le azioni per nome.

- **Barra delle attività** (le icone a sinistra). Ogni icona apre la sua pagina e mostra il suo albero nella barra
  laterale: i moduli, i repository e i bucket, le app, i pacchetti, gli ambienti, i job e i backup. Un punto su
  un’icona segnala un modulo in difficoltà, una build non riuscita o un backup non riuscito. Un clic sull’icona
  della pagina in primo piano nasconde la barra laterale; Ctrl+B fa lo stesso.
- **Schede.** Una pagina aperta con un solo clic è un’anteprima, in corsivo: la pagina successiva si apre al suo
  posto. Modifica la pagina o fai doppio clic sulla sua scheda per tenerla. Le schede tornano dopo un ricaricamento,
  per ogni account in ogni browser. In una finestra larga almeno 1280 pixel, **Sposta la scheda sull’altro lato**
  (`Ctrl+\`) mostra due pagine affiancate.
- **Gli oggetti e i loro menu.** Un clic su una riga della barra laterale, una riga di tabella o una scheda apre
  quell’oggetto: la sua pagina passa in primo piano, con l’oggetto cerchiato di verde o con i dettagli aperti.
  L’indirizzo nomina l’oggetto, quindi indietro, avanti e un link copiato ci riportano. Un doppio clic tiene la
  scheda. Un clic destro, Maiusc+F10 o una pressione prolungata su uno schermo tattile apre il menu dell’oggetto: i
  modi di aprirlo (in questa scheda, in una scheda tenuta, sull’altro lato, in una nuova scheda del browser), ciò che
  la sua pagina può farne, e la copia del nome e dell’indirizzo. Il menu di una scheda la chiude, chiude le altre
  schede, quelle a destra o tutte. Solo un elemento segnato ↗ lascia il pannello. L’icona **Amministrazione** apre la
  pagina Sistema.
- **Tavolozza dei comandi.** Ctrl+Maiusc+P (o Ctrl+K, o F1) elenca i comandi che il tuo ruolo può eseguire: aprire una
  pagina, compilare o pubblicare un’app, avviare un backup, riavviare un modulo, aprire una shell in un ambiente.
  Ctrl+P va a una pagina, un’app, un repository, un ambiente o un pacchetto con poche lettere del suo nome.
- **Pannello inferiore** (Ctrl+J). **Job** mostra i job in corso e recenti con il loro avanzamento e il loro log.
  **Problemi** elenca ciò che segnalano i moduli: build fallite, destinazioni di backup disattivate,
  moduli in difficoltà, un certificato in scadenza, e i rilievi dell’ispettore ELF per l’app in primo piano.
  **Output** segue il job che hai avviato per ultimo.
- **Spostare e ridimensionare.** Trascina un bordo per cambiare la dimensione di ciò che delimita: la barra laterale,
  il pannello inferiore, due pagine affiancate, o due parti di una pagina affiancate, come una procedura guidata e il
  suo riepilogo o l’elenco dei file di un progetto e il file. Un doppio clic su un bordo lo rimette a posto. Una
  finestra di dialogo si sposta dal titolo e si ridimensiona da un bordo o dall’angolo in basso a destra; un doppio
  clic sul titolo la rimette al centro. Per mettere il pannello inferiore accanto alle pagine, trascina la sua
  intestazione sul bordo destro dell’editor o fai clic sul pulsante accanto alla sua ×; trascinalo sul bordo
  inferiore, o fai di nuovo clic sul pulsante, per rimetterlo in basso. Una scheda si può trascinare in un altro punto
  della barra delle schede o sull’altro lato. Il pannello tiene ogni dimensione e posizione per il tuo account in
  questo browser, e **Ripristina il layout** (nella tavolozza dei comandi, o sotto il pulsante del tuo account in
  fondo alla barra delle attività) le rimette tutte. Da tastiera, le frecce spostano un bordo selezionato (con Maiusc
  a passi più lunghi, Home lo rimette a posto), Ctrl+Maiusc+Sinistra e Destra spostano una scheda selezionata, e una
  finestra di dialogo ha in alto una barra e in basso a destra una maniglia con cui le frecce la spostano e la
  ridimensionano. Su un telefono le finestre di dialogo e il pannello inferiore restano dove sono.
- **Barra di stato.** Stati dei moduli, job in corso, problemi, spazio libero sul volume dati, il certificato,
  l’account connesso, la lingua (l’editor la adotta al prossimo avvio, in inglese per svedese e olandese; Git la segue, a meno che tu non ne scelga un’altra lì), il tema (scuro, chiaro, come il sistema, blu profondo, grafite o carta: appartiene al tuo account, e l’editor e Git lo seguono, a meno che tu non ne scelga un altro lì) e la versione. Ogni elemento apre il punto
  in cui guardare meglio.
- **Tastiera.** Ctrl+/ o ? elenca le scorciatoie. Una scheda del browser tiene Ctrl+W e Ctrl+Tab per sé, quindi il
  pannello chiude una scheda con Alt+W e cambia scheda con Alt+PagGiù e Alt+PagSu.
- **Su un telefono** le prime attività sono in basso, **Altro** apre la barra laterale con il resto, e le pagine
  aperte sono un elenco in alto.

## Account

Il pannello ha i propri account. Gli account di QTS non vi accedono.

### Ruoli

| Ruolo | Che cosa può fare |
|---|---|
| viewer | legge il pannello; le pagine Backup, Archiviazione, Utenti e Log sono per gli admin. In Forgejo non crea, non fa fork e non importa alcun repository, non crea alcuna organizzazione e non fa mai push; legge i repository SVN e i bucket su cui ha diritti e non scrive in nessuno |
| member | legge ciò che legge un viewer, e scrive: fa push nei repository Git in cui riceve l’accesso in scrittura, scrive nei repository SVN e nei bucket su cui ha lettura e scrittura, e vi ripristina versioni precedenti dei file. In Forgejo non crea, non fa fork e non importa alcun repository e non crea alcuna organizzazione, e non può fare push in un repository che un account con ruolo inferiore a developer possiede o amministra, né, finché il modulo CI è attivo, in un repository con le Actions attive. Non ha editor, job cron né build |
| developer | inoltre apre l’editor, legge le righe `bvs use` delle toolchain installate, crea, modifica ed esegue i propri job cron, crea e compila app, pubblica pacchetti nel repository (un admin li elenca), verifica moduli del kernel, e crea repository Git i cui workflow girano sul NAS finché il modulo CI è attivo |
| admin | tutto: account, impostazioni, archiviazione, rete, moduli, toolchain, ambienti, ogni job cron, attivazione delle app, caricamento dei moduli del kernel, elenco dei pacchetti nel repository |

Il ruolo developer è root sul NAS: i suoi comandi cron girano come root, così come il terminale del suo editor e, finché
il modulo CI è attivo, i workflow dei repository in cui può fare push. Dallo solo a chi daresti la password admin del
NAS. A chi scrive soltanto nei repository basta il ruolo member.

In **Il tuo account** ognuno vede il proprio ruolo e ciò che consente, con un link a questa sezione. Una pagina sopra il
ruolo di qualcuno dice quale ruolo richiede, e ciò che cambia solo un admin è indicato come tale dove compare. Rete, Git
e CI mostrano agli altri le loro impostazioni come valori, con «Non impostato» dove un valore manca.

I workflow girano come root sul NAS, quindi bvsd tiene le Actions disattivate in ogni repository Git che un member o
un viewer possiede o in cui un tale account può scrivere: dare a un tale account l’accesso in scrittura a un
repository ne disattiva le Actions entro un minuto, e la pagina Git elenca questi repository per gli admin. Per usare
le Actions in uno di essi, togli prima quell’accesso in scrittura in Forgejo, oppure trasferisci un repository che un
tale account possiede all’account di un developer.

### Creare e modificare account

Un admin gestisce gli account nella pagina Utenti.

- Un nuovo account parte con il ruolo viewer. Developer e admin si scelgono di proposito: il ruolo developer è root
  sul NAS (sopra).
- Un nuovo account parte senza job cron, anche quando un account con lo stesso nome ne aveva.
- Un nome ha da 2 a 32 caratteri: prima una lettera minuscola, poi lettere minuscole, cifre, `.`, `_` o `-`. Due tra
  `.`, `_`, `-` di seguito, o uno alla fine, sono rifiutati. Lo sono anche i nomi che Forgejo riserva (`admin`, `api`,
  `user` e altri), `bucketvs` (l’utente di sistema del pacchetto), `cli` e `system` (bvsd li usa entrambi).
- “Deve scegliere la propria password al primo accesso” fa sì che l’account imposti la propria password prima di poter
  fare qualunque altra cosa.
- Disattivare un account chiude subito le sue sessioni e ferma i suoi token API. I suoi dati restano. Anche i suoi
  [job cron](#job-pianificati) restano, ma non girano finché è disattivato, e un’esecuzione in corso di uno di essi
  viene fermata.
- Dare a un account il ruolo member o viewer fa lo stesso con i suoi job cron: restano, non girano finché il suo
  ruolo è inferiore a developer, e un’esecuzione in corso viene fermata.
- Un viewer non fa mai push in Git, nemmeno dove ha ricevuto l’accesso in scrittura in Forgejo. Un account che deve
  fare push riceve il ruolo member.
- Eliminare un account lo rimuove con le sue sessioni e i suoi token API. La sua cartella home va in
  `trash/home/<nome>-<ora>` nella cartella dei dati, dove il job “Svuota il cestino” la rimuove dopo 7 giorni, e la sua
  cartella `tmp/tmux-<nome>` viene rimossa. I suoi job cron vengono eliminati con le loro esecuzioni e i loro log.
- L’ultimo admin attivo non può essere declassato, disattivato o eliminato.
- “Imposta una nuova password” chiude le sessioni dell’account e revoca i suoi token API.

### Password e sessioni

- Una password ha almeno 10 caratteri e al massimo 72 byte.
- Cambiare la propria password (in Il tuo account) chiude le altre tue sessioni e revoca i tuoi token API. La sessione
  in cui l’hai cambiata resta connessa.
- Una sessione termina 12 ore dopo la sua ultima richiesta, e in ogni caso 7 giorni dopo l’accesso. Un account tiene
  al massimo 32 sessioni; un accesso in più chiude quella usata meno di recente.
- Il cookie di sessione è `HttpOnly` e `SameSite=Lax`. È marcato `Secure` quando il browser ha raggiunto il NAS via
  HTTPS, direttamente o tramite un [proxy attendibile](#proxy-attendibili).

### Password errate

- Cinque accessi falliti entro 15 minuti da un indirizzo bloccano quell’indirizzo per 15 minuti, anche con la password
  giusta. Un client IPv6 conta come la sua rete /64.
- Cinque errori per un account da un indirizzo bloccano quell’account da quell’indirizzo per 15 minuti.
- Gli errori per un account da molti indirizzi rallentano i suoi accessi successivi: 125 ms dopo un errore,
  raddoppiando fino a 2 secondi. Dalla rete locale non lo bloccano mai.
- Da internet (qualsiasi indirizzo fuori dalla rete locale), 20 accessi falliti per un account dall’ultimo riuscito lo
  bloccano lì, da qualunque indirizzo siano arrivati; allora lì viene rifiutata anche la password giusta. L’account
  accede ancora dalla rete locale. Un admin lo sblocca nella pagina Utenti (**Sblocca**) o con
  `bvs user unlock <nome>`, oppure gli assegna una nuova password. Il numero si imposta nella pagina Utenti, sotto
  **Accessi da internet**; 0 disattiva questo blocco.
- I conteggi sono tenuti in memoria, quindi un riavvio di bvsd li azzera; un blocco resta finché non viene tolto.

Dietro un proxy, l’indirizzo contato è quello che il proxy riporta, ma solo quando il proxy è un [proxy
attendibile](#proxy-attendibili). Altrimenti ogni client conta come l’indirizzo stesso del proxy, e cinque password
sbagliate di chiunque bloccano tutti per 15 minuti.

### Autenticazione a due fattori

Ogni account lo attiva per sé in Il tuo account: scansiona il codice QR con un’app di autenticazione (o digita la
chiave), poi inserisci il codice a 6 cifre che l’app mostra. Da quel momento l’accesso chiede un codice dopo la
password.

- I codici seguono la RFC 6238: 6 cifre, un nuovo codice ogni 30 secondi. Un codice in anticipo o in ritardo di 30
  secondi è accettato. Lo stesso codice non è mai accettato due volte.
- Se un codice appena generato fallisce, l’orologio del telefono è probabilmente sbagliato; impostalo sull’ora
  automatica.
- Disattivarlo richiede la password dell’account.
- Per un telefono perso, un admin lo disattiva nella pagina Utenti. A quel punto basta la password per accedere.

### Token API

Script e CI usano token API invece di una password. In Il tuo account, “Nuovo token” ne crea uno:

- Ha la forma `bvs_` seguito da 40 cifre esadecimali, ed è mostrato una sola volta. bvsd ne conserva solo l’hash.
- Uno script lo invia come `Authorization: Bearer bvs_...`.
- I suoi ambiti sono `read`, `write` e `admin`, che corrispondono ai ruoli viewer, developer e admin. Un token agisce
    con il suo ambito più alto, mai al di sopra del ruolo attuale del suo proprietario, quindi declassare un account
  declassa anche i suoi token. Nessun ambito corrisponde a member: un member crea token `read`.
- Una nuova password revoca tutti i token del proprietario. I token di un proprietario disattivato smettono di
  funzionare.
- Un token non può cambiare le password né l’autenticazione a due fattori, e non può creare o eliminare token: per questo
  serve una sessione connessa. Un token trapelato quindi non può chiudere fuori il suo proprietario.

### Quando un admin è bloccato fuori

L’admin del NAS può reimpostare qualsiasi account da una sessione SSH sul NAS. Il comando `bvs` parla con bvsd su un
socket che solo root può aprire (`/usr/bin/bucketvs/var/run/bvsd-cli.sock`, modalità 600):

```bash
/usr/bin/bucketvs/bin/bvs user list
/usr/bin/bucketvs/bin/bvs user password administrator
/usr/bin/bucketvs/bin/bvs user totp-reset administrator
/usr/bin/bucketvs/bin/bvs user unlock administrator
```

- `bvs user password <nome>` imposta una nuova password casuale di 20 caratteri e la stampa. L’account deve sceglierne
  una propria al prossimo accesso. Le sue sessioni terminano e i suoi token API vengono revocati. Su una nuova
  installazione senza ancora alcun account, `bvs user password administrator` crea l’account.
- `bvs user totp-reset <nome>` disattiva l’autenticazione a due fattori per l’account.
- `bvs user unlock <nome>` toglie il blocco dell’account dopo troppe password errate da internet.

Ogni richiesta su quel socket agisce come l’admin integrato `cli` ed è scritta in `log/audit.log` nella cartella dei
dati. Per questi comandi bvsd deve essere in esecuzione.

## La cartella dei dati

La cartella dei dati è la cartella che contiene tutto ciò che il pacchetto conserva. Il suo percorso è l’unica riga di
`/usr/bin/bucketvs/etc/data-root`. La pagina Archiviazione la mostra, con il volume su cui si trova e lo spazio libero
lì.

bvsd si avvia solo su una cartella dei dati che è una cartella su un volume vero. L’avvio del pacchetto lo controlla
prima: una cartella mancante, o una su tmpfs o ramfs, dà una nuova cartella dei dati sul volume di installazione, come
descritto in [Dove nasce la cartella dei dati](#dove-nasce-la-cartella-dei-dati). Un volume offline lascia il suo punto di
montaggio come cartella vuota nella RAM del NAS, e le impostazioni scritte lì sparirebbero al riavvio successivo. bvsd
stesso rifiuta una cartella così solo quando viene avviato a mano, e il motivo va allora nei log di sistema di QTS.

### Che cosa bvsd dà a ogni processo

QTS tiene `/tmp` in un piccolo disco RAM e `/root` su un disco RAM che un riavvio svuota. Per questo ogni processo che
bvsd avvia (programmi dei moduli, job cron, installazioni) riceve i suoi file temporanei e la sua cartella home nella
cartella dei dati:

| Variabile | Valore |
|---|---|
| `HOME` | `<cartella dei dati>/home/<utente>` |
| `TMPDIR` | `<cartella dei dati>/tmp` |
| `XDG_CACHE_HOME`, `XDG_CONFIG_HOME` | `.cache` e `.config` in quella home |
| `npm_config_cache` | `.npm` in quella home |
| `PATH` | `~/.local/bin`, `~/.npm-global/bin`, poi `bin`, `sbin` e `node/bin` del pacchetto, poi le cartelle del sistema |
| `SSL_CERT_FILE`, `GIT_SSL_CAINFO`, `CURL_CA_BUNDLE` | il bundle CA del pacchetto, `/usr/bin/bucketvs/etc/ssl/cacert.pem` |
| `TZ` | il fuso orario del NAS, come impostato in QTS |
| `SHELL`, `EDITOR` | `bash` e `nano` del pacchetto |
| `LANG`, `LESSCHARSET` | `C.UTF-8` e `utf-8`, così i nomi di file con lettere accentate appaiono come sono |

### Spostare la cartella dei dati

La pagina Archiviazione sposta l’intera cartella dei dati in un’altra cartella sotto `/share`. Scegli una cartella
condivisa o un volume; i dati vanno in una nuova cartella chiamata `BucketVS` al suo interno. Per usare una cartella
stessa, apri una cartella vuota chiamata `BucketVS`. Le cartelle condivise si creano in QTS (Pannello di controllo,
Cartelle condivise); dentro una cartella condivisa o un volume, la pagina può creare una nuova cartella. Il selettore
si apre dove si trovano ora i dati e lo dice; da lì apri la cartella condivisa o il volume in cui devono essere
spostati.

La pagina verifica la destinazione prima che lo spostamento inizi ed elenca ogni motivo per cui ne rifiuta una:

- il percorso deve essere sotto `/share`, e non `/share` stesso;
- nessun `:`, `&`, `|` o carattere di controllo in alcuna parte del percorso, né nel percorso a cui porta un
  collegamento;
- non la cartella dei dati attuale, non una cartella al suo interno, non una cartella che la contiene;
- una cartella nuova, o una vuota;
- un file system che conserva proprietari e permessi Unix (FAT, exFAT e NTFS sono rifiutati: Forgejo e SVN hanno
  bisogno di proprietari e modalità);
- un volume montato con `exec`, `dev` e `suid`, di cui gli ambienti di build hanno bisogno;
- scrivibile, con spazio libero per i dati più il 10 %.

```mermaid
flowchart TD
    check["Verifica di nuovo la destinazione<br/>spazio libero: i dati + 10 %"] --> stop["Ferma i moduli<br/>il pannello resta attivo"]
    stop --> copy["Copia con<br/>rsync -aHAX --numeric-ids"]
    copy --> verify["Verifica la copia<br/>file, byte, proprietari, modalità, date,<br/>sha256 di fino a 1000 file"]
    verify --> switch["Cambia la cartella dei dati<br/>in /usr/bin/bucketvs/etc/data-root"]
    switch --> start["bvsd riparte sulla nuova radice<br/>e avvia i moduli"]
    start --> old["La vecchia copia resta, marcata MOVED-TO,<br/>finché non la rimuovi"]
    copy -->|errore| failed["La vecchia radice resta in uso,<br/>i moduli ripartono"]
    verify -->|una differenza| failed
```

Mentre lo spostamento è in corso:

- Git, CI, SVN e Bucket e l’Editor non rispondono. Il pannello resta attivo e mostra l’avanzamento.
- Nessuna nuova esecuzione cron parte. Un’esecuzione già in corso continua.
- Attivare o disattivare un modulo è rifiutato finché lo spostamento non termina.

Dopo il cambio, bvsd si ferma e riparte sulla nuova radice, quindi il pannello sparisce per qualche secondo.

Quando lo spostamento fallisce, la vecchia cartella dei dati resta in uso e i moduli ripartono. L’errore del job dice
che cosa è andato storto e che fine ha fatto la copia:

- Un volume pieno, o un annullamento, rimuove ciò che lo spostamento ha creato.
- Qualsiasi altro errore (la copia, la verifica, la scrittura su disco) conserva la copia, marcata con un file
  chiamato `INCOMPLETE`, e la elenca nella pagina Archiviazione sotto “Copie rimaste da spostamenti non riusciti”.
- Un arresto del pacchetto durante lo spostamento tiene la vecchia radice come cartella dei dati. All’avvio successivo
  bvsd segnala lo spostamento incompiuto nei log di sistema ed elenca la copia.

### La vecchia copia e le copie rimaste

Dopo uno spostamento riuscito, la vecchia cartella dei dati resta dov’era, con un file chiamato `MOVED-TO` al suo
interno. Niente la usa. “Rimuovi la vecchia copia” nella pagina Archiviazione la elimina, e “Rimuovi questa copia”
elimina una copia rimasta da uno spostamento non riuscito. Entrambe le rimozioni restano su un solo file system e non
seguono mai un collegamento. Una cartella che è un punto di montaggio o la cartella stessa di una cartella condivisa
viene svuotata e conservata.

## Rete

La pagina Rete contiene le porte, la modalità HTTP semplice, i nomi host dei moduli, i proxy attendibili, l’URL
pubblico e il certificato. Il salvataggio riavvia ciò di cui la modifica ha bisogno e lo elenca.

### Porte

| Impostazione | Predefinito | Che cos’è |
|---|---|---|
| HTTPS | 9951 | il pannello e i moduli via TLS |
| HTTP | 9950 | il pannello e i moduli via HTTP semplice |
| Git via SSH | 9952 | Git via SSH |
| Porta SSH negli URL di clonazione | 0 | la porta mostrata negli indirizzi di clonazione SSH; 0 significa la porta SSH qui sopra. Impostala quando un router inoltra un’altra porta al NAS. |
| Host SSH negli URL di clonazione | vuoto | il nome o l’indirizzo mostrato negli indirizzi di clonazione SSH. Vuoto significa il nome host Git, altrimenti l’host dell’URL pubblico, altrimenti l’indirizzo LAN del NAS; per un nome pubblicato tramite il Cloudflare Tunnel, l’indirizzo LAN e la porta Git via SSH. Impostalo su un nome solo DNS o sull’indirizzo del router quando il router inoltra la porta SSH. |

Le porte vanno da 1024 a 65535 e sono diverse tra loro. Le porte dei moduli su 127.0.0.1 (9960 e 9961) sono rifiutate,
e così la porta che usa httpd o il server del runner CI quando un altro programma ha preso 9960 o 9961 (il messaggio
la nomina). Quando il salvataggio lascia nginx sulle vecchie porte perché non ha potuto usarne una nuova, una notifica
dice perché. nginx ascolta anche su IPv6 quando il NAS lo ha.

### HTTP semplice

- “Servi” (il predefinito): il pannello e i moduli rispondono anche su HTTP semplice. Tienilo quando un proxy sulla
  LAN parla HTTP semplice alla porta 9950.
- “Reindirizza a HTTPS”: una richiesta su HTTP semplice riceve un reindirizzamento (307) alla porta HTTPS. Una
  richiesta da un proxy attendibile che dice che il suo client ha usato HTTPS è servita su HTTP semplice, così un tale
  proxy non va in loop.

La pagina di accesso avvisa quando è arrivata via HTTP semplice: la password attraversa allora la rete in chiaro.

### Il certificato

Al primo avvio bvsd crea un certificato autofirmato (ECDSA P-256) per:

- il nome host del NAS, i nomi host dei moduli e `localhost`;
- gli indirizzi LAN del NAS e `127.0.0.1`.

È valido 825 giorni, il massimo che i dispositivi Apple accettano, anche per un certificato reso attendibile a mano.
bvsd lo controlla all’avvio e una volta al giorno, e ne crea uno nuovo quando restano 30 giorni o meno, quando cambia
l’insieme dei nomi, o quando la sua chiave non corrisponde. Uno nuovo invia una [notifica](#notifiche). La pagina Rete
mostra i suoi nomi, la sua validità e la sua impronta SHA-256: confronta l’impronta prima che un dispositivo se ne
fidi.

“Aggiungi il tuo certificato” accetta una catena di certificati e la sua chiave privata in formato PEM:

- prima il certificato del server, poi i certificati intermedi;
- una chiave senza passphrase (nginx si avvia senza intervento);
- la chiave deve appartenere al primo certificato, ogni certificato deve essere firmato dal successivo, nessuno può
  essere scaduto, e il primo deve nominare i suoi host come subject alternative name.

La coppia è salvata in `etc/tls/` nella cartella dei dati con modalità 600. Quando un certificato caricato scade o
smette di superare questi controlli, nginx serve al suo posto quello autofirmato e una notifica dice perché. Anche un
certificato caricato con 30 giorni o meno rimasti invia una notifica. “Nuovo certificato autofirmato” crea una nuova
coppia autofirmata e torna a essa.

I client che passano per un proxy vedono il certificato del proxy, non questo.

**Scarica il certificato** nella pagina Rete salva il certificato che il pannello serve, senza la sua chiave, come
`<nome>.crt`. Aggiungilo ai certificati attendibili di un dispositivo e i browser di quel dispositivo si fidano del
pannello; le viste delle estensioni dell’editor ne hanno bisogno ([Estensioni](#estensioni)). Di un nuovo certificato
autofirmato bisogna fidarsi di nuovo.

### Proxy attendibili

Un elenco di indirizzi IP o intervalli CIDR, come `192.168.1.5`, l’indirizzo del proxy davanti al NAS. Solo per una
richiesta la cui connessione TCP arriva da uno di essi nginx prende:

- l’indirizzo del client da `X-Forwarded-For`;
- lo schema usato dal client da `X-Forwarded-Proto` (contano solo `http` o `https`).

Una richiesta da qualsiasi altro indirizzo conta come proveniente direttamente dal suo client, e nginx sostituisce le
sue intestazioni `X-Forwarded-*` con i propri valori. Un intervallo rende attendibile ogni host che contiene: ognuno
di essi può allora impostare l’indirizzo del client e lo schema, quindi indica l’indirizzo proprio del proxy. Un
intervallo di tutti gli indirizzi (`0.0.0.0/0`, `::/0`) è rifiutato.

### URL pubblico

Solo l’indirizzo, come `https://git.example.com`, senza percorso. Le sezioni dei moduli dicono quali collegamenti
iniziano con esso.

## Dietro un reverse proxy (Plesk)

La configurazione abituale mette TLS su un proxy con un certificato pubblico e lo lascia parlare HTTP semplice alla
porta 9950 sulla LAN. Gli esempi qui sotto usano `bvs.example.com` per il nome pubblico, `192.168.1.20` per il NAS e
`192.168.1.5` per il server Plesk; metti i tuoi al loro posto.

```mermaid
flowchart LR
    client["Browser, git, client WebDAV"] -->|"HTTPS 443"| plesk["nginx di Plesk<br/>bvs.example.com<br/>certificato pubblico"]
    plesk -->|"HTTP 9950<br/>Host, X-Forwarded-For,<br/>X-Forwarded-Proto: https,<br/>Upgrade per WebSocket"| nas["nginx sul NAS<br/>192.168.1.20"]
    nas --> panel["/ pannello, /api/ bvsd"]
    nas --> mods["/git/, /svn/, /bucket/, /ide/"]
    sshc["git via SSH"] -->|"TCP 9952, non attraverso il proxy"| nas
```

### Sul NAS

1. Nella pagina Rete, aggiungi l’indirizzo del server Plesk (come lo vede il NAS) ai proxy attendibili.
2. HTTP semplice può restare su “Servi”. Anche “Reindirizza a HTTPS” funziona, una volta che il server Plesk è un
   proxy attendibile.

### In Plesk

Metti queste direttive nelle impostazioni di Apache e nginx del dominio, nel campo “Direttive nginx aggiuntive”
(nell’interfaccia inglese “Additional nginx directives”):

```nginx
location ~ ^/ {
    proxy_pass http://192.168.1.20:9950;
    proxy_http_version 1.1;

    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # WebSocket: l'editor e il suo terminale
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;

    # push e caricamenti grandi: nessun limite di dimensione, in streaming, un'ora di silenzio consentita
    client_max_body_size 0;
    proxy_request_buffering off;
    proxy_buffering off;
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}
```

`location ~ ^/` è una location con espressione regolare. nginx preferisce un’espressione regolare che corrisponde a un
semplice `location /` (Plesk ne scrive una propria), quindi questo blocco serve ogni percorso del sito, e `proxy_pass`
senza percorso passa il percorso della richiesta senza cambiarlo. Nella stessa pagina delle impostazioni, disattiva
“Servi i file statici direttamente tramite nginx”: se è attivo, Plesk risponde alle richieste di file `.js`, `.css` e
immagini dalla cartella del dominio, e il pannello non si carica.

Nelle impostazioni di hosting del dominio, attiva il reindirizzamento permanente 301 da HTTP a HTTPS adatto alla SEO
(nell’interfaccia inglese “Permanent SEO-safe 301 redirect from HTTP to HTTPS”). Le direttive nginx aggiuntive valgono
anche per il sito del dominio su HTTP semplice alla porta 80; senza il reindirizzamento, il sito risponde anche lì, e
le password attraversano internet senza cifratura.

Perché ogni riga conta:

| Direttiva | Che cosa non funziona senza |
|---|---|
| `Host $http_host` | nginx sul NAS sceglie il blocco server in base a `Host`, quindi un nome host di modulo non corrisponde mai. bvsd confronta l’`Origin` della pagina con `Host`, porta compresa, quindi ogni modifica nel pannello fallisce con `403 csrf`; `$host` perderebbe una porta pubblica diversa da 443 e fallirebbe allo stesso modo. |
| `X-Forwarded-For` | ogni client conta come il server Plesk: cinque password sbagliate di una sola persona bloccano tutti per 15 minuti |
| `X-Forwarded-Proto $scheme` | il cookie di sessione non è marcato `Secure`, e in modalità “Reindirizza a HTTPS” ogni richiesta viene mandata alla porta 9951 del nome pubblico |
| `Upgrade`, `Connection` | la pagina dell’editor si apre ma l’editor e il suo terminale non si collegano mai |
| `client_max_body_size 0`, `proxy_request_buffering off`, i timeout | ai push e caricamenti grandi si applicano il limite di dimensione e i timeout propri del proxy |

`X-Forwarded-For` e `X-Forwarded-Proto` contano solo quando il server Plesk è tra i proxy attendibili. Quando non c’è,
nginx sul NAS li ignora.

### Git via SSH

Un proxy HTTP non trasporta SSH. Git via SSH va direttamente alla porta 9952 del NAS, o tramite un inoltro di porta
TCP sul router. “Porta SSH negli URL di clonazione” nella pagina Rete imposta la porta che mostrano gli indirizzi di
clonazione, e “Host SSH negli URL di clonazione” il nome o l’indirizzo, quando il nome pubblico raggiunge solo il
proxy.

### Verificare la configurazione

- Il pannello si apre su `https://bvs.example.com/`, e l’accesso e il salvataggio di un’impostazione funzionano.
- Gli strumenti per sviluppatori del browser mostrano il cookie di sessione `bvs_session` con l’attributo `Secure`.
- La pagina Log mostra `nginx-access`: il primo campo di ogni riga è l’indirizzo proprio del client, non quello del
  server Plesk.

## Nomi host dei moduli

Un modulo può avere un proprio nome host (pagina Rete, Nomi host). nginx serve allora quel modulo da solo alla radice
del nome. Il nome deve puntare al NAS, o al proxy davanti a esso. Su un proxy, ogni nome host ha bisogno di un proprio
sito, con le stesse direttive viste sopra.

| Modulo | Alla radice del suo nome host | Presente anche lì |
|---|---|---|
| Git | Forgejo | `/git/` su ogni altro nome risponde 302 verso il nome host Git, con lo stesso percorso, la stessa query e la stessa porta |
| Editor | code-server | la pagina di accesso del pannello e `/api/`, così un utente può accedere su quel nome |
| SVN e Bucket | httpd | i repository restano su `/svn/<nome>` e i bucket su `/bucket/<nome>` |

Un modulo disattivato risponde `503` con una frase che lo dice, sui suoi percorsi e sul suo nome host.

### Un solo accesso per tutti i nomi host

Il cookie di sessione appartiene al nome su cui è stato impostato, a meno che l’impostazione `network.cookie_domain`
non indichi un dominio padre di tutti i nomi host dei moduli. Con `example.com`, un accesso su `bvs.example.com`
raggiunge anche `git.example.com` e `ide.example.com`. bvsd rifiuta un suffisso pubblico (`com`, `com.pl`, `co.uk`,
`myqnapcloud.com`), un dominio che non è padre di tutti i nomi host dei moduli, e qualsiasi dominio finché non è
impostato alcun nome host di modulo. Ogni sito sotto quel dominio riceve il cookie, quindi imposta il padre comune più
ristretto.

La pagina Rete non mostra questa impostazione. L’admin del NAS la imposta tramite il socket CLI con il `curl` del
pacchetto:

```bash
/usr/bin/bucketvs/bin/curl --unix-socket /usr/bin/bucketvs/var/run/bvsd-cli.sock \
  -X PUT -H 'Content-Type: application/json' \
  -d '{"network":{"cookie_domain":"example.com"}}' \
  http://bvsd/api/v1/settings
```

Un valore vuoto (`""`) tiene di nuovo il cookie su ciascun nome.

## Pubblicare su internet (Cloudflare Tunnel)

La pagina **Pubblicazione** (Amministrazione, solo admin) mette il pacchetto su internet con nomi di un dominio il cui DNS
è su Cloudflare. `cloudflared`, fornito dal pacchetto, apre la connessione dal NAS verso Cloudflare: non si apre
alcuna porta del router, e Cloudflare tiene i certificati dei nomi. Ogni richiesta passa per il nginx del
pacchetto: il pannello e la sua API richiedono l’accesso, e senza account si apre solo ciò che hai reso pubblico
(repository pubblici, la loro vista web, il repository dei pacchetti).

Ci sono due tipi di nome, al massimo uno per tipo:

| Tipo | Esempio | Che cosa serve |
|---|---|---|
| Nome host principale | `code.example.com` | il pannello e la sua API, Forgejo su `/git/`, la vista web su `/svn-web/` e `/bucket-web/`, il repository dei pacchetti su `/repo/` (in sola lettura) e la guida. `/svn/` e `/bucket/` lì rispondono 404, e anche `/ide/` finché non pubblichi l’editor (sotto) |
| Nome host dei pacchetti | `qpkg.example.com` | solo GET e HEAD di `/repo/`, per App Center su altri NAS; tutto il resto risponde 404 |

### Che cosa serve al token

Crea un token API su dash.cloudflare.com (My Profile, API Tokens) con questi permessi, per la zona dei nomi:

- Account, Cloudflare Tunnel, Edit
- Zone, DNS, Edit
- Zone, Cache Rules, Edit
- Zone, Cache Purge, Purge

Il pacchetto conserva il token sul NAS in un file che solo root legge, e non lo mostra mai più.

Se il token ha Client IP Address Filtering, inseriscici entrambi gli indirizzi pubblici del NAS, IPv4 e IPv6: il NAS
contatta Cloudflare via IPv6 quando ha un indirizzo IPv6. Altrimenti la pagina Pubblicazione rifiuta con `token_ip_filter`
e indica l’indirizzo che Cloudflare ha visto.

### Pubblicare

Inserisci i nomi e il token, poi **Pubblica**. Il pacchetto prima verifica tutto su Cloudflare e non cambia nulla lì
quando una verifica fallisce; il messaggio dice che cosa correggere:

- un permesso che manca al token, con il suo nome;
- un nome in una zona che il token non vede;
- un record DNS che il nome ha già (il suo tipo e dove punta): eliminalo su Cloudflare o scegli un altro nome;
- un nome host principale mentre **Navigazione pubblica** (pagina Git) è disattivata, perché i suoi visitatori non hanno un
  account;
- nomi in zone di due account Cloudflare.

Poi crea il tunnel (chiamato `bucketvs-<nome del NAS>` a meno che tu non dia un nome; un tunnel con quel nome già
presente su Cloudflare viene ripreso), fa puntare ogni nome al tunnel con un record CNAME proxato, aggiunge le regole
di cache e avvia cloudflared. **Pubblica di nuovo** con gli stessi valori non cambia nulla. Imposta l’URL pubblico
(Rete) sull’indirizzo `https://` del nome host principale, così i collegamenti e gli indirizzi di clonazione HTTPS di Forgejo lo
nominano; la pagina te lo ricorda finché sono diversi.

Git via SSH non passa per il tunnel: verso un nome pubblicato Cloudflare trasporta solo HTTP. L’indirizzo di
clonazione SSH è quindi l’indirizzo LAN del NAS con la sua porta Git via SSH (9952), nel pannello e nel riquadro di
clonazione di Forgejo, e la pagina Git e la Panoramica dicono “La clonazione via SSH funziona nella rete locale; da
internet clona via HTTPS.” Per SSH da internet, inoltra la porta SSH sul router e imposta **Host SSH negli URL di
clonazione** (Rete) sull’indirizzo pubblico del router o su un nome solo DNS che punta a esso, con **Porta SSH negli
URL di clonazione** quando il router usa un’altra porta.

Il nome host principale risponde 404 a `/ide/` finché un admin non accende **Pubblica l’editor sul nome host principale** nella
pagina **Pubblicazione**. Il terminale dell’editor è una shell root sul NAS, e chiunque vi acceda da internet come
developer o admin la ottiene: attiva quindi prima l’autenticazione a due fattori per quegli account. Con l’interruttore
acceso l’editor passa per lo stesso accesso della LAN, il suo proxy delle porte (`/ide/proxy/<port>/`) lì resta
spento e Cloudflare non tiene mai le sue pagine in cache. Finché è acceso, la Panoramica mostra **Editor su
internet**.

Finché il modulo CI è attivo, un workflow inviato con push a qualsiasi repository di questo Forgejo viene eseguito come
root sul NAS tramite il runner del NAS stesso, sia che sia stato inviato da Internet sia dalla LAN: lo stesso potere
del terminale dell’editor. La pagina **Pubblicazione** lo dice sotto **Cosa raggiunge internet**. Concedi l’accesso in push
solo a chi daresti la password di amministratore del NAS.

### La cache

Con **Preimpostazioni di cache** attive, Cloudflare tiene i pacchetti (`*.qpkg`) per un anno, le icone per un giorno,
`repo.xml` per 5 minuti e i file del pannello per un anno; non tiene mai l’API, le pagine di Forgejo, le pagine di
accesso né la vista web. Una pubblicazione nel repository dei pacchetti toglie subito `repo.xml` (e un’icona cambiata
o i file di una versione eliminata) dalla cache di Cloudflare. Il pacchetto aggiunge le proprie regole in fondo alle
regole di cache della zona e non cambia mai le altre.

### Accesso da internet

La prima password non può mai essere impostata tramite un nome pubblicato: la configurazione iniziale accetta solo un
client locale. Le password errate contano per client internet, come sulla LAN. Conta anche una password inviata a
Forgejo con git; un token di accesso di Forgejo funziona come sempre. Cloudflare accetta richieste fino a 100 MB,
quindi un push o un caricamento più grande passa dall’indirizzo LAN.

### Fermare e rimuovere

**Ferma** termina cloudflared; i nomi mostrano allora la pagina di errore di Cloudflare, e su Cloudflare non cambia
nulla. **Rimuovi da Cloudflare** elimina i record DNS, le regole di cache e il tunnel creati dal pacchetto (un tunnel
ripreso resta) e dimentica il token. Mentre ci sono nomi pubblicati, la Panoramica mostra un’etichetta **Internet**
con lo stato del tunnel.

## Moduli

La Panoramica mostra ogni modulo con i suoi processi, la memoria e la CPU, e lo attiva e disattiva (solo admin). Un
modulo disattivato dice che cosa offre; un admin lo attiva con il suo pulsante (**Attiva Git**, **Attiva Editor** e così
via) sul suo riquadro, sulla sua pagina o, per l’Editor, nella scheda Editor.

- Attivare un modulo esegue un job: il modulo si prepara, l’interruttore viene salvato, e i suoi processi si avviano.
  Il job li attende fino a 30 secondi.
- CI ha bisogno di Git: Git non può essere disattivato mentre CI è attivo.
  Mentre Git è disattivato, il pulsante di CI dice **Attiva Git e CI** e attiva prima Git.
- Un modulo disattivato non esegue alcun processo e non usa memoria.
- Il nucleo è sempre attivo.

| Stato | Significato |
|---|---|
| in esecuzione | ogni processo del modulo è in esecuzione |
| in avvio | un processo è partito meno di un secondo fa |
| degradato | un processo attende di essere riavviato, i file del modulo non si sono potuti scrivere, o il suo controllo di salute fallisce |
| si riavvia in un ciclo continuo | un processo è uscito 5 volte in 10 minuti; bvsd lo riavvia ogni 15 minuti, ed è partita una notifica |
| fermo | i processi del modulo non sono in esecuzione (uno spostamento dei dati li ha fermati, o non sono riusciti ad avviarsi) |
| disattivato | il modulo è disattivato |

Un processo che esce viene riavviato dopo 1 secondo, poi 2, 4 e così via fino a 60 secondi. Dopo 10 minuti di
esecuzione l’attesa riparte da 1 secondo. Un processo che si riavvia in un ciclo continuo riparte subito quando il suo
modulo viene disattivato e riattivato.

## Git

Il modulo Git esegue Forgejo, un’interfaccia web per repository git con issue, pull request, release, wiki, LFS,
mirror e webhook. Un admin lo attiva con **Attiva Git** nella Panoramica o nella pagina Git. Il job prepara le
cartelle e il database di Forgejo in `<dati>/forgejo`, aggiunge ogni account del pannello a Forgejo, e lo avvia.
Forgejo si apre poi su `/git/` all’indirizzo del pannello, o su `/` del nome host Git quando è impostato (vedi [Nomi
host dei moduli](#nomi-host-dei-moduli)).

**Nuovo repository** nella pagina Git, e nel riquadro Git della Panoramica finché non ce n’è nessuno, apre il modulo
di Forgejo per un repository.
Il pannello lo offre, insieme a **Migra o fai il mirror di un repository**, dal ruolo developer in su: Forgejo non
lascia creare repository a un viewer o a un member.

### Repository e account

- **Un solo accesso.** Forgejo non ha un accesso proprio: `/git/user/login` porta all’accesso del pannello, con il
  codice di autenticazione quando l’account ne ha uno, e poi di nuovo a Forgejo.
- **Gli account vengono dal pannello.** Ogni account del pannello ha un account Forgejo con lo stesso nome e la stessa
  password. Il ruolo `admin` è amministratore del sito in Forgejo. Un account disattivato, o uno che deve prima
  cambiare la password, non può accedere neanche a Forgejo. Una modifica nel pannello raggiunge Forgejo in pochi
  secondi. Le pagine Utenti e Git mostrano lo stato di ogni account in Forgejo, e **Sincronizza ora tutti gli
  account** nella pagina Git esegue di nuovo la sincronizzazione.
- **Eliminare un account** nel pannello ritira il suo account Forgejo: non può più accedere, e i suoi token e le sue
  chiavi SSH vengono rimossi, ma i suoi repository restano. Il nome non può essere dato a un nuovo account del
  pannello finché un admin non rinomina o elimina il vecchio account Forgejo nell’amministrazione del sito di Forgejo
  (**Account utente**).
- **Un account creato in Forgejo stesso** non viene mai modificato dal pannello. Un account del pannello con lo stesso
  nome mostra il motivo e attende finché uno dei due non viene rinominato.
- **Autenticazione a due fattori e git.** Forgejo non accetta password per un account con l’autenticazione a due fattori attiva.
  git via HTTP o HTTPS richiede allora un token di accesso di Forgejo (in Forgejo, **Impostazioni**, **Applicazioni**)
  al posto della password. Le chiavi SSH funzionano come prima.
- **I repository** si creano, si importano e si mettono in mirror nelle pagine di Forgejo. Le importazioni da
  indirizzi sulla LAN sono consentite per impostazione predefinita (impostazione: `git.allow_local_networks`), perché
  un NAS di solito fa mirror dalla propria rete.

### Clonare via HTTPS e SSH

Nella pagina Git, **Clona un repository** scrive entrambi i comandi per un proprietario e un repository:

| Via | Indirizzo | Accesso |
|---|---|---|
| HTTPS (o HTTP) | `https://<indirizzo del NAS>:9951/git/<proprietario>/<repo>.git` | nome utente e password del pannello, o un token di accesso |
| SSH | `ssh://git@<indirizzo del NAS>:9952/<proprietario>/<repo>.git` | una chiave SSH aggiunta in Forgejo in **Impostazioni**, **Chiavi SSH / GPG** |

- Con un URL pubblico (dietro un proxy, [URL pubblico](#url-pubblico)) l’indirizzo HTTPS lo usa; con un nome host Git
  il percorso è `/<proprietario>/<repo>.git` su quel nome.
- La porta SSH è `network.ssh_port` (9952), o `network.public_ssh_port` quando un router vi inoltra un’altra porta. Il
  proxy non trasporta SSH: inoltra la porta sul router (vedi [Git via SSH](#git-via-ssh)).
- Un nome pubblicato tramite il Cloudflare Tunnel non trasporta SSH, quindi l’indirizzo di clonazione SSH indica
  allora l’indirizzo LAN del NAS, a meno che `network.public_ssh_host` (Rete, «Host SSH negli URL di clonazione») non
  indichi un nome solo DNS o l’indirizzo del router.
- I push grandi non vengono tagliati: nginx non imposta alcun limite alla dimensione del corpo su `/git/`.

### Mostrare repository a persone senza account

**Navigazione pubblica** nella pagina Git permette a chiunque raggiunga Forgejo di leggerne i repository pubblici
senza account. In Forgejo ogni repository è pubblico o privato (le sue **Impostazioni**, **Visibilità**). Con
l’interruttore attivo, un visitatore senza account può:

- aprire un repository pubblico, le sue cartelle e i suoi file, e scaricare un file così com’è;
- leggere la cronologia, aprire un commit con il suo diff (anche come file `.diff` o `.patch`) e confrontare due
  commit;
- vedere le sue release, scaricare un archivio, e clonarlo via HTTP
  (`git clone https://<indirizzo del NAS>:9951/git/<proprietario>/<repo>.git`);
- trovare i repository pubblici in **Esplora** e nel profilo del loro proprietario.

Un repository privato resta nascosto: il suo indirizzo risponde “non trovato”. Mentre l’interruttore è attivo, Forgejo
inoltre non elenca alcun account (la scheda **Utenti** di Esplora e la ricerca utenti sono disattivate, anche per gli
utenti connessi), così un visitatore non può raccogliere i nomi degli account. Push, issue, pull request e
impostazioni richiedono comunque un account. Con l’interruttore disattivato, ogni pagina chiede prima al visitatore di
accedere.

Per condividere un repository, rendilo pubblico in Forgejo, attiva **Navigazione pubblica** e invia l’indirizzo. Su un
nome host Git l’indirizzo è `https://<nome host Git>/<proprietario>/<repo>`.

### Nuovo progetto

**Nuovo progetto** è una pagina che porta un progetto, passo dopo passo, dal nulla a una prima versione compilata e
pubblicata. Si apre in una scheda tutta sua dalla tavolozza dei comandi (Ctrl+Maiusc+P, **Nuovo progetto**), dalla
cima della barra laterale dell’Editor, dall’intestazione della barra laterale Git, dalla pagina Git e dalla pagina
Progetti; un esempio in **Progetti Editor** nella pagina Esempi ha **Nuovo progetto da questo**. Richiede il ruolo
sviluppatore, i moduli Git ed Editor e il tuo account in Forgejo (la pagina Git ne mostra la sincronizzazione).
Finché Git o l’Editor è disattivato, la pagina lo dice sopra i passi, e un admin li attiva lì con un solo pulsante.

1. **Modello.** I modelli sono raggruppati: Programmi (C, C++ con CMake, Go, Rust, Node, Python, Java), Pacchetti per QTS
   (un pacchetto QPKG, un’app di App Builder, un pacchetto dai sorgenti), Kernel (un modulo del kernel, il driver
   USB-seriale ch341), Siti web (un sito statico: pagine in `src/`, un Makefile che compila `public/`, un controllo
   dei link come `make test`) ed Esempi. Ogni scheda dice cosa produce una compilazione e segna cosa manca ancora a
   questo NAS (QDK, un ambiente di compilazione, un modulo spento), con un link alla pagina che lo aggiunge.
2. **Nome e visibilità.** Il nome dà il nome al repository e alla cartella `projects/<nome>`; la pagina lo controlla
   mentre scrivi. Poi il proprietario (il tuo account, o un’organizzazione in cui Forgejo ti lascia creare
   repository), privato o pubblico, e una descrizione se vuoi. Per un’app di App Builder, il preset da cui parte e la
   destinazione.
3. **Servizio**, per un pacchetto QPKG e un pacchetto dai sorgenti: come il pacchetto si avvia e si ferma, costruito a
   blocchi. Scegli un preset (**Strumento da riga di comando** collega il programma in `/usr/bin`; **Servizio in
   background** aggiunge un account, una cartella dati sul volume, il programma avviato come quell'account, un'attesa
   della sua porta e un arresto con timeout; **App web** aggiunge il pulsante Apri di App Center; **Vuota** ha solo i
   verbi), poi cambia i suoi blocchi. La scheda di ogni blocco mostra le righe che scrive nello script mentre digiti,
   e un valore rifiutato è segnato sotto il suo campo. L'avvio non attiva mai l'Enable di App Center, e l'arresto non
   lo tocca mai. La vista Progetto cambia i blocchi in seguito.
4. **Compilazione.** L’ambiente in cui si compila il progetto, conservato per il progetto (la vista Progetto lo cambia in
   seguito), oppure nessuno, e bvsd ne sceglie uno a ogni compilazione. Il passo propone prima quello che bvsd
   sceglierebbe da sé, dove compilano `bvs qbuild` e la CI: l’Ubuntu 24.04 builder, altrimenti il Debian 12 builder,
   altrimenti un altro ambiente Debian o Ubuntu. Quando il Makefile del modello dà a ogni app il suo ambiente in una riga
   ENV, il passo lo dice e non ne chiede nessuno, e il progetto non ne conserva nessuno. Per un pacchetto dai sorgenti
   il passo dice anche che l’indirizzo della sorgente e lo sha256 della ricetta iniziale sono provvisori: la prima
   compilazione fallisce finché la ricetta non indica la vera sorgente del programma e lo sha256 di quel file. La
   pagina mostra il workflow che la CI esegue a ogni push. **Compila subito dopo la creazione** resta attivo finché
   non lo spegni; per un pacchetto dai sorgenti parte spento, e puoi accenderlo. Se sul NAS non c’è un ambiente Debian
   o Ubuntu, il passo lo dice: un admin vi crea l’ambiente di compilazione con **Crea questo ambiente**, e poi il
   passo lo propone; altrimenti disattiva **Compila subito dopo la creazione** e compila il progetto più tardi.
5. **Pubblicazione**, per un pacchetto QPKG, un’app di App Builder o un sito web: **Deploy di prova su questo NAS** (admin,
   solo pacchetti firmati; per un’app, **Attiva su questo NAS**), **Pubblica nel repository di App Center di questo
   NAS** e **Pubblica sul mio server web** (admin) su una delle [destinazioni web](#il-tuo-server-web-destinazioni-web);
   **Aggiungi una destinazione** ne salva una nuova. Ognuna è facoltativa. La scelta di pubblicazione diventa anche
   l’interruttore **Pubblica nel repository di App Center** del progetto (vista Progetto, Impostazioni): finché è
   acceso, la CI del progetto vi pubblica a ogni esecuzione.
6. **Revisione** elenca ogni risposta e i passi che verranno eseguiti. **Crea il progetto** li invia.
7. **Avanzamento** segue il job di ogni passo: il repository e il suo primo commit, la prima compilazione, poi il
   deploy di prova, la pubblicazione in App Center e quella sul web, uno dopo l’altro. Una riga mostra il suo stato e
   la fine del suo log; un clic mostra il log. Un passo fallito dice perché e offre **Riprova**, e i passi successivi
   aspettano. Alla fine, **Apri il progetto**, **Apri nell’editor** e **Apri in Forgejo**. Ricaricare la pagina o
   tornare alla sua scheda rilegge i job e non invia nessun passo due volte. Lasciare la pagina ferma la catena; la
   vista Progetto ha gli stessi passi.

Quando bvsd rifiuta il progetto (un nome preso da una cartella, un’app o un repository, un proprietario in cui non
puoi creare), la pagina torna al passo con quella risposta, mostra il motivo sotto il suo campo e conserva tutte le
altre risposte. Un modulo spento (un admin lo accende dalla pagina), nessun account Forgejo e nessun ambiente Debian o
Ubuntu per un’app di App Builder sono spiegati in Revisione.

Cosa fa il primo job:

- Crea il repository a tuo nome, privato a meno che tu non abbia scelto pubblico.
- Crea la cartella `projects/<nome>` della cartella dei dati con il primo commit: ogni file del modello e il workflow
  `.forgejo/workflows/build.yml`, su `main`. Il commit è tuo (nome e indirizzo sono impostati come autore nella
  cartella), e gli script del modello vi sono eseguibili. Invia questo commit al repository a tuo nome. `origin` è
  l’indirizzo HTTPS del repository, quindi il tuo prossimo push va lì. Il commit contiene anche la [skill per gli
  assistenti IA](#programmare-con-un-assistente-ia), `.claude/skills/qnap-dev/` e `AGENTS.md`. La cartella è nella
  cartella condivisa dell’editor: l’editor di ogni sviluppatore apre `projects/`, che il repository sia privato o
  pubblico.
- Conserva per il progetto l’ambiente scelto, a meno che il Makefile del modello dia a ogni app il suo ambiente. Per un’app
  di App Builder, crea l’app con il nome del progetto e le ricette del preset e la lega al progetto, così l’app può
  compilare anche le ricette che aggiungi alla cartella `recipes/` del progetto. Conserva anche la tua scelta di
  pubblicazione come interruttore Pubblica nel repository di App Center del progetto, prima che la prima esecuzione del
  workflow lo legga.

Il workflow gira sul runner del NAS a ogni push una volta acceso il modulo CI, nell’ambiente che il workflow nomina. Il
workflow QPKG pubblica il pacchetto nel [repository dei pacchetti](#repository-dei-pacchetti), che accetta solo
pacchetti firmati. Se il primo commit o il suo push fallisce dopo la creazione del repository, il job rimuove la
cartella, elimina di nuovo il repository e dice perché. Finché l’interruttore **Pubblica nel repository di App Center**
del progetto è spento, `bvs repo publish` nel workflow stampa un avviso invece di pubblicare, e l’esecuzione prosegue.

### La vista Progetto

Ogni cartella di `projects/` nella cartella dei dati ha una sua pagina: un progetto della procedura guidata, la copia di
un esempio da Progetti Editor o un repository che vi hai clonato. Si apre dal ramo **Progetti** della barra laterale
dell’Editor, dalla pagina **Progetti**, con **Apri il progetto** alla fine di Nuovo progetto e dalla tavolozza dei comandi (**Apri il
progetto …**, o Ctrl+P e il suo nome). Richiede il ruolo sviluppatore. Il menu di un progetto nella pagina Progetti ha
anche Compila, Ricompila, Pubblica e la pubblicazione sulle destinazioni web. Lo stesso menu accende o spegne la
pubblicazione della CI in App Center.

La pagina legge la cartella e da sola non vi cambia nulla. Ricava il tipo del progetto dai suoi file: un’app di App
Builder con il nome della cartella, un `qpkg.cfg` in cima (un pacchetto QPKG), un `Makefile` in cima, o nessuno di
questi, quando lo compila il workflow CI del progetto. Quando un’app di App Builder ha il nome della cartella, un
progetto con un `qpkg.cfg` in cima, o con un Makefile e un `qpkg.cfg` in `qpkg/`, mantiene la propria compilazione, e
Deploy di prova e Pubblica prendono ancora il suo QPKG.

La striscia in alto contiene i passi. Un passo che non può partire tiene il suo pulsante, disattivato, e dice perché
accanto e sotto la striscia. Un passo che il progetto non può proprio fare esce dalla striscia, e una frase sotto dice
che cosa crea il progetto e dove va il risultato della compilazione: la cartella del progetto per un programma, la
pagina Kernel per un modulo del kernel, la pagina di ogni app per un Makefile che compila solo app di App Builder.

- **Compila** gira come job nel pannello inferiore. Un pacchetto QPKG si compila con `qbuild` di QDK, un progetto con
  Makefile con `make`, entrambi nell’ambiente di compilazione del progetto. Il log nomina ogni `.qpkg` che la
  compilazione ha scritto in `build/` e se porta una firma del codice. Un’app di App Builder si compila come nella sua
  pagina. Quando il target predefinito di un Makefile esegue solo righe `./bvsapp.sh create` e `./bvsapp.sh build`,
  Compila fa lo stesso tramite App Builder: crea ogni app di una riga `create` che ancora non c’è, poi compila le app
  una dopo l’altra. Una riga `create` è `./bvsapp.sh create NOME FILE-DELLE-RICETTE [ENV] [LINK] [TARGET] [OTHERS]`. Un
  tale Makefile nomina il target predefinito e i suoi prerequisiti in `.PHONY` e contiene solo assegnazioni semplici,
  ogni variabile impostata una volta, e regole semplici; uno con altro (una condizione, un include) si compila con make.
  TARGET è `native`, `aarch64` o `armv7`, quindi una sola compilazione può fare un’app per questo NAS e una per ARM a 64
  bit. OTHERS dice cosa fa un’app statica con le ricette che non si possono collegare in statico (`refuse` o `bundle`).
  Un’app creata così prende anche le ricette proprie del progetto da `recipes/`, a meno che il nome della cartella non
  sia un nome di progetto (per esempio un nome con uno spazio). Quando un’app con quel nome c’è già con un altro target,
  o con un altro progetto mentre la cartella ha ricette proprie, Compila resta disattivato e dice come rimediare. Prima
  della prima compilazione il passo indica le app che compila. Un progetto con Makefile e il pacchetto in `qpkg/` crea
  il file di pacchetto con `make qpkg` nel terminale dell’editor; finché manca, Deploy di prova, Pubblica e Pubblica sul
  web dicono «Richiede un file di pacchetto».
- **Ricompila** compila da zero: `make clean`, poi `make`; per un pacchetto QPKG prima se ne vanno i vecchi `.qpkg` di
  `build/`. Un Makefile senza target `clean` non può ricompilare, e la pagina lo dice. Un’app di App Builder non ha
  Ricompila, e nemmeno un Makefile che crea e compila solo app di App Builder.
- **Esporta come QPKG** (admin) c’è per un’app di App Builder, come nella pagina dell’app. Pubblica aspetta allora
  un’esportazione, come nella pagina dell’app, e **Apri l’app** nell’intestazione apre quella pagina.
- **Deploy di prova** è per gli admin. Installa il `.qpkg` più recente su questo NAS con `qpkg_cli`, come App Center
  installa un file, e il job aspetta la conferma di App Center. Il pacchetto resta installato finché non lo rimuovi in
  App Center. Un pacchetto senza firma del codice viene rifiutato: il deploy di prova installa solo pacchetti firmati
  e non salta mai il controllo della firma di App Center (in App Center stesso il proprietario di un NAS può
  consentire app senza firma; questa pagina no). Firmalo con un certificato in file (caso 1 in [Firmare il codice di
  un QPKG](#firmare-il-codice-di-un-qpkg)). Il pacchetto di Bucket & Code Server IDE stesso viene rifiutato, perché
  installarlo sostituirebbe il pacchetto in esecuzione. Per un’app di App Builder, il deploy di prova attiva la sua
  compilazione su questo NAS.
- **Pubblica** mette il `.qpkg` più recente nel [repository dei pacchetti](#repository-dei-pacchetti), con le sue
  regole: la pubblicazione di un admin diventa la versione in elenco, quella di uno sviluppatore aspetta un admin, e un
  pacchetto senza firma viene rifiutato a meno che il repository non accetti pacchetti non firmati. Una riga sotto dice
  se anche la CI del progetto pubblica nel repository di App Center; questo pulsante pubblica in ogni caso.
- **Pubblica sul web** (admin) copia il `.qpkg` più recente, o la cartella del sito `public/` (altrimenti `dist/`), in
  una [destinazione web](#il-tuo-server-web-destinazioni-web) e lo rilegge dall’indirizzo pubblico della destinazione.
  Per un sito web creato dalla procedura guidata, il passo aspetta la prima compilazione, che scrive `public/`.

Un progetto con Makefile sceglie un target accanto a **Compila** (il target predefinito di make, il primo, a meno che tu
non ne scelga un altro); un progetto CMake si compila, o si compila ed esegue i suoi test. Un progetto di modulo del
kernel si compila nell’area di lavoro dell’officina del kernel per il kernel in esecuzione, e quella compilazione spetta
a un admin. Un passo in esecuzione ha **Annulla**, e il job di solito finisce subito. **Esegui il workflow** esegue a
mano il workflow CI del progetto quando ha un trigger `workflow_dispatch`; altrimenti il pulsante è spento e la pagina
dice perché. Il workflow di ogni modello ha quel trigger. **Installato su questo NAS** elenca i pacchetti installati dai
deploy di prova del progetto, con **Rimuovi da questo NAS** per un admin. **Elimina il progetto**, nel menu accanto ai
pulsanti dell’intestazione o nel menu del progetto nella pagina Progetti, è per un admin o per lo sviluppatore il cui
account Forgejo contiene il repository. Prima digiti il nome del progetto; la cartella se ne va, e il repository
Forgejo, un’app di App Builder con quel nome e i pacchetti installati restano. La pagina poi dà il link al repository.

**Impostazioni** contiene ciò che bvsd conserva per il progetto fuori dalla sua cartella. Per un progetto Makefile,
CMake o QPKG mostra l’ambiente in cui girano Compila e Ricompila, e lo cambia. Per un Makefile che crea e compila solo
app di App Builder, elenca ogni app con l’ambiente che la sua riga nel Makefile indica. Un progetto che crea un
pacchetto, o il cui workflow esegue `bvs repo publish`, vi ha l’interruttore **Pubblica nel repository di App Center**:
acceso, `bvs repo publish` nella CI del progetto aggiunge i suoi pacchetti al repository; spento, l’esecuzione CI salta
quel passo con un avviso. Un progetto senza una scelta conservata (la copia di un esempio, un clone) lo ha acceso.
**Git** mostra il ramo, quanto è avanti o indietro rispetto al remoto come l’ha lasciato l’ultimo fetch (la pagina non
fa mai fetch), l’ultimo commit e i percorsi modificati. **File** elenca una cartella alla volta; scegli un file per
leggerlo, un `.qpkg` si scarica. Le modifiche restano nell’editor, e la cartella `.git` è esclusa.

**Ricette**, per un progetto con una propria cartella `recipes/`, elenca le ricette che prendono le sue app: ognuna
con la sua versione, la versione della libreria che sostituisce quando ha il nome di una ricetta della libreria, e
sotto ciò che il controllo delle ricette ha trovato, con file e riga. Una ricetta che bvsd esclude ha una riga con il
motivo; oltre tre escluse nello stesso modo, una riga conta le altre. Ciò che il controllo ha trovato sulla cartella
`recipes` nel suo insieme ha una riga propria. Un clic apre la cartella della ricetta nell’editor; il menu copia anche
il comando che esegue lo stesso controllo in un terminale del NAS: `bvs recipe lint` con la cartella del progetto. La
scheda compare anche quando `recipes` del progetto è un link, un file, una cartella che bvsd non può leggere o una
cartella con soli link, e dice perché non ne viene letta nessuna ricetta.

**Servizio e pacchetto** (un pacchetto QPKG, o un progetto Makefile con il pacchetto in `qpkg/`, come un pacchetto dai
sorgenti) contiene lo script di avvio e arresto del pacchetto come blocchi, i campi del suo `qpkg.cfg` (**Pacchetto**)
e i file scritti da essi (**Script**). **Salva** mostra le differenze di ogni file, poi ne esegue il commit nel
repository del progetto; se nel frattempo il progetto è cambiato, pianifica di nuovo. **Modifica a mano** disattiva i
blocchi, così modifichi lo script nell'editor; **Ricollega i blocchi** mostra cosa sostituirebbe una nuova scrittura.
**Aggiorna la libreria** porta nel pacchetto il `bvs-qpkg.sh` di questo Bucket & Code Server IDE, con le sue differenze. Un progetto
creato prima dei blocchi offre **Usa i blocchi per questo script**.

**Versioni** elenca le versioni a cui il progetto può passare, e **Passa a questa versione** lo fa:

| Versione | Cosa fa il passaggio |
|---|---|
| Un `.qpkg` in `build/`, o un’esportazione di App Builder | un deploy di prova di quel file (admin) |
| La compilazione di App Builder | la attiva (admin); App Builder ne conserva una |
| Una versione nel repository dei pacchetti | la rende la versione in elenco (admin) |
| Un tag git | fa il checkout del tag nella cartella |

Un clic sceglie una versione; il suo menu fa anche il deploy di prova, la pubblica, la pubblica su una destinazione
web o scarica un `.qpkg`. Un tag viene preso come HEAD staccato, e **Torna a main** nella scheda Git torna al ramo. Il
passaggio viene rifiutato finché la cartella ha modifiche non committate; il messaggio le elenca, e tu prima le
committi o le metti da parte nell’editor. Spunta due versioni e **Confronta le due** elenca i file che una ha e l’altra
no, e i file cambiati. Due tag, o due pacchetti, si confrontano per contenuto; un pacchetto con un commit solo per
dimensione. Nella pagina **Pacchetti** un admin installa anche una versione del repository su questo NAS (**Installa
su questo NAS**), con i controlli del deploy di prova.

### Il tuo server web (destinazioni web)

Una destinazione web è dove un admin pubblica un pacchetto o un sito web sul proprio server web: una cartella di
questo NAS (la condivisione Web di QTS, servita dal Web Server di QTS, o un’altra cartella condivisa) o un remote
rclone (SFTP, FTP, WebDAV, S3, configurato nel `rclone.conf` dei backup), con l’indirizzo pubblico che la serve. Sono
nella pagina **Pubblicazione**, sotto **Destinazioni web**: **Aggiungi una destinazione**, **Cambia**, **Rimuovi** e
**Prova**, che vi copia un piccolo file, lo recupera dall’indirizzo pubblico e lo elimina di nuovo.

Una destinazione cartella è una cartella a sé dentro una condivisione (`/share/Web/miosito`, mai un’intera
condivisione né un volume), fuori dalla cartella dei dati, perché la pubblicazione di un sito toglie dalla
destinazione ciò che il sito non contiene più. L’indirizzo inizia con `http://` o `https://` e non contiene utente né
password.

Ogni pubblicazione rilegge il file dall’indirizzo pubblico: il `.qpkg` di un pacchetto con il suo `.sha256`,
confrontato per sha256, o l’`index.html` di un sito, confrontato byte per byte. Quando l’indirizzo non serve ciò che è
stato copiato, il job fallisce con `web_check:` ed entrambi i valori.

## Runner CI

Il modulo CI esegue Forgejo Actions: un file di workflow in `.forgejo/workflows/` di un repository gira su un runner
quando viene fatto un push. Richiede il modulo Git. Un admin lo attiva in **Moduli**, **CI**, **Attiva**. Il job
registra il runner proprio del NAS presso Forgejo, riavvia Forgejo con Actions attivo, e avvia il runner; termina
quando il runner ha raggiunto Forgejo.

### Il runner sul NAS

- **Le sue etichette** sono `bucketvs`, `qnap-x86_64` (o `qnap-aarch64`) e `self-hosted`, più le etichette aggiuntive
  che un admin imposta nella pagina CI. Un job il cui `runs-on` ne nomina una gira qui, per esempio
  `runs-on: bucketvs`.
- **Esegue ogni job sul NAS stesso, come admin**, non in un container. Un job ha gli strumenti del pacchetto (git,
  curl, make e gli altri), Node per le azioni JavaScript come `actions/checkout`, e `bvs env run <env> -- <command>`
  per una build dentro un ambiente. Chi può fare push di un workflow in un repository può quindi eseguire comandi
  come admin sul NAS. Un member o un viewer non può creare, fare fork o importare repository, né creare
  organizzazioni, in Forgejo; developer e admin possono. Un viewer non fa mai push. Mentre la CI è attiva, il push di
  un member in un repository con le Actions attive viene rifiutato con il motivo, e così un push con una deploy key.
  Anche il push di un member, o con una deploy key, in un repository che un account con ruolo inferiore a developer
  possiede o amministra viene rifiutato, pure con la CI disattivata: un tale account potrebbe attivare da sé le
  Actions. bvsd disattiva le Actions entro un minuto in ogni repository che un member o un viewer possiede o in cui
  un tale account può scrivere, e prima dell’avvio del runner quando la CI viene attivata.
- **Ciò che resta** è un atto proprio di un developer o di un admin: attivare le Actions, o avviare un workflow, in
  un repository in cui un member ha scritto prima, oppure dare a un tale account l’accesso admin a un repository in
  cui ha scritto prima. Un developer che unisce o compila i commit di un member li esegue anch’egli come admin.
- **Job contemporanei** è `ci.capacity` (1 per impostazione predefinita). Un job che nessun runner online può prendere
  attende in Forgejo.
- **I repository creati prima che CI fosse attivo** hanno le Actions di Forgejo disattivate, quindi un workflow
  inviato con push non avvia nulla: attivale nelle **Impostazioni** del repository, **Unità** (Actions). I repository
  creati mentre CI è attivo le hanno.
- **Le azioni** come `actions/checkout@v4` vengono scaricate da `https://data.forgejo.org`, quindi il NAS ha bisogno
  di accesso a internet per i workflow che le usano. Il runner non ha cache: `actions/cache` non trova nulla e il job
  prosegue.
- **Gli artefatti** (`actions/upload-artifact`) vengono caricati all’indirizzo proprio di Forgejo (quello della pagina
  Git), quindi il NAS deve raggiungere quell’indirizzo, e fidarsi del suo certificato quando è HTTPS.
- **Lo stato del runner** è in `<dati>/runner`; il suo log è **runner** in **Log**. Disattivare il modulo ferma il
  runner e disattiva Actions in Forgejo.

### Runner esterni

Una macchina di build altrove (un server, una VM) può prendere job anche da questo Forgejo. Nella pagina CI, in
**Registra un runner esterno**, un admin le dà un nome e ottiene i comandi per Linux x86_64 e aarch64. Questi
scaricano forgejo-runner 13.2.0 da code.forgejo.org, ne verificano lo sha256, tengono il token del runner in
`runner-token` (leggibile solo dal suo proprietario) e avviano il runner con l’etichetta `linux-amd64` (o
`linux-arm64`). Il runner compare subito nell’elenco, offline finché i comandi non vengono eseguiti. Il token è
mostrato solo in quel momento. Un runner del genere esegue i suoi job sulla propria macchina, come l’utente che lo ha
avviato, e raggiunge Forgejo al suo indirizzo pubblico.

## SVN e Bucket

Il modulo SVN e Bucket serve i repository Subversion su `/svn/<nome>` e i bucket su `/bucket/<nome>`. Un bucket è
un’unità di rete (WebDAV) in cui ogni file salvato diventa una revisione, così qualsiasi versione precedente di un
file può essere letta o ripristinata. Un admin attiva il modulo in **Moduli**; la pagina **SVN e Bucket** crea, elenca
ed elimina poi entrambi i tipi.

- **Gli account** sono quelli del pannello. I permessi si impostano per repository: lettura e scrittura, lettura, o
  nessuno, per ogni account, più un predefinito per ogni account non elencato. Gli admin leggono e scrivono sempre;
  member e developer scrivono dove hanno lettura e scrittura; i viewer al massimo leggono. Un nuovo repository non dà
  alcun accesso a nessuno tranne gli admin finché un admin non lo imposta (l’icona delle persone sulla sua riga,
  **Permessi per `<name>`**).
  Sotto admin, la pagina mostra a ciascuno il proprio accesso a ogni repository e bucket.
- **Le password errate** contano come all’accesso del pannello: 5 password errate entro 15 minuti bloccano
  quell’account da quell’indirizzo, e l’indirizzo stesso, per 15 minuti.
- **Autenticazione a due fattori.** Un account con l’autenticazione a due fattori non usa la sua password per SVN o WebDAV. Crea
  una **password per app** nella pagina del suo account (**Password per app per WebDAV e SVN**): 25 caratteri casuali,
  mostrati una sola volta. Una nuova sostituisce la vecchia; un cambio di password o la disattivazione dell’autenticazione a
  due fattori la rimuove.
- **Eliminare** un repository lo sposta in `<dati>/trash`, dove il job `trash` lo rimuove dopo 7 giorni.

### Repository SVN

```bash
svn checkout "https://<indirizzo del NAS>:9951/svn/<nome>"
svn commit -m "…"
```

La pagina mostra il comando di checkout con l’indirizzo del repository. Dietro un proxy l’indirizzo segue l’URL
pubblico, e con un nome host SVN è `/svn/<nome>` su quel nome. La **Cronologia** di un repository elenca le sue
revisioni; un file può essere letto a qualsiasi revisione, confrontato con un’altra, e ripristinato come nuova
revisione (**Ripristina questa versione**, per gli account con lettura e scrittura).

### Bucket

- **Salvare è fare commit.** Un client che salva un file crea una revisione con il nome dell’account e l’ora; la
  revisione è marcata come fatta tramite WebDAV. Molti client scrivono prima un file vuoto e poi i dati, quindi un
  salvataggio può creare due revisioni.
- **File di macOS.** Un bucket creato con **Rifiuta i file ._\* e .DS_Store di macOS** (il predefinito) non conserva i
  file `._*` e `.DS_Store` che il Finder scrive accanto a ciò che copia. Il client non riceve alcun errore, ma il file
  non diventa mai una revisione. Eliminare un file del genere è consentito.
- **Tornare indietro.** La cronologia del bucket mostra le versioni di ogni file; **Ripristina questa versione** salva
  il vecchio contenuto come nuova revisione, così non si perde nulla.

### Client WebDAV e i loro limiti

| Client | Come collegarsi | Note |
|---|---|---|
| Finder di macOS | **Vai**, **Connessione al server**, `https://<indirizzo del NAS>:9951/bucket/<nome>` | il Finder blocca un file mentre lo scrive; il bucket risponde ai lock WebDAV |
| rclone | un remote `webdav` con `vendor = other` e l’indirizzo del bucket | adatto a copie tramite script |
| davfs2 (Linux) | `mount -t davfs https://<indirizzo del NAS>:9951/bucket/<nome> /mnt/<cartella>` | il nome utente del pannello e la password o la password per app |
| Cyberduck, WinSCP | una connessione WebDAV (HTTPS) alla porta 9951, percorso `/bucket/<nome>` | |
| curl | `curl -T file -u <utente> https://<indirizzo del NAS>:9951/bucket/<nome>/file` | un caricamento, una revisione (o due, vedi sopra) |
| Esplora file di Windows | **Connetti unità di rete** | il WebClient di Windows invia una password solo via HTTPS con un certificato di cui Windows si fida: metti il bucket dietro un proxy con un certificato vero ([Dietro un reverse proxy](#dietro-un-reverse-proxy-plesk)); il certificato autofirmato non basta |

- I file grandi non vengono tagliati: nginx non imposta alcun limite alla dimensione del corpo su `/bucket/` e passa
  il caricamento man mano che arriva.
- A un file che il bucket rifiuta di conservare (un file di macOS, vedi sopra) il server WebDAV risponde comunque
  `201`, quindi un client non mostra alcun errore; il file non è nel bucket.

### Mostrare un repository o un bucket a persone senza account

Un admin può rendere pubblico un repository SVN o un bucket con l’interruttore nella sua riga della pagina **SVN e
Bucket** (**Sul web**). Chiunque abbia l’indirizzo può allora leggerlo in un browser, senza account:

| Indirizzo | Che cosa mostra |
|---|---|
| `https://<indirizzo del NAS>:9951/svn-web/<nome>/` | un repository SVN |
| `https://<indirizzo del NAS>:9951/bucket-web/<nome>/` | un bucket |

Il collegamento **Vedi sul web** della riga apre quella pagina. Elenca i file e le cartelle alla revisione più recente
o a una più vecchia, mostra un file di testo con i numeri di riga (fino a 1 MiB) o un’immagine, e offre ogni file da
scaricare. La **Cronologia** elenca le revisioni con i loro autori, messaggi e file modificati. La pagina di una
revisione mostra il diff di ogni file che ha modificato (fino a 20 file di al massimo 1 MiB; gli altri sono
collegamenti); qualsiasi file può essere confrontato tra due revisioni.

- **Sola lettura.** Le pagine non possono modificare nulla. I client WebDAV e `svn` richiedono comunque un account,
  anche per un repository pubblico.
- **Solo ciò che è pubblico.** Un repository non pubblico e un nome che non esiste rispondono entrambi “There is no
  public repository at this address”, così le pagine non rivelano quali nomi esistono.
- **Limiti.** Un indirizzo può aprire 60 pagine in una volta e poi 2 al secondo, e scaricare 2 file alla volta; oltre,
  la pagina chiede di riprovare tra un momento. Un diff viene tagliato a 2 MiB.
- **Ai motori di ricerca** viene chiesto di non indicizzare le pagine. Le pagine sono in inglese. Hanno l’aspetto del
  pannello, scuro o chiaro a seconda di come è impostato il browser del visitatore.
- Dietro un proxy o con un nome host SVN, l’indirizzo segue quello del repository stesso (l’URL pubblico, o il nome
  host SVN con gli stessi percorsi).

## Editor e strumenti IA

Il modulo Editor esegue VS Code nel browser (code-server) per ogni account developer e admin. Un admin lo attiva in
**Moduli**. Ogni account riceve una propria istanza, avviata quando l’account apre l’editor e fermata dopo un periodo
senza traffico.

**Il terminale dell’editor è una shell root sul NAS.** Su QTS i processi del pacchetto girano come admin (uid 0), tranne
Forgejo e httpd. Dai il ruolo developer solo a chi daresti la password admin del NAS.

### Aprire l’editor

- **Apri l’editor** nella pagina Impostazioni Editor (o `/ide/` all’indirizzo del pannello, o il nome host
  dell’Editor) apre il workbench in una nuova scheda. La prima richiesta avvia l’istanza; quando non c’è una
  sessione, prima viene l’accesso del pannello.
- Tramite il nome host principale pubblicato, finché l’editor non vi è pubblicato, la pagina Impostazioni Editor, la scheda
  Editor, la Panoramica e **Apri una shell** dicono che l’editor si apre solo dalla rete locale e danno il link al
  suo indirizzo LAN (`https://<indirizzo del NAS>:<porta HTTPS>/ide/`); a un admin danno anche il link
  all’interruttore nella pagina **Pubblicazione**.
- L’editor apre `<dati>/projects`, dove gli esempi in Progetti Editor mettono i loro progetti. Le impostazioni, le
  estensioni e la cronologia del terminale dell’account sono tenute in `<dati>/ide/<account>` e la sua cartella
  home in `<dati>/home/<account>`.
- Il terminale ha gli strumenti del pacchetto per primi nel `PATH`: git, svn, node, npm, rsync, curl, bash e gli
  altri. `bvs env shell <nome>` entra in un ambiente di Toolchain e `eval "$(bvs use <id>)"` prepara una toolchain o
  un runtime. **Apri una shell** nella pagina Toolchain apre l’editor con un terminale già dentro quell’ambiente.
- L’attività **Editor**, e la tavolozza dei comandi, aprono l’editor con una shell in un ambiente o con la cartella
  di un’app. L’editor appare dentro la scheda Editor del pannello; su un telefono si apre in una propria finestra del
  browser. Solo le pagine dell’indirizzo del pannello stesso possono mostrare l’editor al loro interno: il pacchetto
  lo dice ai browser in ogni risposta dell’editor, così un altro sito non può mettere il terminale root dell’editor
  sotto una pagina propria.
- **Tempo di inattività** (predefinito 30 minuti) ferma un’istanza senza traffico del browser, ma mai mentre un
  programma avviato dai suoi terminali, un agente IA avviato da un’estensione (il `claude` di Claude Code for VS Code,
  Codex, Gemini CLI) o qualcosa lasciato in esecuzione da lì è ancora in esecuzione. 0 disattiva l’arresto per
  inattività. Un account può fermare la propria istanza nella pagina Impostazioni Editor; un admin può fermarne
  qualsiasi.
- **Memoria e processi**, nella pagina Impostazioni Editor, mostra cosa esegue un’istanza e quanta memoria occupa
  rispetto a quella del NAS, segna ciò che la tiene attiva, termina un processo con **Termina** e dice perché è
  arrivato l’ultimo arresto e cosa ha terminato. Anche un agente IA in attesa al prompt tiene attiva l’istanza:
  terminalo lì quando non serve più.
- La vista **Ports** di VS Code raggiunge un server di sviluppo sul NAS su `/ide/proxy/<port>/`; le porte loopback
  proprie del pacchetto lì sono rifiutate.
- **New recipe**, nel menu della cartella `recipes` di un progetto, o di una cartella di ricetta al suo interno, in
  **Explorer**, chiede un nome e vi scrive una nuova ricetta: `recipe.json` e `build.sh` dalla ricetta iniziale del
  pacchetto. Nella tavolozza dei comandi è **Bucket: New recipe**, che funziona mentre è aperto un file di un progetto
  con una cartella `recipes`. A ogni salvataggio di un file di una ricetta, l’editor controlla le ricette del
  progetto con `bvs recipe lint` ed elenca i risultati nella vista **Problems**. Avvisa dello sha256 della ricetta
  iniziale finché non inserisci quello vero della tua sorgente.

L’editor risponde solo alle proprie pagine. Un WebSocket, un modulo o la richiesta di uno script che un’altra pagina
invia con la sessione dell’account, da un altro sito o da un’altra porta del NAS, riceve 403 e non avvia alcuna
istanza; così un’altra pagina non raggiunge nemmeno un server di sviluppo dietro la vista **Ports**.

### Estensioni

- Le estensioni si installano nella vista Extensions dell’editor (Ctrl+Maiusc+X). Vengono da Open VSX: il browser cerca
  su open-vsx.org e mostra la pagina e l’icona di ogni estensione da openvsx.eclipsecontent.org, e il NAS scarica
  l’estensione stessa da open-vsx.org. Per questo sia il browser sia il NAS devono raggiungere internet.
- La prima estensione di ogni editore chiede «Do you trust the publisher …?». **Trust Publisher & Install** la
  installa; fino ad allora l’estensione mostra «Installing».
- Ogni account tiene le sue estensioni in `<dati>/ide/<account>/extensions`, e un aggiornamento del pacchetto le
  conserva.
- Il pacchetto porta un insieme di estensioni e formattatori, che l’editor installa per ogni account al suo primo avvio,
  e di nuovo dopo un aggiornamento del pacchetto che porta un nuovo insieme. Con esso l’editor evidenzia e formatta
  (**Format Document** nella tavolozza dei comandi) Markdown, script di shell, C, C++, Rust, Go, Pascal, Python, Perl,
  PHP, JSON, YAML, TOML, configurazione nginx, CSS, HTML, JavaScript e TypeScript. ShellCheck segnala i problemi negli
  script di shell, i file di unità systemd hanno evidenziazione e completamento, il `.editorconfig` di un progetto
  stabilisce il rientro, i codici colore come `#ff8800` o `rgb(0, 128, 0)` appaiono nel loro colore in ogni file, e
  **Hex Editor: Open Active File in Hex Editor** nella tavolozza dei comandi mostra i byte di un file.
- Rust viene formattato quando il runtime Rust è installato nella pagina Toolchain e l’editor dell’account è ripartito;
  fino ad allora Format Document in un file Rust segnala che la richiesta non è riuscita.
- Su un NAS ARM (il pacchetto aarch64) Pascal, Perl e PHP sono evidenziati ma non formattati: il pacchetto non ha per
  esso una build di Free Pascal, Perl o Mago.
- Un’estensione dell’insieme che l’account rimuove nella vista Extensions resta rimossa, anche dopo un aggiornamento
  del pacchetto, e una che aggiorna mantiene la versione più recente. Un formattatore predefinito scelto per un
  linguaggio (**Format Document With…**, poi **Configure Default Formatter…**) resta la scelta dell’account.
- Ogni file `.conf` si apre come configurazione nginx. Il nome del linguaggio nella barra di stato passa un file a un
  altro linguaggio.
- Ruff, che formatta Python, segnala una volta che non è installata un’estensione per gli ambienti Python. Formatta
  anche senza.
- Nel terminale dell’editor, `code-server --install-extension <editore.nome>` installa anch’esso un’estensione da
  Open VSX.
- Alcune estensioni disegnano viste proprie, come il pannello di Claude Code for VS Code, l’editor esadecimale e
  l’anteprima Markdown. Il browser mostra queste viste solo se si fida del certificato del pannello. Con il
  certificato autofirmato accettato solo tramite la pagina di avviso del browser restano vuote, e la scheda Editor e
  la pagina Impostazioni Editor lo dicono. Lo risolve una qualsiasi di queste strade:
  - fidarsi del certificato del NAS sul dispositivo: **Scarica il certificato** nella pagina Rete o in quell’avviso,
    poi aggiungerlo ai certificati attendibili del sistema o del browser ([Il certificato](#il-certificato));
  - un admin aggiunge un certificato per il nome del NAS nella pagina Rete;
  - aprire l’editor tramite il nome host principale pubblicato, quando un admin ve lo pubblica.
- Claude Code for VS Code (editore Anthropic) si installa dalla vista Extensions, e il suo pannello chiede come
  accedere ad Anthropic. **Claude Code: Open in Terminal** nella palette dei comandi avvia Claude Code in un
  terminale, e questo funziona con qualsiasi certificato. Le funzioni di chat proprie di VS Code partono spente
  (`chat.disableAIFeatures`); quell’impostazione non riguarda questa estensione.

### Claude Code, Gemini CLI e Codex CLI

In **Strumenti IA a riga di comando** nella pagina Impostazioni Editor ogni account installa, aggiorna e rimuove
questi strumenti per sé:

| Strumento | Installato con | Comando |
|---|---|---|
| Claude Code | l’installer di Anthropic (`claude.ai/install.sh`), npm se fallisce | `claude` |
| Gemini CLI | `npm install -g @google/gemini-cli` | `gemini` |
| Codex CLI | `npm install -g @openai/codex` | `codex` |

- Finiscono nella cartella home dell’account (`~/.local/bin`, `~/.npm-global/bin`), che è nel `PATH` del terminale; un
  altro account non li vede.
- Ogni installazione è un job con il suo log nella pagina Job. L’accesso a ogni strumento (un account o una chiave
  API) avviene nello strumento stesso, nel terminale dell’editor.

### Programmare con un assistente IA

Ogni progetto creato da **Nuovo progetto** contiene uno skill per gli assistenti IA: `.claude/skills/qnap-dev/` e un
`AGENTS.md` che vi rimanda. Lo skill, in inglese, raccoglie le linee guida per tutto ciò che riguarda un NAS QNAP: cos’è
nella shell (una root su disco RAM, BusyBox, niente systemd, nessun gestore di pacchetti, `getcfg` e `setcfg`),
archiviazione e percorsi, come compilare, firmare e pubblicare con questo pacchetto (`bvs env run`, `bvs ldd`, `bvs
qbuild`, l’App Builder, l’officina del kernel, il repository dei pacchetti), porte, backup e cosa non deve suggerire,
come `apt` sul NAS o consentire app non firmate in App Center. Dice chiaramente che la firma con un token hardware non è
supportata né testata.

- Avvia l’assistente nella cartella del progetto (`cd <dati>/projects/<nome>`), non in `projects/`: ogni strumento
  cerca nella cartella in cui parte.
- Claude Code carica la skill da `.claude/skills/` quando un compito ne ha bisogno. Codex CLI legge `AGENTS.md`.
  Gemini CLI legge `AGENTS.md` una volta che il `.gemini/settings.json` del progetto contiene
  `{"context": {"fileName": ["AGENTS.md", "GEMINI.md"]}}`.
- Il pacchetto tiene la skill attuale in `/usr/bin/bucketvs/share/skills/qnap-dev`. Un progetto creato prima che il
  pacchetto l’avesse, o un progetto copiato dalla galleria Esempi, la riceve copiando quella cartella in
  `.claude/skills/` del progetto.
- La skill cita la knowledge base di QNAP Club Poland (forum.qnap.net.pl) per ciò che il NAS ha e ciò che gli manca.
- La build del pacchetto scrive lo skill anche come `qnap-skill-<versione>.zip`, gli stessi file, da usare fuori
  dall’editor: estrailo in `.claude/skills/` di un progetto o in `~/.claude/skills/`. Il testo dello skill è sotto
  licenza CC BY 4.0 (il suo `LICENSE`): puoi condividerlo e adattarlo con la dicitura "QNAP guidelines skill by QNAP Club Poland
  / qnaputilities.com, CC BY 4.0".

## Toolchain e ambienti

La pagina Toolchain scarica ciò che serve per compilare software sul NAS:

| Tipo | Che cos’è | Esempi |
|---|---|---|
| Filesystem radice | un sistema Linux da decomprimere in un ambiente | Debian 12 e 13, Ubuntu 22.04 e 24.04, Alpine 3.22 |
| Toolchain incrociata | un compilatore che compila per un altro processore | toolchain Bootlin per aarch64, x86-64 e armv7, con glibc o musl |
| Runtime | un linguaggio o uno strumento di build | Python, Go, Node.js, Zig, Rust, CMake, Ninja, Java (JDK) |
| QNAP | gli strumenti propri di QNAP | QDK 2.5.0, per [`bvs qbuild`](#compilare-un-qpkg-con-bvs-qbuild) ed Esporta come QPKG |
| Sorgenti del kernel | un albero del kernel Linux per l’[officina del kernel](#officina-del-kernel) | Linux 6.6.129 da kernel.org; il rilascio GPL del kernel di QNAP per QTS 5.2.3 |

Le voci per un altro processore sono nascoste; “Mostra le voci per altri processori” le elenca, ma non si possono
installare. Le toolchain Bootlin girano su x86_64, quindi un NAS ARM non ha toolchain incrociate.

### Il catalogo

Il pacchetto contiene un catalogo integrato. Il catalogo remoto firmato su
`https://pub.qnaputilities.com/bucketvs/catalog.json` può aggiungere voci senza un nuovo pacchetto. bvsd lo usa solo
quando la sua firma ed25519 corrisponde alla chiave integrata nel pacchetto e non è più vecchio del catalogo in uso.
“Aggiorna il catalogo” lo scarica subito, e il job cron integrato “Aggiornamento del catalogo delle toolchain” lo
scarica ogni giorno alle 04:15.

### Installare e rimuovere

Installare è compito di un admin, mostrato con il suo avanzamento nella pagina Job:

1. L’archivio viene scaricato in `toolchains/cache/` nella cartella dei dati. Un download interrotto riprende quando
   l’installazione viene riavviata.
2. Il suo sha256 viene confrontato con il catalogo. Una differenza elimina il file.
3. Una toolchain incrociata o un runtime viene decompresso in `toolchains/<kind>/<id>` e il suo archivio eliminato. Un
   filesystem radice o delle sorgenti del kernel restano come archivio; ogni ambiente o area di lavoro del kernel ne
   decomprime una propria copia. QNAP pubblica il suo rilascio GPL del kernel in due parti: entrambe vengono
   scaricate, ciascuna verificata con il proprio sha256, e unite.

Un volume pieno fa fallire l’installazione con “no space left on” e il punto di montaggio del volume.

“Rimuovi” elimina una voce installata. Gli ambienti creati da un filesystem radice sono copie e restano.

### Usare una toolchain o un runtime

`bvs use <id>` stampa le righe `export` (come `PATH`) che rendono utilizzabile in una shell una toolchain incrociata o
un runtime installato:

```bash
eval "$(bvs use bootlin-aarch64-glibc-stable)"
```

La pagina Toolchain mostra le stesse righe in “bvs use” per ogni voce installata.

### Ambienti

Un ambiente è un sistema Linux decompresso da un filesystem radice in `envs/<nome>` nella cartella dei dati. È
modificabile: `apt install build-essential` funziona dentro un ambiente Debian o Ubuntu, e ciò che installi resta lì.

- “Nuovo ambiente” (admin) scarica prima il filesystem radice quando non è installato, poi lo decomprime.
- Finché non c’è nessun ambiente, l’elenco propone l’Ubuntu 24.04 builder, quello in cui si compilano gli esempi di
  app, con un solo pulsante (admin): il suo job compare nell’elenco, e l’ambiente appare quando il job è finito.
  Niente viene creato da solo.
- Un nome ha da 1 a 64 caratteri: lettere minuscole, cifre, `.`, `_` o `-`, iniziando con una lettera o una cifra.
- Un modello della galleria Esempi installa anche pacchetti con apt-get prima che l’ambiente compaia: il builder
  Ubuntu 24.04 (il builder predefinito) e il builder Debian 12, entrambi completi, con gli strumenti di build
  di App Builder e patchelf; Clang/LLVM (LLVM 18), Fortran (gfortran 13), Kernel builder, .NET 8 SDK e Kotlin (Kotlin
  1.3.31), tutti su Ubuntu 24.04. La glibc di ogni modello è al massimo 2.39, quindi ciò che vi si compila gira su
  ogni NAS su cui gira il pacchetto ([Su quali modelli di NAS gira un’app](#su-quali-modelli-di-nas-gira-unapp)). Quando apt-get fallisce,
  non resta alcun ambiente. Da una shell:
  `bvs env create <nome> --from <rootfs-id> --package <nome> --package <nome>`.
- “Clona” fa una copia completa con un nuovo nome, per esempio per provare una modifica senza toccare l’originale.
- “Esporta” impacchetta l’ambiente in `toolchains/exports/<nome>-<ora>.tar.zst`.
- “Elimina” lo rimuove. bvsd rifiuta finché una shell o un processo gira al suo interno. I file in `projects/` non
  sono nell’ambiente e restano.

Dentro un ambiente:

- `projects/` della cartella dei dati è visibile come `/projects`, e una shell parte lì;
- `HOME` è `/root` dell’ambiente, e `LANG` è `C.UTF-8`;
- sono montati `/proc`, un `/sys` in sola lettura e un piccolo `/dev` proprio, e vi vengono copiati `resolv.conf` e
  `hosts` del NAS;
- i montaggi appartengono allo spazio dei nomi di montaggio proprio della shell e spariscono quando essa termina,
  quindi sul NAS non resta montato nulla.

Lo stesso da una shell (gli esempi chiamano `bvs` per nome, come possono fare un job cron e una sessione SSH sul NAS
mentre il pacchetto è in esecuzione):

```bash
bvs env list
bvs env create deb12 --from debian-12-x86_64
bvs env shell deb12
bvs env run deb12 -- make -C /projects/hello
```

`bvs env shell` e `bvs env run` partono nella cartella da cui li esegui. Una cartella sotto `projects/` della cartella
dei dati è la stessa cartella sotto `/projects` dentro l’ambiente. Qualsiasi altra cartella (il checkout di un job CI,
una cartella in una condivisione) viene montata su `/work` e il comando parte lì. Da `/` partono in `/projects`.

### Compilare un QPKG con bvs qbuild

`bvs qbuild` compila un QPKG dalla cartella in cui lo esegui (una cartella con `qpkg.cfg`, come quella che crea il
modello QPKG di Nuovo progetto). Esegue il `qbuild` di QNAP dentro un ambiente Debian o Ubuntu. qbuild chiama i
programmi di sistema con i loro percorsi completi, e nell’ambiente quei percorsi portano ai programmi dell’ambiente
invece che a quelli del NAS. Il pacchetto finisce in `build/` della cartella.

```bash
bvs tc install qdk-2.5.0-x86_64      # una volta; qdk-2.5.0-aarch64 su un NAS ARM
cd /share/CACHEDEV1_DATA/BucketVS/projects/MyTool
bvs qbuild                           # --env <nome> sceglie l'ambiente
```

Usa l’ambiente `ubuntu2404-builder` (l’esempio “Ubuntu 24.04 builder”), altrimenti `debian12-builder`, altrimenti un
altro ambiente Debian o Ubuntu, e vi ha bisogno di `rsync`. Quando manca qualcosa, si ferma e indica il comando che lo
aggiunge, per esempio `bvs env run ubuntu2404-builder -- apt-get install -y rsync`. Gli argomenti dopo `bvs qbuild`
vanno a qbuild. Prima che qbuild parta, `bvs qbuild` dice quale [caso di firma del
codice](#firmare-il-codice-di-un-qpkg) vale per la cartella.

Un ambiente ha propri file e strumenti. Tutto ciò che contiene gira come root, vede ogni processo del NAS e usa la
rete del NAS.

### Firmare il codice di un QPKG

App Center controlla la firma del codice di un pacchetto quando lo installa. Per firmare serve un certificato di
firma del codice rilasciato da QNAP. `bvs qbuild` indica uno dei tre casi nella prima riga, per esempio
`bvs: code signing, case 1 (a certificate as files): …`:

| Caso | La cartella contiene | Cosa succede |
|---|---|---|
| 1, un certificato come file | `certificate` e `private_key`, facoltativamente `ca_certs` (la catena) | qbuild firma il pacchetto con `openssl`; l’ambiente ha bisogno di `openssl` e `bsdextrautils` |
| 2, un certificato su un token hardware | `certificate_hsm` | non supportato e non testato: `bvs qbuild` si ferma prima di compilare qualsiasi cosa |
| 3, nessun certificato | nessuno di questi file | il pacchetto viene compilato senza firma |

- **Caso 1.** Metti i tre file accanto a `qpkg.cfg`. Se all’ambiente manca un programma, `bvs qbuild` indica il
  comando, per esempio `bvs env run ubuntu2404-builder -- apt-get install -y openssl bsdextrautils`. Rifiuta una
  cartella con uno solo dei file `certificate` e `private_key` (qbuild avviserebbe e compilerebbe un pacchetto non
  firmato) e una in cui uno dei due è vuoto. Tieni questi file fuori da git: il `.gitignore` del modello QPKG li
  elenca. In CI conservali come secret del repository e scrivili nella cartella solo per la build.
- **Caso 2.** qbuild leggerebbe la posizione della chiave sul token da variabili che dentro l’ambiente non gli
  arrivano, e il pacchetto non è stato testato con un token. Compila senza `certificate_hsm` e firma il pacchetto
  sulla macchina che ha il token con gli strumenti di QDK; lo skill QNAP per gli assistenti IA
  (`references/signing.md`) descrive la via di QDK, che questo pacchetto non ha mai eseguito.
- **Caso 3.** Su un’unità con QTS 6.1, `qpkg_cli -m` di un pacchetto non firmato non ha installato nulla, mentre
  `sh <file>.qpkg` in una cartella condivisa lo ha installato e avviato. Il [repository dei pacchetti](#repository-dei-pacchetti) lo
  accetta solo finché un admin consente i pacchetti non firmati, e App Center su un altro NAS lo installa solo se il
  suo proprietario consente app senza una firma digitale valida, cosa che questo pacchetto non consiglia mai.
  **Esporta come QPKG** dà sempre questo caso.
- `qbuild --sign` aggiunge una firma GnuPG, che non è la firma del codice letta da App Center e dal repository.

## App Builder

App Builder compila i programmi scelti in un’unica cartella, il prefisso dell’app (per impostazione predefinita
`/usr/app/my_app_prefix`), dentro un ambiente.

La scheda **Esempi** di **App** contiene app pronte, ambienti, workflow CI, job cron e Progetti Editor. **Vedi il
codice** su una scheda mostra cosa crea l’esempio prima di usarlo, per ogni ruolo: i file di un progetto Editor, il file
del workflow, il comando del job cron o, per un’app, le sue ricette in ordine di build con `build.sh` e
`recipe.json` di ciascuna. Il visualizzatore è di sola lettura e mostra i file di testo; un ambiente non ha file,
quindi la sua scheda non ha questo pulsante. Usare un esempio richiede il ruolo developer, e un ambiente il ruolo
admin: la scheda di un ambiente non offre **Usa questo esempio** a un developer.

### Ricette

Ogni programma viene compilato da una ricetta. Una ricetta indica la sua sorgente con uno sha256, le ricette da cui
dipende, gli strumenti di build di cui l’ambiente ha bisogno, e una verifica che dimostra che il risultato
funziona (come `bin/bash --version`). Ci sono ricette per librerie (zlib, openssl, ncurses, readline e altre), shell
(bash, zsh, busybox), strumenti (coreutils, curl, git, jq, rsync, tmux, htop, nano e altri), server (nginx, redis,
openssh, dropbear) e un linguaggio (lua).

- Una ricetta viene compilata solo dopo le ricette da cui dipende.
- Una nuova build riusa il risultato di una ricetta finché i suoi input non cambiano (la sorgente, la ricetta, le
  sue dipendenze, il compilatore e la libreria C dell’ambiente, il prefisso, e se il link è dinamico o statico) e ogni
  file che ha installato è ancora nel prefisso.
- La modalità di collegamento decide come i programmi trovano le loro librerie. **dinamico** dà a ogni programma un
  RUNPATH `<prefix>/lib`. **relocatable** lo scrive relativo alla cartella del programma stesso (`$ORIGIN/../lib`),
  così l’albero trova le sue librerie in qualsiasi cartella; i percorsi compilati dentro i programmi, come la cartella
  terminfo, nominano ancora il prefisso. **bundle** è relocatable e copia inoltre in `lib` le librerie che i programmi
  caricano dall’ambiente di build (come libstdc++), così l’app ha bisogno solo della libreria C del NAS.
  **statico** collega staticamente le ricette che lo supportano. Passare tra dinamico, relocatable e bundle non
  ricompila nulla.
- Un’app statica rifiuta le ricette che non si possono collegare staticamente (openssh, dropbear e tmux), nominando
  ciascuna con il motivo, a meno che l’app non sia impostata per compilarle come bundle. La pagina Nuova app e
  **Cambia** offrono tutte e quattro le modalità e, per un’app statica, quella scelta.
- Un programma statico che cerca utenti, gruppi o nomi host li legge solo da `/etc/passwd`, `/etc/group`, `/etc/hosts`
  e dal DNS del NAS stesso, qualunque cosa indichi `/etc/nsswitch.conf`, quindi gli account di un dominio o di LDAP
  gli sono sconosciuti. Compila questi programmi dinamici quando quegli account contano. Il pannello avvisa quando
  un’app statica ne include uno.
- Dopo l’ultima ricetta, la verifica di ogni ricetta viene eseguita di nuovo sul prefisso finito.
- I file di configurazione sotto `etc/` del prefisso sopravvivono a una nuova build: un file modificato resta, e il
  nuovo predefinito va accanto come `<file>.bvs-new`.

### Aggiornamenti delle ricette

Le ricette nuove e aggiornate arrivano anche come catalogo firmato da pub.qnaputilities.com, così raggiungono il NAS
prima della prossima versione di Bucket & Code Server IDE. bvsd cerca un catalogo più recente una volta al giorno. Lo usa solo se la
firma corrisponde alla chiave integrata in bvsd, ogni file corrisponde all’elenco del catalogo e le sue ricette sono
più recenti di quelle arrivate con il pacchetto; un catalogo che non supera un controllo non viene mai usato. Finché
un catalogo è in uso, Nuova app segna le ricette che porta come **nuova nel catalogo** o **aggiornata dal
catalogo**.

La scheda **Libreria di ricette** nella pagina App mostra quale libreria usano le build (**Libreria del pacchetto**
con la versione del pacchetto, o **Catalogo** con la versione del catalogo) e l’ultimo controllo con il suo esito. Un
admin vi trova due pulsanti; il secondo compare finché un catalogo è in uso:

- **Cerca aggiornamenti delle ricette** controlla subito, senza aspettare il controllo giornaliero.
- **Usa la libreria del pacchetto** torna alle ricette arrivate con Bucket & Code Server IDE. Il catalogo resta messo da parte finché
  non ne arriva uno più recente; allora le build usano quello più recente.

Dopo un cambio, una nuova build riusa ogni ricetta i cui file non sono cambiati. Prima di ogni build bvsd controlla di
nuovo i file del catalogo. Se uno è cambiato sul NAS, la build si ferma prima che parta qualcosa, e le build usano il
catalogo precedente o la libreria del pacchetto.

### Compilare un’app

**App**, **Nuova app** (ruolo developer) chiede un nome, il prefisso (`/usr/app/<nome>` a meno che tu non ne scelga un
altro), l’ambiente in cui l’app si compila (un ambiente Debian o Ubuntu; la galleria Esempi crea l’“Ubuntu 24.04
builder”, che la pagina sceglie per primo), la destinazione (l’architettura di questo NAS, o aarch64 o armv7 come
build incrociata), i modelli di NAS su cui gira, la modalità di collegamento e le ricette. Le ricette da cui
dipendono vengono aggiunte nell’ordine di build.

Un’app può anche prendere le ricette di un progetto che ha una propria cartella `recipes/`. Scegli il progetto in
**Ricette da un progetto**, al passo **Pacchetti**. Le sue ricette sono le prime dell’elenco, sotto il nome del
progetto e segnate **progetto**, e dopo vengono quelle della libreria. Una ricetta del progetto con il nome di una
ricetta della libreria la sostituisce in questa app e mostra quale versione della libreria sostituisce. Una ricetta
che il progetto esclude è indicata con il motivo. Una ricetta scelta che manca nell’elenco del progetto è indicata
sotto la selezione ed esclusa dall’app; torna quando scegli un elenco che la contiene. La pagina dell’app indica il
progetto, e da quel nome un developer apre la vista Progetto. Un viewer vede le ricette dell’ultima build dell’app
nell’ordine in cui le nomina il suo log, ognuna con il suo stato e il suo log; prima della prima build non c’è un
ordine da mostrare.

Ogni modalità di collegamento è una scheda, con un’immagine di dove i programmi trovano le loro librerie, quanto costa
la modalità in dimensione, in aggiornamenti e in dove gira l’app, e quali ricette la supportano. Prima che tu scelga
le ricette le schede contano l’intera libreria di ricette; quando torni al primo passo contano le ricette della tua
build. Per un’app statica scegli che cosa succede alle ricette che non si possono collegare staticamente:
compilarle come bundle, o lasciarle fuori.

- **Compila** (o **Ricompila**) esegue un job: scarica ogni sorgente e ne verifica lo sha256, installa nell’ambiente
  gli strumenti di build di cui le ricette hanno bisogno, compila in una copia della build precedente
  dell’app, e verifica ogni programma.
- Una build che fallisce, viene annullata o viene interrotta lascia l’app com’era: attivazione, download ed
  esportazione continuano a usare la build più recente che è riuscita.
- Gira una sola build di un’app alla volta, e una build attende mentre girano programmi dell’app attiva
  (fermali prima).
- La pagina dell’app mostra l’ordine di build, lo stato e il log di ogni ricetta, i file sotto il prefisso, e
  l’archivio della build riuscita più recente (**Scarica**, un `.tar.zst`).
- **Cambia** nella pagina dell’app imposta un altro ambiente, i modelli di NAS su cui gira, un’altra modalità di
  collegamento, il progetto da cui prende le ricette o altre ricette; la build successiva lo applica.
- **Dipendenze**, una scheda della pagina dell’app, mostra ciò di cui ogni file ELF della build più recente ha
  bisogno e dove il loader del NAS lo trova, letto senza eseguire nulla. Una libreria mancante, una versione che la
  libreria non definisce, o un RUNPATH che punta fuori dall’app è elencato come rilievo con la sua soluzione. Un
  developer esegue una soluzione con il suo pulsante: **Aggiungi … e compila**, **Passa a … e compila**,
  **Ricompila**, **Correggi il RUNPATH**, **Imposta l’interprete**, **Scegli un altro ambiente** o
  **Compila solo per questo NAS**. Una soluzione senza
  pulsante (installare un pacchetto in un ambiente, compilare per un’altra macchina) è una frase da seguire. Sotto i
  rilievi, ogni file e ciò di cui ha bisogno sono disegnati come grafo (i programmi, le librerie dell’app, quelle del
  NAS e quelle mancanti in colonne; una linea tratteggiata è una versione che manca alla libreria, una linea
  punteggiata una libreria non trovata da nessuna parte) o come albero; scegli un file per vederne i dettagli. Sul
  NAS, `bvs deps <app>` stampa la stessa cosa.
- **Ispettore ELF** (Toolchain, ruolo developer) verifica un file: dalla build di un’app, dalla cartella dei
  progetti o da un ambiente, e per un admin qualsiasi file sul NAS, come girerebbe su questo NAS o dentro un ambiente.
  `bvs ldd <file>` fa lo stesso sul NAS (`--env <name>` per un ambiente).
- **Ogni build** esegue la stessa verifica sul suo risultato prima di sostituire l’app, e fallisce su un
  problema che ferma un programma su questo NAS, o su un programma che richiede una glibc più recente di quella che
  l’app consente, con il problema e la sua soluzione nel messaggio del job.
- **Strumento patch** (la scheda Dipendenze) imposta il RUNPATH (il `lib` dell’app, la forma `$ORIGIN`, o una cartella
  dell’app) o l’interprete del programma di file scelti della build più recente, con il patchelf dell’ambiente,
  su una copia che sostituisce la build solo quando riesce. **Anteprima** mostra ogni file prima e dopo e quali
  librerie il nuovo RUNPATH non trova nell’app; **Applica la patch** la esegue. La build successiva riscrive i
  RUNPATH nella modalità di collegamento dell’app.

Una build di Qt esegue gli strumenti di Qt stesso (moc, rcc e, per le traduzioni, lrelease di Linguist). In una
build incrociata le ricette di Qt (`qtbase`, `qttools`) compilano quindi prima questi strumenti per questo NAS, in
`host/` sotto il prefisso, e poi Qt per la destinazione; le ricette compilate su Qt eseguono gli strumenti da lì.
Niente sulla destinazione esegue `host/`. Fa parte della build dell’app, quindi anche l’archivio ed
**Esporta come QPKG** lo contengono.

### Le tue ricette

Quando la libreria di ricette non ha una ricetta per un programma, un progetto può averne una sua. Le ricette vanno
nella cartella `recipes/` del progetto, nel formato della libreria, e le compilano solo le app che nominano il progetto.
Un pacchetto si fa così in cinque passi:

1. **Crea il progetto.** In [Nuovo progetto](#nuovo-progetto), il modello **Package from source** (pacchetto dai
   sorgenti) crea una cartella con una ricetta iniziale che porta il nome del progetto, un Makefile per due app (una
   per il processore di questo NAS, una per aarch64, con una compilazione incrociata su un NAS x86_64) e lo script di
   avvio e arresto del pacchetto dal passo **Servizio**.
2. **Scrivi la ricetta.** **New recipe** nell’editor aggiunge un’altra ricetta dalla stessa ricetta iniziale
   ([Aprire l’editor](#aprire-leditor)). Metti in `recipe.json` il vero indirizzo del sorgente del programma e il suo
   sha256 (`curl -fsSL <indirizzo> | sha256sum` nel terminale dell’editor stampa la somma), e i passi di compilazione
   in `build.sh`. La cartella di una ricetta contiene `recipe.json`, `build.sh` e il file vendor che `recipe.json` nomina,
   nient’altro, quindi una patch va in `build.sh` come here-document.
3. **Controllala.** Ogni salvataggio di un file di una ricetta controlla le ricette del progetto ed elenca i risultati
   nella vista **Problems**; `make lint` nel terminale e la scheda **Ricette** della [vista Progetto](#la-vista-progetto)
   danno gli stessi risultati. Il controllo legge `recipe.json` e analizza `build.sh` con `bash -n`. Non esegue mai la
   ricetta.
4. **Compila.** **Compila** nella vista Progetto crea le due app quando mancano e le compila una dopo l’altra. Una build
   legge le ricette del progetto una volta, all’avvio, quindi un file salvato dopo aspetta la build successiva. Una
   ricetta del progetto con il nome di una ricetta della libreria la sostituisce solo nelle app di questo progetto, e
   il log della build lo dice.
5. **Impacchetta e installa.** `make qpkg` nel terminale dell’editor mette l’ultima build riuscita di ogni app nel
   pacchetto, con il QDK delle Toolchain, e tralascia un’app che non ne ha. Con i file di firma del codice in `qpkg/`
   accanto a `qpkg.cfg`, o in una cartella indicata da `QPKG_CERT_DIR`, il pacchetto è firmato ([Firmare il codice di
   un QPKG](#firmare-il-codice-di-un-qpkg)), e **Deploy di prova** nella vista Progetto (admin) lo installa su questo
   NAS.

Alcune regole valgono per le ricette di un progetto:

- Una ricetta che il progetto esclude (un file in più nella sua cartella, un link, un file oltre il suo limite di
  dimensione) è indicata con il motivo nella scheda Ricette e dal controllo, e una build che ne ha bisogno viene
  rifiutata.
- Quando la cartella del progetto viene rinominata o eliminata, le sue app non si compilano: la build risponde “Non esiste
  un progetto ‘…’” finché **Cambia** nella pagina dell’app non imposta il nuovo nome del progetto o **Nessuno: solo la
  libreria**.
- Le ricette di un progetto girano come root nell’ambiente di compilazione, come quelle della libreria. Ogni developer
  può scriverle e compilarle, come ogni developer può eseguire il Makefile di un progetto con Compila. Un viewer vede i
  nomi delle ricette di un’app nel log della sua build, mai i file delle ricette.
- Una ricetta di progetto resta nel suo progetto. Entra nella libreria di ricette, e nel catalogo firmato che ogni NAS
  riceve, solo con una modifica revisionata di Bucket & Code Server IDE stesso.
- Una ricetta la cui build ha bisogno di pacchetti npm, Go o Cargo li elenca in un file vendor. Questo elenco lo
  scrivono `npm-vendor.py`, `go-vendor.py` e `cargo-vendor.py` in `/usr/bin/bucketvs/share/bucketvs/tools/`; hanno
  bisogno di un runtime Python dalle [Toolchain](#toolchain-e-ambienti).

### Su quali modelli di NAS gira un’app

Un programma compilato con la glibc parte solo su un NAS la cui glibc è almeno recente quanto la versione più recente
che il programma chiede. Il pacchetto conta su una glibc 2.39 o più recente su ogni NAS su cui gira (QTS 6.0, QuTS
hero 6.0 e successivi): è la sua soglia minima di glibc. 2.39 è la glibc di Ubuntu 24.04 e di QuTS hero 6.0.1, letta
dal `/lib/libc.so.6` di quel firmware; QNAP non pubblica alcuna versione di glibc, e le sue note di rilascio non elencano alcun QTS 6.0. I NAS di prova con QTS 6.1.0 e QuTS hero 6.1.0 hanno 2.43.

**Gira su** nella nuova app e in **Cambia** sceglie uno dei due:

- **Ogni NAS su cui gira il pacchetto** (predefinito): nessun programma può richiedere una glibc sopra 2.39. Compila
  un’app del genere in un ambiente con glibc 2.39 o precedente, come l’Ubuntu 24.04 builder (2.39) o il Debian 12
  builder (2.36). È la scelta per **Esporta come QPKG** e il repository dei pacchetti.
- **Solo questo NAS**: i programmi possono richiedere la glibc di questo NAS, per un software che ne richiede una più
  recente. Un’app del genere si compila per l’architettura di questo NAS e non viene mai esportata come QPKG.

Una compilazione incrociata (aarch64 o armv7) è sempre per ogni NAS. La nuova app avvisa quando la glibc
dell’ambiente scelto supera quanto l’app consente. Dopo ogni compilazione, bvsd legge la versione di glibc che chiede
ogni programma e ogni libreria, anche in una compilazione incrociata. Un file sopra il limite fa fallire la
compilazione, e le soluzioni sono indicate: un altro ambiente (**Scegli un altro ambiente**), la modalità di
collegamento static o **Compila solo per questo NAS**. La pagina dell’app dice quale glibc richiede la sua
compilazione riuscita più recente, e la scheda **Dipendenze** elenca ogni file sopra il limite.

Sul NAS, `bvs tree-deps <prefisso> --tree <cartella> --glibc-max 2.39` esegue la stessa verifica su qualsiasi albero
di un’app.

### Attivare un’app sul NAS

**Attiva** (admin) rende il prefisso un collegamento alla build riuscita più recente dell’app, così i suoi
programmi girano sul NAS da `/usr/app/<nome>/bin`. L’attivazione non sostituisce mai nulla che non abbia creato: una
cartella, un file o un altro collegamento al prefisso viene rifiutato con il motivo. Il disco di sistema di QTS viene
ricostruito a ogni avvio, quindi il pacchetto ricrea i collegamenti a ogni suo avvio. **Disattiva** (admin) rimuove il
collegamento; eliminare l’app prima la disattiva e poi la sposta nel cestino per 7 giorni. Un developer può eliminare
un’app solo quando non è attiva. Un’app compilata per un’altra architettura non può essere attivata qui; scarica il
suo archivio per un NAS di quel tipo.

### Esportare un’app

**Esporta come QPKG** (admin) impacchetta la build riuscita più recente come un QPKG a sé, che installa l’app
su un altro NAS senza questo pacchetto. Rifiuta un’app compilata solo per questo NAS, e si ferma prima di
impacchettare quando un programma della build richiede una glibc sopra 2.39 (vedi
[Su quali modelli di NAS gira un’app](#su-quali-modelli-di-nas-gira-unapp)). Richiede QDK: installa “QNAP QDK
2.5.0” nella pagina Toolchain, scheda QNAP.
Finché QDK non è installato il pulsante non compare e la pagina dell’app dice dove ottenerlo. L’esportazione esegue
qbuild nell’ambiente dell’app, allo stesso modo di [`bvs qbuild`](#compilare-un-qpkg-con-bvs-qbuild). Ogni
build vi installa `rsync`; per un’app compilata l’ultima volta con una versione più vecchia del pacchetto, il
rifiuto indica il comando che lo aggiunge. Il pacchetto esportato crea il collegamento del suo prefisso all’avvio e lo
rimuove all’arresto. Non è firmato: il log del job indica il [caso 3](#firmare-il-codice-di-un-qpkg), come una riga
sotto il pulsante. In una sessione SSH, `/etc/init.d/<nome>.sh disable` ed `enable` impostano l’interruttore del
pacchetto esportato in App Center e lasciano com’è il collegamento del prefisso. A ogni avvio il pacchetto esportato
aggiunge anche una riga a `/etc/profile` e a `/root/.profile`, che mette `bin` e `sbin` dell’app nel `PATH` di un
accesso SSH, dopo i comandi propri del NAS, per admin e per ogni altro account che può accedere; la rimozione toglie di
nuovo la riga.

## Repository dei pacchetti

Il pacchetto gestisce un repository di App Center: i pacchetti QPKG pubblicati lì si possono installare su altri NAS
da App Center, come le app di qualsiasi repository.

- **L’indirizzo.** `bvs repo list` lo stampa (e `GET /api/v1/repo`): `<base>/repo/repo.xml`, dove `<base>` è l’host
  dei pacchetti pubblicato (altrimenti il nome host principale pubblicato) finché il pacchetto è pubblicato su internet
  ([Pubblicare su internet](#pubblicare-su-internet-cloudflare-tunnel)), altrimenti l’URL pubblico quando è impostato,
  altrimenti `http://<indirizzo del NAS>:9950` (`https://…:9951` quando HTTP semplice si limita a reindirizzare).
  Sull’altro NAS: App Center, **Impostazioni**, **Repository di app**, **Aggiungi**, e quell’indirizzo.
- **Pubblicazione.** `bvs repo publish <file.qpkg>` (sul NAS, come root) o `POST /api/v1/repo/packages` con il file
  come corpo (ruolo developer). L’architettura viene dal nome del file QDK `<name>_<version>_<arch>.qpkg` (`x86_64`,
  `arm_64`, `arm-x41`), o da `--arch`. Nome, versione, descrizione e autore vengono dal pacchetto stesso;
  `PUT /api/v1/repo/packages/<name>/meta` e `…/icon` (un PNG, admin) cambiano ciò che App Center mostra. Un pacchetto
  non può chiamarsi `default`: è il nome dell’icona propria del repository.
- **Solo pacchetti firmati.** Un pacchetto senza firma del codice viene rifiutato, perché App Center installa questi
  pacchetti solo su un NAS impostato per consentirli. Il repository considera firmato un pacchetto quando il suo
  blocco di firma del codice è una firma PKCS#7 con un certificato e un firmatario. Non verifica quella firma né i
  suoi certificati rispetto a quelli di QNAP; lo fa App Center quando installa il pacchetto. Un admin può attivare
  `repo.accept_unsigned` nelle impostazioni per una rete in cui questo è noto; quei pacchetti sono marcati nella loro
  descrizione. I pacchetti che App Builder esporta non sono firmati, quindi hanno bisogno di questa impostazione.
- **Versioni.** Ogni versione resta scaricabile, ma App Center vede una versione per pacchetto, quella che un admin
  elenca. La prima versione di un pacchetto pubblicata da un admin viene elencata subito; una successiva quando
  l’admin la sposta (`POST /api/v1/repo/packages/<name>/latest`, o `bvs repo publish --latest`). La versione di un
  developer, compresa la prima di un nuovo pacchetto, non è elencata da nessuna parte finché un admin non rende una
  versione la più recente: ogni NAS che ha aggiunto il repository installa la versione elencata come root. Eliminare
  una versione (admin) ne rimuove tutte le architetture; quando era quella elencata, nessuna versione è elencata
  finché un admin non ne sceglie un’altra.
- **Caricamenti.** Due caricamenti vengono copiati alla volta; il repository rifiuta un caricamento che lascerebbe
  meno di 256 MiB liberi sul volume dati. Il repository non fa parte di un backup: dopo un ripristino, pubblica di
  nuovo i pacchetti.
- **Nel pannello.** La pagina **Pacchetti** mostra l’indirizzo da copiare, i pacchetti con le loro versioni e se sono
  firmati; lì un developer carica un QPKG, e un admin elenca una versione come la più recente ed elimina una
  versione. La sua scheda **Firma del codice** mostra i [tre casi](#firmare-il-codice-di-un-qpkg), quante versioni
  elencate non hanno firma, e l’interruttore dell’admin per i pacchetti non firmati, che chiede conferma prima di
  attivarsi. La pagina di un’app mostra la sua pipeline (build, esportazione come QPKG, pubblicazione) con
  un pulsante **Pubblica**.

## Officina del kernel

L’officina del kernel compila moduli per il kernel che il NAS esegue, verifica ciascuno rispetto a quel kernel e lo
carica. QNAP non ha pubblicato i sorgenti dei suoi kernel attuali (i suoi rilasci GPL si fermano a QTS 5.2.3), quindi
l’officina legge ciò che il kernel in esecuzione mostra di sé e configura un albero del kernel che gli corrisponda.

### Che cosa serve

- L’ambiente Kernel builder dalla galleria Esempi (Ubuntu 24.04 con GCC 13 e gli strumenti di build del
  kernel). Un modulo deve essere compilato con la stessa versione principale di GCC del kernel; l’officina le
  confronta.
- Il ruolo admin, e root in un terminale: i comandi `bvs kernel` usano il socket CLI di bvsd o caricano moduli.

### Il profilo

`bvs kernel profile` mostra che cos’è il kernel in esecuzione: la sua release (`6.6.129-qnap`), il compilatore con cui
QNAP l’ha compilato, il vermagic dei moduli propri di QNAP, la dimensione di `struct module` in essi e dove si trovano
init e exit, se i moduli devono essere firmati, e le impostazioni che un albero del kernel riceve per corrispondergli.

### L’area di lavoro

`bvs kernel prepare` crea l’area di lavoro del kernel in esecuzione in `kernels/<release>` della cartella dei dati:

1. Prende i sorgenti del kernel che il catalogo ha per questa versione (`bvs kernel sources`): l’albero GPL proprio di
   QNAP quando QNAP ne ha pubblicato uno, altrimenti l’albero di kernel.org, a cui mancano le modifiche proprie di
   QNAP. `--source` ne sceglie un altro.
2. Configura l’albero: con la configurazione di QNAP per questo modello quando l’albero è di QNAP, altrimenti con le
   impostazioni del profilo. `--option NAME=m` aggiunge un’impostazione, per esempio un driver.
3. Prepara l’albero per la build di moduli (`--full` compila invece l’intero kernel, il che richiede molto più
   tempo e permette alla verifica di vedere i simboli esportati del kernel).
4. Verifica che l’albero compili esattamente per la release del kernel in esecuzione.

Gira come job nella pagina Job. Un prepare non riuscito conserva l’albero del precedente.

### Compilare un modulo

- Il tuo modulo è una cartella in `projects/` con un file `Kbuild`. `bvs kernel make` in quella cartella (o
  `bvs kernel make --project <folder>`) lo compila. L’esempio “Kernel module” è un progetto di questo tipo.
- Un driver del kernel che QNAP non fornisce si compila dalla sua directory dell’albero del kernel:
  `bvs kernel make --dir drivers/usb/serial`. L’esempio “USB serial driver ch341” compila così il driver ch341; la sua
  impostazione viene prima con `bvs kernel prepare --option USB_SERIAL=m --option USB_SERIAL_CH341=m`.

I moduli prodotti da una build sono conservati in `kernels/<release>/modules/`. Un modulo che QNAP fornisce per
questo kernel non viene conservato: la copia propria di QNAP resta quella che si carica.
`bvs kernel make --build-only` non vi conserva nulla: il modulo resta dove è stato compilato. I workflow CI dei
modelli del kernel di Nuovo progetto compilano così.

### La verifica e il caricamento

`bvs kernel check <module>` confronta un modulo con il kernel in esecuzione: il vermagic, la dimensione di
`struct module` e dove si trovano init e exit al suo interno (rispetto ai moduli propri di QNAP), se è richiesta una
firma, i moduli da cui dipende, e ogni simbolo di cui ha bisogno. Ogni rilievo dice che cosa non corrisponde e che
cosa fare. Lo stato di uscita 3 significa che il modulo non deve essere caricato.

`bvs kernel load <module>` esegue di nuovo la verifica e carica il modulo, prima i moduli da cui dipende (quelli di
QNAP dal NAS, o i tuoi). Un modulo con un errore nella verifica non viene mai caricato. `bvs kernel unload <module>`
lo rimuove; un modulo in uso resta caricato. Ogni caricamento e scaricamento viene scritto in
`log/kernel-modules.log`.

Il kernel di QNAP non elenca i suoi simboli di dati, quindi la verifica non può cercare simboli come `jiffies`; li
nomina come avvisi. Se uno di essi manca, il kernel rifiuta il modulo al caricamento.

### Moduli in un’app

Un’app può nominare moduli dell’area di lavoro. Attivare l’app li carica prima che venga creato il suo collegamento, e
ogni avvio del pacchetto li carica di nuovo una volta che il pannello è attivo; la pagina Kernel mostra un caricamento
ancora in corso. Disattivarla li scarica prima; un modulo in uso tiene l’app attiva.

- Se il NAS si è riavviato mentre i moduli venivano caricati, l’avvio successivo non ne carica nessuno e mostra una
  notifica: caricali a mano o attiva di nuovo l’app.
- Se il NAS si è fermato senza l’arresto ordinato del pacchetto (un crash, un’interruzione di corrente o un reset
  forzato) in un avvio in cui il pacchetto aveva caricato moduli, l’avvio successivo non carica quei moduli, perché
  uno di essi può aver causato il crash. La notifica e la pagina Kernel li nominano. Caricarne uno a mano, o attivare
  di nuovo la sua app, permette all’avvio di caricarlo di nuovo. Uno spegnimento o un riavvio normale del NAS ferma il
  pacchetto in ordine e non cambia nulla.
- Un modulo il cui avvio non finisce mai (un driver che aspetta il suo hardware) lascia il pannello funzionante. Una
  notifica lo nomina dopo un minuto; gli altri caricamenti e scaricamenti lo attendono, e un riavvio del NAS lo
  termina.

Dopo un aggiornamento di QTS a un altro kernel, i moduli vanno ricompilati nell’area di lavoro del nuovo kernel. Lo
stesso vale dopo un ripristino su un’altra cartella dei dati: `kernels/` non fa parte di un backup.

### Nel pannello

La pagina **Kernel** mostra il profilo del kernel in esecuzione, i sorgenti del kernel del catalogo, l’area di lavoro
e i suoi moduli. Lì un admin prepara l’area di lavoro (una sorgente, l’ambiente, impostazioni aggiuntive, l’intero
kernel o no) e compila moduli da una cartella di progetto o da una directory dell’albero; entrambe le cose girano come
job. **Verifica** (developer) mostra ogni rilievo con la sua soluzione; **Carica** e **Scarica dal kernel** (admin)
fanno ciò che fanno `bvs kernel load` e `unload`, e un caricamento rifiutato mostra la verifica che lo ha fermato. Un
file `.ko` in qualsiasi punto sotto la cartella dei dati può essere verificato indicandone il percorso.

## Job cron e operazioni

La pagina Job ha due elenchi: i job pianificati (cron) e le operazioni (i job lunghi che bvsd esegue, come
installazioni e spostamenti).

### Job pianificati

Un developer crea e modifica i propri job; un admin li modifica tutti. Un job ha:

- Pianificazione: cinque campi cron (`0 3 * * *`), sei con i secondi per primi, o un descrittore (`@daily`, `@hourly`,
  `@every 6h`). Un prefisso `CRON_TZ=<zone>` sceglie un altro fuso orario; senza, il job gira nel fuso orario del NAS,
  mostrato in cima alla pagina.
- Comando: eseguito come root dal `bash -lc` del pacchetto. Più righe girano come un unico script, e il suo output
  diventa il log dell’esecuzione.
- Dove gira: sul NAS, o dentro un ambiente (con il `bash` dell’ambiente, o `/bin/sh` dove non ce l’ha).
- Cartella di lavoro: relativa a `projects/` nella cartella dei dati, o un percorso completo. Dentro un ambiente è
  visibile solo `projects/`, come `/projects`.
- Timeout: in secondi (0: nessuno). Un’esecuzione ancora in corso dopo di esso viene fermata e marcata come scaduta.
- Mentre un’esecuzione è in corso: la successiva viene saltata, o attende (al massimo una attende).
- In caso di errore: un’esecuzione fallita o scaduta invia una [notifica](#notifiche).

Ogni job conserva le sue ultime 100 esecuzioni con la fine del loro output (4 KB). Il log completo di un’esecuzione è
conservato 30 giorni.

Un job gira come root per conto del suo proprietario, con la cartella home del proprietario come `HOME`, quindi gira
solo finché il suo proprietario è un developer o un admin attivo. Finché il proprietario è disattivato o ha il ruolo
member o viewer, ogni orario del job viene registrato come esecuzione saltata che dice perché, per esempio “its owner
ola is disabled”, e **Esegui ora** risponde con lo stesso motivo. Non viene inviata nessuna notifica. Un’esecuzione
in corso quando il proprietario viene disattivato o riceve un ruolo inferiore a developer viene fermata. Il job torna
a girare quando l’account viene riattivato, o torna developer. Eliminare un account elimina i suoi job. I job
integrati girano qualunque cosa succeda a un account.

Quando un comando termina, i processi che ha avviato in background continuano a girare. Al timeout, all’eliminazione
del job o all’arresto del pacchetto, l’intero gruppo di processi dell’esecuzione riceve SIGTERM, poi SIGKILL dopo 5
secondi.

Nelle notti in cui cambia l’ora, i job seguono la regola del cron di Debian. Un job con `*` all’inizio del campo dei
minuti o delle ore (anche `*/N`), `@hourly` o `@every` segue il tempo trascorso: in autunno gira in entrambi i
passaggi dell’ora ripetuta, e in primavera l’ora saltata non arriva. Ogni altro job esegue ciascuno dei suoi orari una
volta: un orario nell’ora ripetuta gira una volta, e un orario nell’ora saltata gira subito dopo il cambio.

Ogni account connesso, anche un `viewer`, legge i comandi delle attività e l’output delle loro esecuzioni. Non
mettere password o token in un comando: fai in modo che li legga da un file in `<data>/etc` che solo root legge
(`chmod 600`).

### Job integrati

| Job | Quando | Che cosa fa |
|---|---|---|
| Manutenzione | ogni giorno alle 03:30 | rimuove da `tmp/` nella cartella dei dati ciò che nulla ha modificato da 10 giorni; ruota `log/audit.log` oltre i 10 MB |
| Svuota il cestino | ogni giorno alle 03:45 | rimuove ciò che è stato spostato in `trash/` nella cartella dei dati più di 7 giorni fa (come la cartella home di un account eliminato) |
| Aggiornamento del catalogo delle toolchain | ogni giorno alle 04:15 | scarica il catalogo firmato delle toolchain |
| Ruota i log di nginx | ogni ora, al minuto 7 | rinomina `nginx-access.log` o `nginx-error.log` in `.1` quando supera i 20 MB (cinque vecchi file conservati) e dice a nginx di riaprire i suoi log |

Un admin può cambiare pianificazione, timeout, criterio e notifica di un job integrato, e disattivarlo. Il suo nome e
il suo comando restano.

### Operazioni

Installazioni, job degli ambienti, spostamenti dei dati e l’attivazione di un modulo girano come operazioni. L’elenco
mostra le ultime 50, le più recenti per prime, con il loro avanzamento e il loro log. Le operazioni e i loro log sono
conservati 30 giorni. Un’operazione che era in corso quando il pacchetto si è fermato appare come `interrupted`. Un
developer annulla le proprie operazioni; un admin annulla qualsiasi.

## Backup

La pagina **Backup** (admin) imposta che cosa viene salvato, quando, dove vanno le copie e quante ne vengono
conservate. **Esegui il backup ora** avvia un backup in qualsiasi momento. Su una nuova installazione i backup
pianificati sono disattivati; una volta attivato **Backup pianificati**, un backup gira ogni giorno per impostazione
predefinita (il job cron integrato `backup`). Conserva l’esecuzione più recente di ciascuno degli ultimi 7 giorni, 4
settimane e 6 mesi, e sempre l’esecuzione riuscita più recente. Gira un solo backup, verifica, ripristino o
eliminazione alla volta.

Due esecuzioni nello stesso giorno contano come un solo giorno: quando la più recente termina, l’esecuzione precedente
di quel giorno viene rimossa. Un **Esegui il backup ora** nel pomeriggio sostituisce quindi l’esecuzione pianificata
di quella mattina; copia prima un’esecuzione altrove quando lo stato di quella mattina conta.

### Che cosa contiene un backup

Un’esecuzione è una cartella `<dati>/backups/<id>/` con un `manifest.json` che elenca ogni file con il suo sha256:

| Parte | Che cosa |
|---|---|
| impostazioni | `etc/`: impostazioni, account, segreti TOTP, la chiave TLS, le chiavi di backup |
| Git | il dump di Forgejo: repository, wiki, oggetti LFS, allegati e il suo database |
| SVN, bucket | una copia di ogni repository, fatta in modo incrementale: una revisione già copiata non viene copiata di nuovo |
| impostazioni di VS Code | le impostazioni di VS Code di ogni account, le scorciatoie da tastiera, gli snippet e l’elenco delle sue estensioni (non i file delle estensioni: la vista Extensions le reinstalla) |
| ambienti | gli ambienti di Toolchain (disattivati per impostazione predefinita: si possono ricreare) |
| progetti | ogni cartella di progetto con la sua cronologia Git e i file non ancora inviati con commit, senza `node_modules` |
| cartelle HOME | la cartella HOME di ogni account, senza cache e senza le installazioni degli strumenti di IA stessi (il loro pulsante Installa le rimette) |
| App Builder | le impostazioni di ogni app e la cronologia delle sue build; una build rifà i file dell’app |
| repository dei pacchetti | i pacchetti pubblicati, le loro icone e l’indice che legge App Center |

Queste quattro parti sono in ogni backup, qualunque cosa sia spuntata nella pagina. Una cartella che non è cambiata
dal backup precedente è collegata alla copia di quel backup e non occupa spazio.

L’esecuzione diventa visibile solo quando ogni parte è scritta e verificata; un errore o un volume pieno rimuove la
cartella incompiuta e lascia le esecuzioni precedenti come erano. **Verifica** rilegge completamente un’esecuzione: lo
sha256 di ogni archivio, `svnadmin verify` di ogni revisione, `git fsck` di ogni repository e un controllo di
integrità del database di Forgejo.

### Destinazioni

Ogni esecuzione viene copiata su ogni destinazione della pagina:

- **Cartella locale** su questo NAS: l’esecuzione così com’è, con i file invariati collegati alla copia precedente,
  così ogni copia costa solo ciò che è cambiato. Non cifrata. Sugli stessi dischi dei dati, protegge dagli errori, non
  da un volume guasto.
- **rsync** (`user@host:path`) via SSH. **Accedi con la chiave di backup del pacchetto**: la pagina mostra la chiave
  pubblica da aggiungere a `authorized_keys` dell’altra macchina.
- **rclone** (`remote:path`, un remote da `<dati>/etc/rclone.conf`): archiviazione cloud e tutto ciò che rclone
  raggiunge.

Una copia che lascia il NAS (rsync, rclone) deve essere cifrata: attiva **Cifra le copie che lasciano il NAS** e
**Crea la chiave**. Le copie vengono cifrate con age per la sua metà pubblica; **la metà segreta è mostrata una sola
volta** e bvsd non la conserva. Senza di essa nessun NAS, questo compreso, può ripristinare una copia cifrata:
conservala lontano da questo NAS. Una nuova chiave cifra le esecuzioni da quel momento in poi; le copie più vecchie
richiedono la vecchia chiave segreta.

Una destinazione che fallisce viene nominata nel risultato dell’esecuzione; l’esecuzione stessa resta valida.

`rclone.conf` contiene le credenziali dei remoti. Prima che una copia o un **Test** lo usi, bvsd lo rende (insieme alla
chiave di backup) leggibile solo da root (modalità 600) se non lo è, e il log dell’attività lo dice.

### Ripristinare

**Ripristina** su un’esecuzione rimette al suo posto tutto, un repository (un repository SVN, un bucket o un
repository Git) oppure una cartella di progetto:

- Nulla viene sostituito prima di essere verificato: ogni archivio rispetto al suo sha256, ogni copia SVN con
  `svnadmin verify`. Una copia che non passa ferma il ripristino prima che cambi qualcosa nei dati in uso.
- I dati in uso che vengono sostituiti vanno in `<dati>/trash` per 7 giorni, così anche un ripristino si può
  annullare.
- **Un repository** o **una cartella di progetto** viene rimesso al suo posto mentre i moduli continuano a girare.
  Una cartella di progetto eliminata per sbaglio torna con la sua cronologia e i file non ancora inviati con commit.
- **Tutto** ferma i moduli, scambia i dati e riavvia bvsd con le impostazioni ripristinate: ogni sessione termina, e
  gli account e le password sono quelli del backup. Una cartella di progetto o HOME creata dopo il backup resta.

## Notifiche

bvsd scrive ogni notifica nei log di sistema di QTS. Può anche inviarle a servizi di chat e di posta tramite URL
shoutrrr (Telegram, Discord, Slack, e-mail, un webhook generico e altri).

| Notifica | Quando | Interruttore per gli URL |
|---|---|---|
| errore di un job | un’esecuzione cron di un job con notifica in caso di errore è fallita o è scaduta; un’operazione è fallita | `notify.on_job_failure` |
| backup | il risultato di ogni backup, verifica e ripristino, anche di quelli riusciti; un backup pianificato che non è potuto partire | `notify.on_backup` |
| ciclo di riavvii | un processo ha iniziato a riavviarsi in un ciclo continuo | `notify.on_crashloop` |
| certificato | a un certificato caricato restano 30 giorni o meno; nginx è tornato al certificato autofirmato; è stato creato un nuovo certificato autofirmato | `notify.on_cert_expiry` |
| sistema | impostazioni rifiutate all’avvio, trovato uno spostamento dei dati incompiuto, processi rimasti terminati, la configurazione non si è potuta scrivere | inviata sempre |

- Gli interruttori decidono solo che cosa va agli URL. I log di sistema ricevono ogni notifica.
- La stessa notifica parte al massimo una volta ogni 10 minuti. La successiva dopo quel tempo dice quante sono state
  trattenute.
- Ogni URL riceve il proprio `shoutrrr send` con un limite di 10 secondi. Un errore viene registrato solo con lo
  schema dell’URL, perché il resto di un URL contiene il suo token.

Gli URL e gli interruttori si trovano in **Notifiche** nella pagina Sistema; gli interruttori sono attivi per
impostazione predefinita. Un admin li modifica lì. Gli altri account vedono gli interruttori e solo lo schema di ogni
URL, perché il resto di un URL contiene il suo token. L’admin del NAS può impostarli anche tramite il socket CLI:

```bash
/usr/bin/bucketvs/bin/curl --unix-socket /usr/bin/bucketvs/var/run/bvsd-cli.sock \
  -X PUT -H 'Content-Type: application/json' \
  -d '{"notify":{"shoutrrr_urls":["telegram://<token>@telegram?chats=<chat id>"]}}' \
  http://bvsd/api/v1/settings
```

## Log e pacchetto diagnostico

Ogni log è `log/<name>.log` nella cartella dei dati. I log di bvsd e di ogni processo che sorveglia vengono ruotati a 20
MB, e cinque vecchi file sono conservati. `nginx-access.log` e `nginx-error.log` hanno gli stessi limiti, controllati
a ogni avvio di bvsd e ogni ora dal job “Ruota i log di nginx”, quindi uno di essi può superare i 20 MB di al massimo
un’ora di traffico. Il job “Manutenzione” ruota `audit.log` quando supera i 10 MB e conserva tre vecchi file. La
pagina Log (admin) mostra le ultime righe di qualsiasi log, le filtra e segue le nuove righe man mano che arrivano:

| Log | Chi lo scrive |
|---|---|
| `bvsd` | bvsd stesso: avvii, arresti, impostazioni applicate, problemi |
| `nginx` | l’output proprio di nginx |
| `nginx-access`, `nginx-error` | ogni richiesta, e gli errori di nginx |
| `audit` | ogni richiesta sul socket CLI |
| uno per processo di modulo | il programma di ciascun modulo |

La pagina Sistema mostra la versione, la piattaforma, il fuso orario e ogni processo che bvsd sorveglia. “Scarica il
pacchetto diagnostico” (admin) crea un archivio per una segnalazione di bug:

- le impostazioni con la password SMTP e ogni URL shoutrrr mascherati;
- il `nginx.conf` generato, i dettagli del certificato senza la sua chiave, i volumi;
- lo stato di ogni modulo, processo e operazione recente;
- le ultime 2000 righe di ogni log, con ogni stringa di query al loro interno sostituita da `?(query removed)`.

Non contiene mai password, sessioni, token o chiavi.

## Aggiornamenti

bvsd legge `https://pub.qnaputilities.com/bucketvs/latest.json` all’avvio e ogni 24 ore. Quando indica una versione
più recente, la pagina Sistema la nomina, con il collegamento che il feed fornisce. bvsd si limita a segnalare la
nuova versione: non scarica né installa mai un pacchetto. Un controllo non riuscito non cambia nulla nel pannello.

### Installare una nuova versione

Installa il nuovo pacchetto sopra il vecchio, da App Center o con **Installazione manuale**. L’installazione ferma
bvsd, sostituisce i file del programma e lo riavvia; la cartella dei dati, le impostazioni, gli account e ogni
repository restano, e così le sessioni del pannello. Le istanze dell’editor si fermano con bvsd, quindi i terminali
aperti degli editor terminano; un’istanza riparte quando il suo account apre l’editor.

**Da una versione che usava le porte 6050, 6051 e 6052.** Le versioni precedenti ascoltavano su 6050 (HTTP), 6051
(HTTPS) e 6052 (Git via SSH). Il primo avvio della nuova versione sposta ciascuna di esse su 9950, 9951 e 9952 e lo
dice in una notifica; una porta scelta da te resta. Cambia ciò che nomina le vecchie porte: segnalibri, un proxy Plesk
verso 6050, e remote git come `ssh://git@<NAS>:6052/…` (`git remote set-url`).

### Rimuovere, reinstallare e un pacchetto danneggiato

Il tuo lavoro resta quando il pacchetto viene rimosso, reinstallato o si danneggia. Tutto ciò che crei sta nella
cartella dei dati: account e impostazioni, repository Git e SVN, bucket, cartelle di progetto, cartelle HOME,
impostazioni ed estensioni di VS Code, app dell’App Builder, ambienti, il repository dei pacchetti e i backup. La
cartella di installazione (`<volume>/.qpkg/bucketvs`) contiene solo i programmi e un file che indica la cartella dei
dati.

- **Rimuovere, poi reinstallare.** La rimozione del pacchetto cancella la cartella di installazione e tiene la cartella
  dei dati e `/etc/config/bucketvs-data-root`, il file che la indica. L’installazione successiva legge quel file e
  parte sulla stessa cartella dei dati: gli stessi account e password, gli stessi progetti e repository, sessioni
  aperte comprese.
- **Installare sopra il pacchetto installato.** La stessa versione o una più recente sostituisce i file dei
  programmi; la cartella dei dati non viene toccata.
- **Un pacchetto danneggiato.** Quando la cartella di installazione è danneggiata (manca un programma, la cartella è
  stata eliminata a mano), reinstalla il pacchetto da App Center o con **Installazione manuale**. L’installazione
  trova la cartella dei dati allo stesso modo. Un bvsd che gira ancora da file eliminati viene fermato all’avvio, e
  parte quello installato.
- **Cosa cancella i dati.** Solo l’eliminazione della cartella della cartella dei dati. Per ripartire da zero di
  proposito, rimuovi il pacchetto, elimina la cartella della cartella dei dati, poi installa: il primo avvio crea una
  nuova cartella dei dati, come descritto in [Dove nasce la cartella dei dati](#dove-nasce-la-cartella-dei-dati).

Un disco che si guasta è un’altra cosa: la cartella dei dati sta sui dischi del NAS stesso. Tieni i backup su un’altra
macchina ([Backup](#backup)); ogni backup contiene le tue cartelle di progetto, le cartelle HOME, le impostazioni
dell’App Builder e il repository dei pacchetti.

## Il comando bvs

`bvs` è la riga di comando del pacchetto, in `/usr/bin/bucketvs/bin`. È nel `PATH` di ogni processo che bvsd avvia,
job cron compresi. Mentre il pacchetto è in esecuzione, `/usr/bin/bvs` è un link al comando, quindi anche in una
sessione SSH sul NAS lo chiami per nome; l’arresto del pacchetto rimuove il link.

Ogni comando che parla con bvsd tramite il suo socket CLI richiede root, perché il socket è solo di root: `user`,
`jobs` e, mentre bvsd è in esecuzione, `tc install`, `tc remove` ed `env create`, `clone`, `export` e `delete`. Anche
`env shell`, `env run` e `qbuild` richiedono root: avviano `bvs-chroot`. Come altro utente, un comando che ha bisogno
del socket si ferma con “bvsd's CLI socket … is open to root only: run bvs as root”.

```text
bvs env list
bvs env create <name> --from <rootfs-id> [--package <name>]...
bvs env shell <name>
bvs env run <name> -- <command> [<arg>...]
bvs env clone <name> <new-name>
bvs env export <name>
bvs env delete <name>
bvs tc list
bvs tc install <id>
bvs tc remove <id>
bvs use <id>
bvs jobs
bvs user list
bvs user password <name>
bvs user totp-reset <name>
bvs guide [--path] [en|pl|de|fr|it|nl|sv|ja|zh-TW]
bvs qbuild [--env <name>] [<qbuild argument>...]
bvs kernel profile [--json]
bvs kernel sources
bvs kernel prepare [--source <id>] [--env <name>] [--option NAME=VALUE]... [--full]
bvs kernel make [--project <folder> | --dir <tree directory>] [--build-only] [<target>...]
bvs kernel check <file.ko | module>
bvs kernel load <module>
bvs kernel unload <module>
```

- Mentre bvsd è in esecuzione, le installazioni e i job degli ambienti vanno a bvsd tramite il suo socket CLI, così il
  pannello li mostra, e `bvs` li segue finché non terminano. Senza bvsd girano nel processo `bvs` stesso; `--local`
  prima del comando lo fa di proposito.
- `bvs jobs` e i comandi `user` richiedono bvsd.
- `bvs guide` apre questa guida con il `less` del pacchetto in un terminale, e altrimenti, o con `--path`, stampa il
  percorso del suo file. Senza una lingua segue la prima impostata tra `LC_ALL`, `LC_MESSAGES` e `LANG`: `de_DE.UTF-8`
  apre la guida tedesca, `zh_TW` e `zh_HK` quella in cinese tradizionale, e una lingua in cui la guida non è scritta
  (`C`, `zh_CN`) quella inglese.

## File e cartelle

| Percorso | Che cosa |
|---|---|
| `/usr/bin/bucketvs` | i programmi del pacchetto (un collegamento alla sua cartella di installazione) |
| `/usr/bin/bucketvs/etc/data-root` | il percorso della cartella dei dati |
| `/usr/bin/bucketvs/var/run/bvsd.sock` | il socket API del pannello (lo usa nginx) |
| `/usr/bin/bucketvs/var/run/bvsd-cli.sock` | il socket del comando `bvs`, solo root |
| `<cartella dei dati>/etc/` | impostazioni, account, sessioni, token, i certificati, il `nginx.conf` generato (segreti con modalità 600) |
| `<cartella dei dati>/log/` | i log |
| `<cartella dei dati>/tmp/` | file temporanei di ogni processo |
| `<cartella dei dati>/home/<utente>/` | la cartella home di ogni account |
| `<cartella dei dati>/jobs/` | operazioni, job cron e le loro esecuzioni, con i log |
| `<cartella dei dati>/toolchains/` | la cache dei download, toolchain e runtime installati, esportazioni |
| `<cartella dei dati>/envs/` | ambienti |
| `<cartella dei dati>/projects/` | aree di lavoro, visibili come `/projects` dentro gli ambienti |
| `<cartella dei dati>/trash/` | elementi eliminati, conservati 7 giorni |

I moduli aggiungono cartelle proprie; le loro sezioni le nominano.
