# Bucket & Code Server IDE: przewodnik

Bucket & Code Server IDE (QPKG `bucketvs`) przechowuje kod źródłowy i pliki na NAS-ie QNAP i buduje na nim
oprogramowanie. Ma własny serwer WWW, własne narzędzia i własne konta.

Przewodnik opisuje pakiet działający na NAS-ie.

## Spis treści

- [Co działa na NAS-ie](#co-działa-na-nas-ie)
- [Instalacja pakietu](#instalacja-pakietu)
- [Pierwsze logowanie](#pierwsze-logowanie)
- [Praca w panelu](#praca-w-panelu)
- [Konta](#konta)
- [Folder danych](#folder-danych)
- [Sieć](#sieć)
- [Za odwrotnym proxy (Plesk)](#za-odwrotnym-proxy-plesk)
- [Nazwy hostów modułów](#nazwy-hostów-modułów)
- [Publikacja w internecie (Cloudflare Tunnel)](#publikacja-w-internecie-cloudflare-tunnel)
- [Moduły](#moduły)
- [Git](#git)
- [Runnery CI](#runnery-ci)
- [SVN i Bucket](#svn-i-bucket)
- [Edytor i narzędzia AI](#edytor-i-narzędzia-ai)
- [Toolchainy i środowiska](#toolchainy-i-środowiska)
- [App Builder](#app-builder)
- [Repozytorium pakietów](#repozytorium-pakietów)
- [Warsztat jądra](#warsztat-jądra)
- [Zadania cron i operacje](#zadania-cron-i-operacje)
- [Kopie zapasowe](#kopie-zapasowe)
- [Powiadomienia](#powiadomienia)
- [Logi i paczka diagnostyczna](#logi-i-paczka-diagnostyczna)
- [Aktualizacje](#aktualizacje)
- [Polecenie bvs](#polecenie-bvs)
- [Pliki i foldery](#pliki-i-foldery)

## Co działa na NAS-ie

| Część | Co robi | Gdzie nasłuchuje |
|---|---|---|
| nginx | jedyne wejście z sieci LAN: udostępnia panel i przekazuje każdą ścieżkę do jej modułu | 9950 (HTTP), 9951 (HTTPS) |
| bvsd | uruchamia i pilnuje wszystkich pozostałych procesów, przechowuje konta i ustawienia, wykonuje zadania i zadania cron, odpowiada panelowi | dwa gniazda uniksowe w `/usr/bin/bucketvs/var/run/` |
| Git (Forgejo) | hosting repozytoriów Git z interfejsem WWW | gniazdo uniksowe w `/usr/bin/bucketvs/var/run/forgejo/` oraz 9952 dla Gita przez SSH |
| SVN i Bucket (httpd) | repozytoria Subversion i foldery WebDAV z historią | 127.0.0.1:9961 albo następny wolny port, gdy ten zajmuje inny program |
| Edytor (code-server) | VS Code w przeglądarce, jedna instancja na konto | gniazdo uniksowe każdego konta w `/usr/bin/bucketvs/var/run/ide/` |
| CI | runner Forgejo Actions | bez własnego portu; gdy moduł jest włączony, nginx nasłuchuje dla niego na 127.0.0.1:9960 albo na następnym wolnym porcie, gdy 9960 zajmuje inny program |

Rdzeń (nginx, bvsd, panel, zadania cron, toolchainy) działa zawsze. Git, CI, SVN i Bucket oraz Edytor to moduły,
które admin włącza i wyłącza. Na każdym adresie NAS-a moduły odpowiadają pod swoimi ścieżkami: `/git/`, `/svn/`,
`/bucket/`, `/ide/`. Panel jest pod `/`, a ten przewodnik pod `/guide/`.

Gdy pakiet działa, jego programy są w `/usr/bin/bucketvs/bin`. Wszystko, co pakiet przechowuje (konta, ustawienia,
repozytoria, środowiska, logi), jest w jednym folderze na woluminie z danymi: w [folderze danych](#folder-danych).

## Instalacja pakietu

Pakiet wymaga QTS 6.0 albo QuTS hero 6.0 lub nowszego, na NAS-ie x86_64 albo ARM 64-bit; dla każdej architektury
jest osobny pakiet (`bucketvs_<wersja>_x86_64.qpkg`, `bucketvs_<wersja>_arm_64.qpkg`). Jest podpisany certyfikatem
kodu: instaluje się go z App Center (z repozytorium, które go wymienia, albo przez **Instalację ręczną** pobranego
pliku). App Center sprawdza podpis; nie trzeba zezwalać na aplikacje bez ważnego podpisu.

Instalacja zakłada użytkownika usługi `bucketvs` (dla Forgejo i httpd), dowiązanie `/usr/bin/bucketvs` do folderu
instalacji i uruchamia bvsd. System Logs podaje folder danych.

### Gdzie powstaje folder danych

Przy pierwszym uruchomieniu folder danych to `BucketVS` na woluminie, na którym zainstalowano pakiet, na przykład
`/share/CACHEDEV1_DATA/BucketVS` (QTS) albo `/share/ZFS2_DATA/BucketVS` (QuTS hero). Później można go przenieść na
inny wolumin z panelu ([Przenoszenie folderu danych](#przenoszenie-folderu-danych)).

Pakiet pamięta swój folder danych w `/etc/config/bucketvs-data-root`, który przeżywa usunięcie pakietu. Usunięcie
pakietu zostawia folder danych i ten plik, więc instalacja po usunięciu startuje na tych samych danych i kontach. Start
nigdy nie zależy od folderu, którego nie ma. Gdy folderu danych już nie ma (usunięty, na wymienionym dysku albo na
woluminie, który akurat jest offline), start tworzy `BucketVS` na woluminie pakietu, nowy folder, chyba że folder o tej
nazwie już tam jest, a w nowym [pierwsze logowanie](#pierwsze-logowanie) ustawia hasło administratora. Dotyczy to
instalacji po usunięciu i zainstalowanego pakietu, także przy starcie NAS-a. System Logs ma o tym ostrzeżenie, a pliki
`.prev` zachowują starą ścieżkę: `/etc/config/bucketvs-data-root.prev`, a przy zainstalowanym pakiecie także
`etc/data-root.prev` w folderze instalacji. Jeśli stary folder leżał na woluminie, który w tamtym momencie był tylko
offline, włącz wolumin, zatrzymaj pakiet, skopiuj ten plik na `etc/data-root` w folderze instalacji (na przykład
`/share/CACHEDEV1_DATA/.qpkg/bucketvs/etc/data-root`) i uruchom pakiet: znowu działa na starych danych.

### Otwieranie panelu z App Center

**Otwórz** przy pakiecie w App Center otwiera panel na porcie 9950 (HTTP) albo 9951 (HTTPS). Gdy porty zmieni się w
panelu ([Porty](#porty)), pakiet przekazuje App Center nowe przy następnym uruchomieniu.

### Autostart z sesji SSH

Dopóki przełącznik pakietu w App Center jest włączony, NAS uruchamia pakiet przy starcie systemu. W sesji SSH na
NAS-ie, jako admin, ten sam przełącznik ustawia skrypt usługi:

```sh
/etc/init.d/bucketvs.sh disable   # następny start zostanie odrzucony, także przy starcie systemu
/etc/init.d/bucketvs.sh enable    # pakiet znów startuje razem z systemem
```

Każde z tych poleceń zapisuje tylko przełącznik (`Enable` w `/etc/config/qpkg.conf`), wypisuje, co ustawiło, i dodaje
ten sam wiersz do System Logs. Po `disable` działający pakiet działa dalej, a `enable` niczego nie uruchamia: do tego
służą `stop` i `start`, przy czym `start` odmawia, dopóki pakiet jest wyłączony. `status` mówi, czy bvsd działa.

## Pierwsze logowanie

Panel jest pod adresem `https://<adres NAS-a>:9951/` albo `http://<adres NAS-a>:9950/`. Adres HTTPS ma na początku
certyfikat z podpisem własnym, więc przeglądarka ostrzega, dopóki urządzenie mu nie zaufa; część
[Certyfikat](#certyfikat) opisuje, jak porównać jego odcisk.

Nowa instalacja nie ma żadnego konta. Dopóki pierwszy administrator nie ma hasła, panel otwiera się na jednym
ekranie, „Ustaw hasło administratora”, dla konta `administrator`. Wpisz hasło dwa razy: co najmniej 10 znaków
i najwyżej 72 bajty. Zapisanie hasła tworzy konto i loguje do panelu.

```mermaid
flowchart TD
    start["Pakiet startuje<br/>bez żadnego konta"] --> screen["Ustaw hasło administratora<br/>60 minut, tylko sieć lokalna"]
    screen -->|"zapisane"| panel["administrator zalogowany"]
    screen -->|"minęło 60 minut"| restart["Ponowne uruchomienie pakietu<br/>w App Center: nowe 60 minut"]
    restart --> screen
```

- Ekran przyjmuje hasło przez 60 minut od startu pakietu. Potem informuje, że czas na ustawienie hasła minął;
  ponowne uruchomienie Bucket & Code Server IDE w App Center daje kolejne 60 minut.
- Przyjmuje hasło tylko z sieci lokalnej: z samego NAS-a albo z adresu z zakresu prywatnego (`10.0.0.0/8`,
  `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `fc00::/7`, `fe80::/10`).
- Dopóki ekran jest otwarty, Dzienniki systemu QTS pokazują wpis informacyjny dla każdego adresu LAN NAS-a, na
  przykład:

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

- Odrzuca żądanie, które przyszło przez proxy (z nagłówkiem `X-Forwarded-For`, `X-Forwarded-Host`,
  `X-Forwarded-Proto`, `X-Real-IP` albo `Forwarded`), także przez proxy w sieci LAN: aby ustawić hasło, otwórz
  panel bezpośrednio z komputera w sieci lokalnej.

Z sesji SSH na NAS-ie `/usr/bin/bucketvs/bin/bvs user password administrator` tworzy konto, gdy jeszcze go nie ma,
i wypisuje losowe hasło; przy pierwszym logowaniu panel prosi wtedy o własne hasło
([Gdy admin nie może się zalogować](#gdy-admin-nie-może-się-zalogować)).

Po pierwszym zalogowaniu Przegląd mówi, że działa tylko rdzeń. Kafelek każdego modułu mówi, co moduł daje, i ma
przycisk, który go włącza, np. **Włącz Git**: włącz te, których potrzebujesz ([Moduły](#moduły)).

## Praca w panelu

Panel działa jak edytor kodu: każda strona otwiera się w karcie, a paleta poleceń prowadzi do stron i działań po
nazwie.

- **Pasek obszarów** (ikony po lewej). Każda ikona otwiera swoją stronę i pokazuje swoje drzewo na pasku bocznym:
  moduły, repozytoria i buckety, aplikacje, pakiety, środowiska, zadania i kopie zapasowe. Kropka na ikonie oznacza
  moduł w kłopocie, nieudane budowanie albo nieudaną kopię zapasową. Kliknięcie ikony strony na wierzchu ukrywa pasek
  boczny; to samo robi Ctrl+B.
- **Karty.** Strona otwarta pojedynczym kliknięciem jest podglądem, kursywą: następna strona otwiera się w jej
  miejscu. Edycja strony albo dwuklik na karcie ją zatrzymuje. Karty wracają po przeładowaniu, dla każdego konta w
  każdej przeglądarce. W oknie o szerokości co najmniej 1280 pikseli **Przenieś kartę na drugą stronę** (`Ctrl+\`)
  pokazuje dwie strony obok siebie.
- **Obiekty i ich menu.** Jedno kliknięcie wiersza paska bocznego, wiersza tabeli albo karty otwiera ten obiekt: jego
  strona wychodzi na wierzch, a obiekt dostaje zieloną obwódkę albo otwierają się jego szczegóły. Adres wskazuje
  obiekt, więc Wstecz, Dalej i skopiowany link do niego wracają. Dwuklik zachowuje kartę. Prawy przycisk myszy,
  Shift+F10 albo dłuższe przytrzymanie na ekranie dotykowym otwiera menu obiektu: sposoby otwarcia (w tej karcie, w
  zatrzymanej karcie, po drugiej stronie, w nowej karcie przeglądarki), to, co jego strona może z nim zrobić, oraz
  kopiowanie nazwy i adresu. Menu karty zamyka ją, pozostałe karty, karty po prawej albo wszystkie. Panel opuszcza
  tylko element oznaczony ↗. Ikona **Administracja** otwiera stronę System.
- **Paleta poleceń.** Ctrl+Shift+P (albo Ctrl+K, albo F1) wymienia polecenia, które twoja rola może uruchomić:
  otwarcie strony, budowanie lub publikację aplikacji, kopię zapasową, ponowne uruchomienie modułu, powłokę w
  środowisku. Ctrl+P przechodzi do strony, aplikacji, repozytorium, środowiska albo pakietu po kilku literach nazwy.
- **Dolny panel** (Ctrl+J). **Zadania** pokazuje działające i ostatnie zadania z postępem i logiem. **Problemy**
  wymienia to, co zgłaszają moduły: nieudane budowania, wyłączone miejsca docelowe kopii, moduły w kłopocie,
  certyfikat bliski wygaśnięcia i uwagi inspektora ELF dla aplikacji na wierzchu. **Wyjście** śledzi zadanie,
  które uruchomiłeś ostatnio.
- **Przesuwanie i zmiana rozmiaru.** Przeciągnij krawędź, aby zmienić rozmiar tego, co ogranicza: paska bocznego,
  dolnego panelu, dwóch stron obok siebie albo dwóch części strony obok siebie, na przykład kreatora i jego
  podsumowania albo listy plików projektu i pliku. Dwukrotne kliknięcie krawędzi przywraca ją na miejsce. Okno
  dialogowe przesuwasz, ciągnąc za tytuł, a jego rozmiar zmieniasz, ciągnąc za krawędź albo prawy dolny róg; dwukrotne
  kliknięcie tytułu przywraca je na środek. Aby postawić dolny panel obok stron, przeciągnij jego nagłówek do prawej
  krawędzi edytora albo kliknij przycisk obok jego ×; przeciągnięcie do dolnej krawędzi albo ponowne kliknięcie
  przycisku odstawia go z powrotem. Kartę można przeciągnąć w inne miejsce paska kart albo na drugą stronę. Panel
  pamięta każdy rozmiar i miejsce dla twojego konta w tej przeglądarce, a **Przywróć układ** (w palecie poleceń albo
  pod przyciskiem konta na dole paska obszarów) przywraca je wszystkie. Z klawiatury strzałki przesuwają wybraną
  krawędź (z Shift dłuższymi krokami, Home przywraca ją na miejsce), Ctrl+Shift+strzałka w lewo i w prawo przesuwają
  wybraną kartę, a okno dialogowe ma u góry pasek, a w prawym dolnym rogu uchwyt, którymi strzałki je przesuwają i
  zmieniają jego rozmiar. Na telefonie okna dialogowe i dolny panel zostają tam, gdzie są.
- **Pasek stanu.** Stany modułów, działające zadania, problemy, wolne miejsce na woluminie danych, certyfikat,
  zalogowane konto, język (edytor przyjmuje go przy następnym uruchomieniu, a dla szwedzkiego i niderlandzkiego zostaje po angielsku; Git go stosuje, chyba że wybierzesz tam inny), motyw (ciemny, jasny, jak w systemie, głęboki granat, grafit albo papier: należy do konta, a edytor i Git też go dostają, chyba że wybierzesz tam inny) i wersja. Każdy element otwiera miejsce, gdzie
  widać więcej.
- **Klawiatura.** Ctrl+/ albo ? pokazuje skróty. Karta przeglądarki zatrzymuje Ctrl+W i Ctrl+Tab dla siebie, więc
  panel zamyka kartę przez Alt+W i przełącza karty przez Alt+PageDown i Alt+PageUp.
- **Na telefonie** pierwsze obszary są na dole, **Więcej** otwiera pasek boczny z resztą, a otwarte strony są listą
  na górze.

## Konta

Panel ma własne konta. Konta QTS się do niego nie logują.

### Role

| Rola | Co może |
|---|---|
| viewer | przegląda panel; strony Kopie zapasowe, Miejsce na dane, Konta i Logi są dla adminów. W Forgejo nie zakłada, nie forkuje ani nie importuje repozytoriów, nie zakłada organizacji i nigdy nie wypycha zmian; czyta repozytoria SVN i buckety, do których ma prawa, i do żadnego nie zapisuje |
| użytkownik (`member`) | czyta to, co viewer, i zapisuje: wypycha zmiany do repozytoriów Git, w których dostał prawo zapisu, zapisuje w repozytoriach SVN i bucketach, w których ma odczyt i zapis, i przywraca tam starsze wersje plików. W Forgejo nie zakłada, nie forkuje ani nie importuje repozytoriów i nie zakłada organizacji, nie wypchnie zmian do repozytorium, którego właścicielem lub administratorem jest konto z rolą niższą niż developer, ani, gdy moduł CI jest włączony, do repozytorium z włączonymi Actions. Nie dostaje edytora, zadań cron ani budowania |
| developer | do tego otwiera edytor, czyta wiersze `bvs use` zainstalowanych toolchainów, tworzy, zmienia i uruchamia własne zadania cron, tworzy i buduje aplikacje, publikuje pakiety w repozytorium (wystawia je admin), sprawdza moduły jądra i zakłada repozytoria Git, których workflowy działają na NAS-ie, gdy moduł CI jest włączony |
| admin | wszystko: konta, ustawienia, miejsce na dane, sieć, moduły, toolchainy, środowiska, każde zadanie cron, aktywacja aplikacji, ładowanie modułów jądra, wystawianie pakietów w repozytorium |

Rola developer to root na NAS-ie: jej polecenia cron działają jako root, tak samo jej terminal edytora i, gdy moduł CI
jest włączony, workflowy repozytoriów, do których może wysyłać zmiany. Dawaj ją tylko osobom, którym dałbyś hasło
administratora NAS-a. Komuś, kto tylko zapisuje zmiany w repozytoriach, wystarczy rola użytkownika.

W sekcji **Twoje konto** każdy widzi swoją rolę i to, na co ona pozwala, z linkiem do tej sekcji. Strona powyżej czyjejś
roli mówi, jakiej roli wymaga, a to, co zmienia tylko admin, jest tak opisane tam, gdzie się pojawia. Sieć, Git i CI
pokazują pozostałym swoje ustawienia jako wartości, z „Nie ustawiono” tam, gdzie wartości brak.

Workflowy działają na NAS-ie jako root, więc bvsd trzyma Actions wyłączone w każdym repozytorium Git, którego
właścicielem jest konto z rolą użytkownika lub viewer albo do którego takie konto może zapisywać: nadanie takiemu kontu prawa zapisu do
repozytorium wyłącza jego Actions w ciągu minuty, a strona Git pokazuje adminom te repozytoria. Aby użyć tam Actions,
najpierw odbierz to prawo zapisu w Forgejo albo przenieś repozytorium należące do takiego konta na konto developera.

### Tworzenie i zmiana kont

Admin zarządza kontami na stronie Konta.

- Nowe konto zaczyna z rolą viewer. Rolę developer i admin wybiera się świadomie: rola developer to root na NAS-ie
  (wyżej).
- Nowe konto zaczyna bez zadań cron, nawet gdy konto o tej samej nazwie jakieś miało.
- Nazwa ma od 2 do 32 znaków: najpierw mała litera, potem małe litery, cyfry, `.`, `_` lub `-`. Dwa znaki spośród
  `.`, `_`, `-` z rzędu albo jeden na końcu są odrzucane. Tak samo nazwy zarezerwowane przez Forgejo (`admin`,
  `api`, `user` i inne), `bucketvs` (konto systemowe pakietu) oraz `cli` i `system` (obu używa sam bvsd).
- „Przy pierwszym logowaniu musi ustawić własne hasło” sprawia, że konto ustawia własne hasło, zanim zrobi
  cokolwiek innego.
- Wyłączenie konta od razu kończy jego sesje, a jego tokeny API przestają działać. Jego dane zostają. Jego
  [zadania cron](#zaplanowane-zadania) też zostają, ale nie działają, dopóki konto jest wyłączone, a trwające
  uruchomienie któregoś z nich jest zatrzymywane.
- Nadanie kontu roli użytkownika albo viewer działa tak samo na jego zadania cron: zostają, nie działają, dopóki rola
  konta jest niższa niż developer, a trwające uruchomienie jest zatrzymywane.
- Viewer nigdy nie wypycha zmian do Git, także tam, gdzie dostał w Forgejo prawo zapisu. Konto, które ma wypychać
  zmiany, dostaje rolę użytkownika.
- Usunięcie konta usuwa je razem z sesjami i tokenami API. Jego folder domowy trafia do
  `trash/home/<nazwa>-<czas>` w folderze danych, skąd zadanie „Opróżnianie kosza” usuwa go po 7 dniach, a folder
  `tmp/tmux-<nazwa>` jest usuwany. Jego zadania cron są usuwane razem z uruchomieniami i logami.
- Ostatniego włączonego admina nie da się zdegradować, wyłączyć ani usunąć.
- „Ustaw nowe hasło” kończy sesje konta i unieważnia jego tokeny API.

### Hasła i sesje

- Hasło ma co najmniej 10 znaków i najwyżej 72 bajty.
- Zmiana własnego hasła (na stronie Twoje konto) kończy pozostałe sesje i unieważnia tokeny API. Sesja, w której
  hasło zmieniono, zostaje zalogowana.
- Sesja kończy się 12 godzin po ostatnim żądaniu, a w każdym razie 7 dni po zalogowaniu. Konto ma najwyżej 32
  sesje; kolejne logowanie kończy tę, której używano najdawniej.
- Ciasteczko sesji ma flagi `HttpOnly` i `SameSite=Lax`. Dostaje flagę `Secure`, gdy przeglądarka połączyła się
  z NAS-em przez HTTPS, bezpośrednio albo przez [zaufane proxy](#zaufane-proxy).

### Błędne hasła

- Pięć nieudanych logowań w ciągu 15 minut z jednego adresu blokuje ten adres na 15 minut, nawet przy dobrym
  haśle. Klient IPv6 liczy się jako cała jego sieć /64.
- Pięć nieudanych logowań na jedno konto z jednego adresu blokuje to konto z tego adresu na 15 minut.
- Nieudane logowania na jedno konto z wielu adresów spowalniają jego kolejne logowania: 125 ms po jednej porażce,
  z podwojeniem aż do 2 sekund. Z sieci lokalnej nigdy go nie blokują.
- Z internetu (z każdego adresu spoza sieci lokalnej) 20 nieudanych logowań na jedno konto od ostatniego udanego
  blokuje je tam, niezależnie od adresów, z których przyszły; wtedy i dobre hasło jest tam odrzucane. Z sieci lokalnej
  konto nadal się loguje. Admin odblokowuje je na stronie Konta (**Odblokuj**) albo poleceniem
  `bvs user unlock <nazwa>`, albo nadaje mu nowe hasło. Liczbę ustawia się na stronie Konta w **Logowania z
  internetu**; 0 wyłącza tę blokadę.
- Liczniki są w pamięci, więc restart bvsd je zeruje; blokada trwa, dopóki ktoś jej nie zdejmie.

Za proxy liczy się adres, który proxy podaje, ale tylko gdy to proxy jest na liście [zaufanych proxy](#zaufane-proxy).
W przeciwnym razie każdy klient liczy się jako adres samego proxy i pięć błędnych haseł od kogokolwiek blokuje
wszystkich na 15 minut.

### Uwierzytelnianie dwuskładnikowe

Każde konto włącza je samo na stronie Twoje konto: zeskanuj kod QR aplikacją uwierzytelniającą (albo wpisz klucz),
a potem wpisz 6-cyfrowy kod, który pokazuje aplikacja. Od tej chwili logowanie po haśle prosi o kod.

- Kody są zgodne z RFC 6238: 6 cyfr, nowy kod co 30 sekund. Kod o 30 sekund za wczesny lub za późny jest
  przyjmowany. Ten sam kod nigdy nie jest przyjęty dwa razy.
- Jeśli świeży kod nie przechodzi, zegar telefonu najpewniej się rozjechał; ustaw w nim czas automatyczny.
- Wyłączenie wymaga hasła konta.
- Przy zgubionym telefonie admin wyłącza je na stronie Konta. Wtedy do logowania wystarcza samo hasło.

### Tokeny API

Skrypty i CI zamiast hasła używają tokenów API. Na stronie Twoje konto „Nowy token” tworzy token:

- Ma postać `bvs_` i 40 cyfr szesnastkowych i jest pokazywany jeden raz. bvsd przechowuje tylko jego skrót.
- Skrypt wysyła go jako `Authorization: Bearer bvs_...`.
- Zakresy to `read`, `write` i `admin`; odpowiadają rolom viewer, developer i admin. Token działa z najwyższym
  swoim zakresem, nigdy ponad obecną rolę właściciela, więc zdegradowanie konta degraduje też jego tokeny. Roli
  użytkownika nie odpowiada żaden zakres: konto z rolą użytkownika tworzy tokeny `read`.
- Nowe hasło unieważnia wszystkie tokeny właściciela. Tokeny wyłączonego właściciela przestają działać.
- Token nie zmienia haseł ani uwierzytelniania dwuskładnikowego i nie tworzy ani nie usuwa tokenów: to wymaga zalogowanej
  sesji. Wykradziony token nie odetnie więc właściciela od konta.

### Gdy admin nie może się zalogować

Admin NAS-a może odzyskać każde konto z sesji SSH na NAS-ie. Polecenie `bvs` rozmawia z bvsd przez gniazdo, które
otwiera tylko root (`/usr/bin/bucketvs/var/run/bvsd-cli.sock`, tryb 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 <nazwa>` ustawia nowe losowe hasło o długości 20 znaków i je wypisuje. Przy następnym
  logowaniu konto musi ustawić własne. Jego sesje się kończą, a tokeny API są unieważniane. W nowej instalacji,
  która nie ma jeszcze konta, `bvs user password administrator` tworzy to konto.
- `bvs user totp-reset <nazwa>` wyłącza kontu uwierzytelnianie dwuskładnikowe.
- `bvs user unlock <nazwa>` zdejmuje blokadę konta po zbyt wielu błędnych hasłach z internetu.

Każde żądanie na tym gnieździe działa jako wbudowany admin `cli` i trafia do `log/audit.log` w folderze danych. Te
polecenia działają tylko wtedy, gdy działa bvsd.

## Folder danych

Folder danych to folder, w którym jest wszystko, co pakiet przechowuje. Jego ścieżka to jedyny wiersz pliku
`/usr/bin/bucketvs/etc/data-root`. Strona Miejsce na dane pokazuje go razem z woluminem, na którym jest, i wolnym
miejscem na nim.

bvsd startuje tylko na folderze danych, który jest folderem na prawdziwym woluminie. Start pakietu sprawdza to
wcześniej: folder, którego nie ma, albo folder na tmpfs lub ramfs daje nowy folder danych na woluminie instalacji, jak
opisuje [Gdzie powstaje folder danych](#gdzie-powstaje-folder-danych). Wolumin, który jest offline, zostawia swój punkt
montowania jako pusty folder w RAM-ie NAS-a, a zapisane tam ustawienia zniknęłyby przy następnym restarcie. Sam bvsd
odrzuca taki folder tylko wtedy, gdy ktoś uruchomi go ręcznie, a przyczyna trafia wtedy do Dzienników systemu QTS.

### Co bvsd daje każdemu procesowi

QTS trzyma `/tmp` na małym RAM-dysku, a `/root` na RAM-dysku, który restart czyści. Dlatego każdy proces, który
uruchamia bvsd (programy modułów, zadania cron, instalacje), ma pliki tymczasowe i folder domowy w folderze danych:

| Zmienna | Wartość |
|---|---|
| `HOME` | `<folder danych>/home/<konto>` |
| `TMPDIR` | `<folder danych>/tmp` |
| `XDG_CACHE_HOME`, `XDG_CONFIG_HOME` | `.cache` i `.config` w tym folderze domowym |
| `npm_config_cache` | `.npm` w tym folderze domowym |
| `PATH` | `~/.local/bin`, `~/.npm-global/bin`, potem `bin`, `sbin` i `node/bin` pakietu, potem foldery systemu |
| `SSL_CERT_FILE`, `GIT_SSL_CAINFO`, `CURL_CA_BUNDLE` | zestaw certyfikatów CA pakietu, `/usr/bin/bucketvs/etc/ssl/cacert.pem` |
| `TZ` | strefa czasowa NAS-a, ustawiona w QTS |
| `SHELL`, `EDITOR` | `bash` i `nano` pakietu |
| `LANG`, `LESSCHARSET` | `C.UTF-8` i `utf-8`, więc nazwy plików z polskimi literami wyglądają tak, jak powinny |

### Przenoszenie folderu danych

Strona Miejsce na dane przenosi cały folder danych do innego folderu w `/share`. Wybierz folder udostępniony albo
wolumin; dane trafią do nowego folderu `BucketVS` w środku. Żeby użyć samego wybranego folderu, otwórz pusty folder
o nazwie `BucketVS`. Foldery udostępnione tworzy się w QTS (Panel sterowania, Foldery udostępnione); wewnątrz
folderu udostępnionego albo woluminu strona może utworzyć nowy folder. Okno wyboru otwiera się tam, gdzie dane są
teraz, i mówi to; stamtąd otwórz folder udostępniony albo wolumin, do którego mają trafić.

Przed startem przenoszenia strona sprawdza cel i wymienia każdy powód, dla którego go odrzuca:

- ścieżka musi być w `/share` i nie może być samym `/share`;
- żadna część ścieżki, ani ścieżki, do której prowadzi dowiązanie, nie zawiera `:`, `&`, `|` ani znaku sterującego;
- nie obecny folder danych, nie folder w nim i nie folder, który go zawiera;
- nowy folder albo pusty;
- system plików, który przechowuje uniksowych właścicieli i uprawnienia (FAT, exFAT i NTFS są odrzucane: Forgejo
  i SVN potrzebują właścicieli i trybów);
- wolumin zamontowany z `exec`, `dev` i `suid`, których potrzebują środowiska do budowania;
- zapisywalny, z wolnym miejscem na dane i 10 % zapasu.

```mermaid
flowchart TD
    check["Ponowne sprawdzenie celu<br/>wolne miejsce: dane + 10 %"] --> stop["Zatrzymanie modułów<br/>panel działa dalej"]
    stop --> copy["Kopiowanie<br/>rsync -aHAX --numeric-ids"]
    copy --> verify["Weryfikacja kopii<br/>pliki, bajty, właściciele, tryby, czasy,<br/>sha256 do 1000 plików"]
    verify --> switch["Przełączenie folderu danych<br/>w /usr/bin/bucketvs/etc/data-root"]
    switch --> start["bvsd startuje od nowa na nowym folderze<br/>i uruchamia moduły"]
    start --> old["Stara kopia zostaje, oznaczona MOVED-TO,<br/>dopóki jej nie usuniesz"]
    copy -->|błąd| failed["Stary folder zostaje w użyciu,<br/>moduły startują ponownie"]
    verify -->|różnica| failed
```

W trakcie przenoszenia:

- Git, CI, SVN i Bucket oraz Edytor nie odpowiadają. Panel działa i pokazuje postęp.
- Nie startuje żadne nowe uruchomienie zadania cron. Uruchomienie, które już trwało, trwa dalej.
- Modułu nie da się włączyć ani wyłączyć, dopóki przenoszenie się nie skończy.

Po przełączeniu bvsd zatrzymuje się i uruchamia od nowa na nowym folderze, więc panel znika na kilka sekund.

Gdy przenoszenie się nie uda, stary folder danych zostaje w użyciu, a moduły startują ponownie. Błąd zadania mówi,
co poszło źle i co stało się z kopią:

- Pełny wolumin albo anulowanie usuwa to, co przenoszenie utworzyło.
- Każdy inny błąd (kopiowania, sprawdzenia, zapisu na dysk) zostawia kopię oznaczoną plikiem `INCOMPLETE` i pokazuje
  ją na stronie Miejsce na dane w części „Kopie pozostałe po nieudanych przeniesieniach”.
- Zatrzymanie pakietu w trakcie przenoszenia zostawia stary folder jako folder danych. Przy następnym starcie bvsd
  zgłasza niedokończone przenoszenie w Dziennikach systemu QTS i pokazuje kopię.

### Stara kopia i kopie pozostałe

Po udanym przenoszeniu stary folder danych zostaje na miejscu, z plikiem `MOVED-TO` w środku. Nic go nie używa.
„Usuń starą kopię” na stronie Miejsce na dane go usuwa, a „Usuń tę kopię” usuwa kopię pozostałą po nieudanym
przenoszeniu. Oba usuwania nie wychodzą poza jeden system plików i nie podążają za dowiązaniami. Folder, który
jest punktem montowania albo samym folderem udostępnionym, zostaje opróżniony i zachowany.

## Sieć

Strona Sieć zawiera porty, tryb zwykłego HTTP, nazwy hostów modułów, zaufane proxy, publiczny URL i certyfikat.
Zapisanie ustawień restartuje to, czego wymaga zmiana, i podaje, co zostało zrestartowane.

### Porty

| Ustawienie | Domyślnie | Co to jest |
|---|---|---|
| HTTPS | 9951 | panel i moduły przez TLS |
| HTTP | 9950 | panel i moduły przez zwykłe HTTP |
| Git przez SSH | 9952 | Git przez SSH |
| Port SSH w adresach klonowania | 0 | port pokazywany w adresach klonowania SSH; 0 oznacza port SSH powyżej. Ustaw go, gdy router przekierowuje na NAS inny port. |
| Host SSH w adresach klonowania | puste | nazwa albo adres pokazywany w adresach klonowania SSH. Puste oznacza nazwę hosta Gita, a bez niej host z publicznego URL-a, a bez niego adres NAS-a w LAN; dla nazwy opublikowanej przez Cloudflare Tunnel adres w LAN i port Git przez SSH. Wpisz nazwę bez proxy (w Cloudflare: „DNS only”) albo adres routera, gdy router przekierowuje port SSH. |

Porty mieszczą się w zakresie od 1024 do 65535 i różnią się od siebie. Porty modułów na 127.0.0.1 (9960
i 9961) są odrzucane, podobnie jak port, którego używa httpd albo serwer runnera CI, gdy 9960 lub 9961 zajął inny
program (komunikat go podaje). Gdy po zapisie nginx zostaje na starych portach, bo nie mógł użyć nowego, powiadomienie
mówi dlaczego. Gdy NAS ma IPv6, nginx nasłuchuje też na nim.

### Zwykłe HTTP

- „Obsługuj” (domyślnie): panel i moduły odpowiadają także przez zwykłe HTTP. Zostaw to, gdy proxy w sieci LAN
  łączy się z portem 9950 przez zwykłe HTTP.
- „Przekieruj na HTTPS”: żądanie przez zwykłe HTTP dostaje przekierowanie (307) na port HTTPS. Żądanie od zaufanego
  proxy, które zgłasza, że jego klient użył HTTPS, jest obsługiwane przez zwykłe HTTP, więc takie proxy nie wpada
  w pętlę.

Strona logowania ostrzega, gdy została otwarta przez zwykłe HTTP: hasło idzie wtedy przez sieć otwartym tekstem.

### Certyfikat

Przy pierwszym starcie bvsd tworzy certyfikat z podpisem własnym (ECDSA P-256) dla:

- nazwy hosta NAS-a, nazw hostów modułów i `localhost`;
- adresów LAN NAS-a i `127.0.0.1`.

Jest ważny 825 dni, najdłużej, ile przyjmują urządzenia Apple, nawet dla certyfikatu zaufanego ręcznie. bvsd sprawdza
go przy starcie i raz dziennie i tworzy nowy, gdy zostało 30 dni lub mniej, gdy zmienia się zestaw nazw albo gdy
klucz nie pasuje. Nowy certyfikat wysyła [powiadomienie](#powiadomienia). Strona Sieć pokazuje jego nazwy, ważność
i odcisk SHA-256: porównaj odcisk, zanim urządzenie zaufa certyfikatowi.

„Dodaj własny certyfikat” przyjmuje łańcuch certyfikatów i klucz prywatny w formacie PEM:

- najpierw certyfikat serwera, potem certyfikaty pośrednie;
- klucz bez hasła (nginx startuje sam, bez wpisywania hasła);
- klucz musi pasować do pierwszego certyfikatu, każdy certyfikat musi być podpisany przez następny, żaden nie może
  być wygasły, a pierwszy musi wymieniać swoje hosty jako alternatywne nazwy podmiotu (SAN).

Para jest zapisywana w `etc/tls/` w folderze danych z trybem 600. Gdy dodany certyfikat wygaśnie albo przestanie
przechodzić te sprawdzenia, nginx używa w zamian certyfikatu z podpisem własnym, a powiadomienie mówi dlaczego.
Dodany certyfikat, któremu zostało 30 dni lub mniej, także wysyła powiadomienie. „Nowy certyfikat z podpisem
własnym” tworzy nową parę z podpisem własnym i wraca do niej.

Klienci, którzy przychodzą przez proxy, widzą certyfikat proxy, nie ten.

**Pobierz certyfikat** na stronie Sieć zapisuje certyfikat, który podaje panel, bez klucza, jako `<nazwa>.crt`.
Dodaj go do zaufanych certyfikatów urządzenia, a przeglądarki tego urządzenia zaufają panelowi; potrzebują tego widoki
rozszerzeń w edytorze ([Rozszerzenia](#rozszerzenia)). Nowemu certyfikatowi z podpisem własnym trzeba zaufać ponownie.

### Zaufane proxy

Lista adresów IP albo zakresów CIDR, na przykład `192.168.1.5`, adres proxy przed NAS-em. Tylko dla żądania,
którego połączenie TCP przychodzi z jednego z nich, nginx bierze:

- adres klienta z `X-Forwarded-For`;
- schemat, którego użył klient, z `X-Forwarded-Proto` (liczą się tylko `http` i `https`).

Żądanie z każdego innego adresu liczy się jako przychodzące wprost od klienta, a nginx zastępuje jego nagłówki
`X-Forwarded-*` własnymi wartościami. Wpisanie zakresu sprawia, że zaufany jest każdy host w tym zakresie: każdy
z nich może wtedy podać adres klienta i schemat, więc wpisz adres samego proxy. Zakres obejmujący wszystkie adresy
(`0.0.0.0/0`, `::/0`) jest odrzucany.

### Publiczny URL

Sam adres, na przykład `https://git.example.com`, bez ścieżki. Części o modułach mówią, które odnośniki od niego się
zaczynają.

## Za odwrotnym proxy (Plesk)

Zwykle TLS kończy się na proxy z publicznym certyfikatem, a proxy łączy się z portem 9950 w sieci LAN przez zwykłe
HTTP. Przykłady poniżej używają `bvs.example.com` jako nazwy publicznej, `192.168.1.20` jako adresu NAS-a
i `192.168.1.5` jako adresu serwera Plesk; wstaw w ich miejsce własne.

```mermaid
flowchart LR
    client["Przeglądarka, git, klient WebDAV"] -->|"HTTPS 443"| plesk["nginx Pleska<br/>bvs.example.com<br/>publiczny certyfikat"]
    plesk -->|"HTTP 9950<br/>Host, X-Forwarded-For,<br/>X-Forwarded-Proto: https,<br/>Upgrade dla WebSocket"| nas["nginx na NAS-ie<br/>192.168.1.20"]
    nas --> panel["/ panel, /api/ bvsd"]
    nas --> mods["/git/, /svn/, /bucket/, /ide/"]
    sshc["git przez SSH"] -->|"TCP 9952, z pominięciem proxy"| nas
```

### Na NAS-ie

1. Na stronie Sieć dodaj adres serwera Plesk (taki, jaki widzi NAS) do listy Zaufane proxy.
2. Zwykłe HTTP może zostać na „Obsługuj”. „Przekieruj na HTTPS” też działa, gdy serwer Plesk jest zaufanym proxy.

### W Plesku

Wpisz te dyrektywy w ustawieniach Apache i nginx domeny, w polu „Dodatkowe dyrektywy nginx”:

```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: edytor i jego terminal
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;

    # duże wypchnięcia i wysyłki: bez limitu rozmiaru, strumieniowo, dopuszczalna godzina ciszy
    client_max_body_size 0;
    proxy_request_buffering off;
    proxy_buffering off;
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}
```

`location ~ ^/` to lokalizacja z wyrażeniem regularnym. nginx przedkłada pasujące wyrażenie regularne nad zwykłe
`location /` (Plesk zapisuje własne), więc ten blok obsługuje każdą ścieżkę witryny, a `proxy_pass` bez ścieżki
przekazuje ścieżkę żądania bez zmian. Na tej samej stronie ustawień wyłącz „Serwuj pliki statyczne bezpośrednio
przez nginx”: gdy jest włączone, Plesk odpowiada na żądania plików `.js`, `.css` i obrazów z folderu domeny i panel
się nie wczytuje.

W ustawieniach hostingu domeny włącz stałe przekierowanie 301 z HTTP na HTTPS (w angielskim interfejsie
„Permanent SEO-safe 301 redirect from HTTP to HTTPS”). Dodatkowe dyrektywy nginx obowiązują też w witrynie domeny
na zwykłym HTTP (port 80); bez przekierowania witryna odpowiada także tam, a hasła idą przez internet
niezaszyfrowane.

Po co jest każdy wiersz:

| Dyrektywa | Co się psuje bez niej |
|---|---|
| `Host $http_host` | nginx na NAS-ie wybiera blok serwera po `Host`, więc nazwa hosta modułu nigdy nie pasuje. bvsd porównuje `Origin` strony z `Host` razem z portem, więc każda zmiana w panelu kończy się `403 csrf`; `$host` gubi publiczny port inny niż 443 i kończy się tak samo. |
| `X-Forwarded-For` | każdy klient liczy się jako serwer Plesk: pięć błędnych haseł jednej osoby blokuje wszystkich na 15 minut |
| `X-Forwarded-Proto $scheme` | ciasteczko sesji nie dostaje flagi `Secure`, a w trybie „Przekieruj na HTTPS” każde żądanie idzie na port 9951 nazwy publicznej |
| `Upgrade`, `Connection` | strona edytora się otwiera, ale edytor i jego terminal nigdy się nie łączą |
| `client_max_body_size 0`, `proxy_request_buffering off`, limity czasu | do dużych wypchnięć i wysyłek stosuje się własny limit rozmiaru i limity czasu proxy |

`X-Forwarded-For` i `X-Forwarded-Proto` liczą się tylko wtedy, gdy serwer Plesk jest na liście Zaufane proxy.
W przeciwnym razie nginx na NAS-ie je pomija.

### Git przez SSH

Proxy HTTP nie przenosi SSH. Git przez SSH łączy się bezpośrednio z portem 9952 NAS-a albo przez przekierowanie
portu TCP na routerze. „Port SSH w adresach klonowania” na stronie Sieć ustawia port, który pokazują adresy
klonowania, a „Host SSH w adresach klonowania” nazwę albo adres, gdy publiczna nazwa prowadzi tylko do proxy.

### Sprawdzenie konfiguracji

- Panel otwiera się pod `https://bvs.example.com/`, a logowanie i zapisanie ustawienia działają.
- Narzędzia deweloperskie przeglądarki pokazują ciasteczko sesji `bvs_session` z flagą `Secure`.
- Strona Logi pokazuje `nginx-access`: pierwsze pole każdego wiersza to adres klienta, nie serwera Plesk.

## Nazwy hostów modułów

Moduł może mieć własną nazwę hosta (strona Sieć, Nazwy hostów). nginx udostępnia wtedy pod główną ścieżką tej nazwy
tylko ten moduł. Nazwa musi wskazywać na NAS albo na proxy przed nim. Na proxy każda nazwa hosta potrzebuje własnej
witryny z tymi samymi dyrektywami co wyżej.

| Moduł | Pod główną ścieżką swojej nazwy | Dodatkowo |
|---|---|---|
| Git | Forgejo | `/git/` na każdej innej nazwie odpowiada 302 na nazwę hosta Gita, z tą samą ścieżką, zapytaniem i portem |
| Edytor | code-server | strona logowania panelu i `/api/`, żeby można było zalogować się pod tą nazwą |
| SVN i Bucket | httpd | repozytoria zostają pod `/svn/<nazwa>`, a buckety pod `/bucket/<nazwa>` |

Wyłączony moduł odpowiada `503` ze zdaniem, które to mówi, na swoich ścieżkach i pod swoją nazwą hosta.

### Jedno logowanie dla wszystkich nazw

Ciasteczko sesji należy do nazwy, pod którą je ustawiono, chyba że ustawienie `network.cookie_domain` wskazuje
domenę nadrzędną wszystkich nazw hostów modułów. Przy `example.com` logowanie pod `bvs.example.com` sięga też
`git.example.com` i `ide.example.com`. bvsd odrzuca sufiks publiczny (`com`, `com.pl`, `co.uk`, `myqnapcloud.com`),
domenę, która nie jest nadrzędna dla każdej nazwy hosta modułu, i każdą domenę, dopóki żaden moduł nie ma nazwy
hosta. Ciasteczko dostaje każda witryna w tej domenie, więc ustaw najwęższą wspólną domenę nadrzędną.

Strona Sieć nie pokazuje tego ustawienia. Admin NAS-a ustawia je przez gniazdo CLI programem `curl` z pakietu:

```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
```

Pusta wartość (`""`) znów przypisuje ciasteczko do każdej nazwy osobno.

## Publikacja w internecie (Cloudflare Tunnel)

Strona **Publikowanie** (Administracja, tylko dla administratorów) udostępnia pakiet w internecie pod nazwami z domeny,
której DNS jest w Cloudflare. `cloudflared`, który jest w pakiecie, sam łączy się z NAS-a z Cloudflare: na routerze
nie otwiera się żadnego portu, a certyfikaty dla tych nazw ma Cloudflare. Każde żądanie przechodzi przez nginx
pakietu: panel i jego API wymagają zalogowania, a bez konta otwiera się tylko to, co upubliczniłeś (publiczne
repozytoria, ich widok WWW, repozytorium pakietów).

Są dwa typy nazw, najwyżej po jednej:

| Typ | Przykład | Co udostępnia |
|---|---|---|
| Główna nazwa hosta | `code.example.com` | panel i jego API, Forgejo pod `/git/`, widok WWW pod `/svn-web/` i `/bucket-web/`, repozytorium pakietów pod `/repo/` (tylko odczyt) i przewodnik. `/svn/` i `/bucket/` odpowiadają tam 404, a `/ide/` też, dopóki nie opublikujesz edytora (niżej) |
| Nazwa hosta pakietów | `qpkg.example.com` | tylko GET i HEAD dla `/repo/`, dla App Center na innych NAS-ach; na resztę odpowiada 404 |

### Czego potrzebuje token

Utwórz token API w dash.cloudflare.com (My Profile, API Tokens) z tymi uprawnieniami, dla strefy tych nazw:

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

Pakiet trzyma token na NAS-ie w pliku, który czyta tylko root, i już go nie pokazuje.

Jeśli token ma Client IP Address Filtering, wpisz tam oba publiczne adresy NAS-a, IPv4 i IPv6: NAS łączy się z Cloudflare przez IPv6, gdy ma adres IPv6. Inaczej strona Publish odmawia z kodem `token_ip_filter` i podaje adres, który zobaczył Cloudflare.

### Publikowanie

Wpisz nazwy i token, potem **Opublikuj**. Pakiet najpierw sprawdza wszystko w Cloudflare i niczego tam nie zmienia,
gdy któreś sprawdzenie się nie powiedzie; komunikat mówi, co poprawić:

- brakujące uprawnienie tokenu, po nazwie;
- nazwę w strefie, której token nie widzi;
- rekord DNS, który nazwa już ma (jego typ i dokąd wskazuje): usuń go w Cloudflare albo wybierz inną nazwę;
- główna nazwa hosta przy wyłączonym **Publicznym przeglądaniu** (strona Git), bo jego goście nie mają konta;
- nazwy w strefach dwóch kont Cloudflare.

Potem tworzy tunel (o nazwie `bucketvs-<nazwa NAS-a>`, chyba że podasz inną; tunel o tej nazwie, którego ustawienia
trzyma Cloudflare, zostaje przejęty), kieruje na niego każdą nazwę rekordem CNAME przez proxy, dodaje reguły pamięci
podręcznej i uruchamia cloudflared. **Opublikuj ponownie** z tymi samymi wartościami niczego nie zmienia. Ustaw adres
publiczny (Sieć) na adres `https://` głównej nazwy hosta, żeby linki Forgejo i adresy klonowania HTTPS go podawały; strona
przypomina o tym, dopóki się różnią.

Git przez SSH nie przechodzi przez tunel: do opublikowanej nazwy Cloudflare przenosi tylko HTTP. Dlatego adresem
klonowania SSH jest adres NAS-a w LAN z portem Git przez SSH (9952), w panelu i w okienku klonowania samego Forgejo,
a strona Git i Przegląd piszą: „Klonowanie przez SSH działa w sieci lokalnej; z internetu klonuj przez HTTPS.” Żeby
SSH działał z internetu, przekieruj port SSH na routerze i ustaw **Host SSH w adresach klonowania** (Sieć) na
publiczny adres routera albo na nazwę bez proxy (w Cloudflare: „DNS only”), która na niego wskazuje, a **Port SSH w
adresach klonowania**, gdy router używa innego portu.

Główna nazwa hosta odpowiada na `/ide/` kodem 404, dopóki admin nie włączy przełącznika **Opublikuj edytor pod główną nazwą hosta** na stronie **Publikowanie**.
Terminal edytora to powłoka roota na NAS-ie i dostaje ją każdy, kto zaloguje się tam z internetu jako programista lub
admin, więc najpierw włącz uwierzytelnianie dwuskładnikowe na tych kontach. Po włączeniu edytor przechodzi przez to samo logowanie
co w sieci LAN, jego proxy portów (`/ide/proxy/<port>/`) pozostaje tam wyłączone, a Cloudflare nigdy nie trzyma jego
stron w pamięci podręcznej. Dopóki przełącznik jest włączony, Przegląd pokazuje **Edytor w internecie**.

Gdy moduł CI jest włączony, workflow wypchnięty do dowolnego repozytorium tego Forgejo działa jako root na NAS-ie
przez runner samego NAS-a, niezależnie od tego, czy wypchnięto go z internetu, czy z sieci lokalnej: to ta sama władza
co terminal edytora. Strona **Publikowanie** mówi o tym pod **Co widać z internetu**. Dostęp do zapisu dawaj tylko osobom, którym
dałbyś hasło admina NAS-a.

### Pamięć podręczna

Przy włączonych **Gotowych regułach pamięci podręcznej** Cloudflare trzyma pakiety (`*.qpkg`) przez rok, ikony przez
dzień, `repo.xml` przez 5 minut i pliki panelu przez rok; nigdy nie trzyma API, stron Forgejo, stron logowania ani
widoku WWW. Publikacja w repozytorium pakietów od razu usuwa `repo.xml` (i zmienioną ikonę albo pliki usuniętej
wersji) z pamięci podręcznej Cloudflare. Pakiet dodaje własne reguły na końcu reguł pamięci podręcznej strefy i nigdy
nie zmienia pozostałych.

### Logowanie z internetu

Pierwszego hasła nie da się ustawić przez opublikowaną nazwę: pierwsze uruchomienie przyjmuje tylko klienta z sieci
lokalnej. Błędne hasła liczą się osobno dla każdego klienta z internetu, jak w sieci lokalnej. Liczy się też hasło
wysłane przez git do Forgejo; token dostępu Forgejo działa jak zawsze. Cloudflare przyjmuje żądania do 100 MB, więc
większy push albo wysyłka idzie przez adres w sieci lokalnej.

### Zatrzymanie i usunięcie

**Zatrzymaj** kończy cloudflared; nazwy pokazują wtedy stronę błędu Cloudflare, a w Cloudflare nic się nie zmienia.
**Usuń z Cloudflare** usuwa rekordy DNS, reguły pamięci podręcznej i tunel, które pakiet utworzył (przejęty tunel
zostaje), i zapomina token. Dopóki nazwy są opublikowane, Przegląd pokazuje plakietkę **Internet** ze stanem tunelu.

## Moduły

Przegląd pokazuje każdy moduł z jego procesami, pamięcią i CPU oraz włącza go i wyłącza (tylko admin). Wyłączony moduł
mówi, co daje; admin włącza go jego przyciskiem (**Włącz Git**, **Włącz Edytor** itd.) na jego kafelku, na jego stronie,
a Edytor także w karcie Edytor.

- Włączenie modułu to zadanie: moduł się przygotowuje, przełącznik jest zapisywany, a procesy startują. Zadanie
  czeka na nie do 30 sekund.
- CI wymaga Gita: Gita nie da się wyłączyć, dopóki CI jest włączone.
  Gdy Git jest wyłączony, przycisk CI brzmi **Włącz Git i CI** i najpierw włącza Gita.
- Wyłączony moduł nie uruchamia żadnego procesu i nie zajmuje pamięci.
- Rdzeń działa zawsze.

| Stan | Znaczenie |
|---|---|
| działa | działa każdy proces modułu |
| uruchamia się | proces wystartował niecałą sekundę temu |
| z problemami | proces czeka na restart, pliki modułu nie dały się zapisać albo nie przechodzi sprawdzenie jego stanu |
| restartuje się w pętli | proces zakończył się 5 razy w ciągu 10 minut; bvsd uruchamia go ponownie co 15 minut, a jedno powiadomienie już wyszło |
| zatrzymany | procesy modułu nie działają (zatrzymało je przenoszenie danych albo nie wystartowały) |
| wyłączone | moduł jest wyłączony |

Proces, który się kończy, startuje ponownie po 1 sekundzie, potem po 2, 4 i tak dalej aż do 60 sekund. Po
10 minutach działania czekanie znów zaczyna się od 1 sekundy. Wyłączenie i ponowne włączenie modułu od razu
uruchamia proces, który restartuje się w pętli.

## Git

Moduł Git uruchamia Forgejo: interfejs WWW do repozytoriów git ze zgłoszeniami, pull requestami, wydaniami, wiki, LFS,
kopiami lustrzanymi i webhookami. Admin włącza go przyciskiem **Włącz Git** w Przeglądzie albo na stronie Git. Zadanie
przygotowuje foldery i bazę Forgejo w `<data>/forgejo`, zakłada w Forgejo każde konto panelu i uruchamia go. Forgejo
otwiera się potem pod `/git/` na adresie panelu albo pod `/` nazwy hosta Gita, gdy jest ustawiona (zob. [Nazwy hostów
modułów](#nazwy-hostów-modułów)).

**Nowe repozytorium** na stronie Git, a dopóki nie ma żadnego, także na kafelku Git w Przeglądzie, otwiera formularz
Forgejo do tworzenia repozytorium.
Panel pokazuje ten przycisk i **Migracja lub mirror repozytorium** od roli developer w górę: konto z rolą viewer ani z rolą użytkownika
nie może w Forgejo tworzyć repozytoriów.

### Repozytoria i konta

- **Jedno logowanie.** Forgejo nie ma własnego logowania: `/git/user/login` prowadzi do logowania panelu, z kodem
  z aplikacji uwierzytelniającej, jeśli konto go ma, a potem z powrotem do Forgejo.
- **Konta pochodzą z panelu.** Każde konto panelu ma w Forgejo konto o tej samej nazwie i tym samym haśle. Rola
  `admin` to administrator serwisu w Forgejo. Konto wyłączone albo takie, które musi najpierw zmienić hasło, nie
  zaloguje się też do Forgejo. Zmiana w panelu dociera do Forgejo w ciągu kilku sekund. Strony Konta i Git
  pokazują stan każdego konta w Forgejo, a **Synchronizuj teraz wszystkie konta** na stronie Git uruchamia
  synchronizację ponownie.
- **Usunięcie konta** w panelu wyłącza jego konto w Forgejo: nie da się na nie zalogować, jego tokeny i klucze SSH
  są usuwane, ale repozytoria zostają. Tej nazwy nie można nadać nowemu kontu panelu, dopóki administrator nie zmieni
  nazwy starego konta Forgejo albo go nie usunie w administracji serwisu Forgejo (**Konta użytkowników**).
- **Konta założonego w samym Forgejo** panel nigdy nie zmienia. Konto panelu o tej samej nazwie pokazuje powód i
  czeka, aż jedno z nich dostanie inną nazwę.
- **Uwierzytelnianie dwuskładnikowe a git.** Dla konta z włączonym uwierzytelnianiem dwuskładnikowym Forgejo nie przyjmuje hasła. git
  przez HTTP lub HTTPS potrzebuje wtedy tokenu dostępu Forgejo (**Ustawienia**, **Aplikacje** w Forgejo) zamiast
  hasła. Klucze SSH działają jak dotąd.
- **Repozytoria** zakłada się, importuje i ustawia jako kopie lustrzane na stronach samego Forgejo. Import z adresów
  w sieci lokalnej jest domyślnie dozwolony (ustawienie `git.allow_local_networks`), bo NAS zwykle kopiuje z własnej
  sieci.

### Klonowanie przez HTTPS i SSH

Na stronie Git sekcja **Sklonuj repozytorium** podaje oba polecenia dla właściciela i repozytorium:

| Sposób | Adres | Logowanie |
|---|---|---|
| HTTPS (lub HTTP) | `https://<adres NAS-a>:9951/git/<właściciel>/<repo>.git` | nazwa i hasło z panelu albo token dostępu |
| SSH | `ssh://git@<adres NAS-a>:9952/<właściciel>/<repo>.git` | klucz SSH dodany w Forgejo (**Ustawienia**, **Klucze SSH / GPG**) |

- Z publicznym adresem (za proxy) adres HTTPS go używa; z nazwą hosta Gita ścieżka to `/<właściciel>/<repo>.git`
  na tej nazwie.
- Port SSH to `network.ssh_port` (9952) albo `network.public_ssh_port`, gdy router przekierowuje na niego inny port.
  Proxy nie przenosi SSH: port trzeba przekierować na routerze (zob. [Git przez SSH](#git-przez-ssh)).
- Host SSH to `network.public_ssh_host`, gdy jest ustawiony, a bez niego nazwa hosta Gita albo host z publicznego
  URL-a. Nazwa opublikowana przez Cloudflare Tunnel nie przenosi SSH, więc dla niej adresem jest adres NAS-a w LAN
  na porcie Git przez SSH ([Publikacja w internecie](#publikacja-w-internecie-cloudflare-tunnel)).
- Duże wypchnięcia nie są przerywane: nginx nie ustawia na `/git/` limitu rozmiaru.

### Pokazywanie repozytoriów osobom bez konta

**Publiczne przeglądanie** na stronie Git pozwala każdemu, kto dotrze do Forgejo, czytać jego publiczne repozytoria
bez konta. W Forgejo każde repozytorium jest publiczne albo prywatne (jego **Ustawienia**, **Widoczność**). Przy
włączonym przełączniku gość bez konta może:

- otworzyć publiczne repozytorium, jego foldery i pliki oraz pobrać plik w oryginalnej postaci;
- czytać historię, otworzyć commit z jego różnicami (także jako plik `.diff` albo `.patch`) i porównać dwa commity;
- zobaczyć wydania, pobrać archiwum i sklonować repozytorium przez HTTP
  (`git clone https://<adres NAS-a>:9951/git/<właściciel>/<repo>.git`);
- znaleźć publiczne repozytoria w **Eksploruj** i na profilu ich właściciela.

Prywatne repozytorium pozostaje ukryte: jego adres odpowiada „nie znaleziono”. Przy włączonym przełączniku Forgejo
nie pokazuje też listy kont (karta **Użytkownicy** w Eksploruj i wyszukiwanie użytkowników są wyłączone, także dla
zalogowanych), więc gość nie zbierze nazw kont. Wypychanie, zgłoszenia, pull requesty i ustawienia nadal wymagają
konta. Przy wyłączonym przełączniku każda strona najpierw prosi o zalogowanie.

Żeby udostępnić repozytorium, oznacz je w Forgejo jako publiczne, włącz **Publiczne przeglądanie** i wyślij adres. Na
nazwie hosta Gita adres to `https://<nazwa hosta Gita>/<właściciel>/<repo>`.

### Nowy projekt

**Nowy projekt** to strona, która krok po kroku prowadzi projekt od zera do zbudowanej i opublikowanej pierwszej
wersji. Otwiera się we własnej karcie z palety poleceń (Ctrl+Shift+P, **Nowy projekt**), z góry paska bocznego
Edytora, z nagłówka paska bocznego Git, ze strony Git i ze strony Projekty; przykład z kategorii **Projekty edytora** na stronie Przykłady ma
**Nowy projekt z niego**. Wymaga roli developer, modułów Git i Edytor oraz twojego konta w Forgejo (stronę Git pokazuje
stan jego synchronizacji). Gdy Git lub Edytor jest wyłączony, strona mówi o tym nad krokami, a admin włącza je tam jednym
przyciskiem.

1. **Szablon.** Szablony są pogrupowane: Programy (C, C++ z CMake, Go, Rust, Node, Python, Java), Pakiety dla QTS
   (pakiet QPKG, aplikacja App Buildera, pakiet ze źródeł), Jądro (moduł jądra, sterownik USB-serial ch341), Strony
   WWW (statyczna strona: strony w `src/`, Makefile budujący `public/`, sprawdzanie linków jako `make test`) i
   Przykłady. Każda karta mówi, co powstaje z budowania, i zaznacza, czego temu NAS-owi jeszcze brakuje (QDK, środowiska
   budowania, wyłączonego modułu), z linkiem do strony, która to dodaje.
2. **Nazwa i widoczność.** Nazwa jest nazwą repozytorium i folderu `projects/<nazwa>`; strona sprawdza ją w trakcie
   pisania. Dalej właściciel (twoje konto albo organizacja, w której Forgejo pozwala ci tworzyć repozytoria),
   prywatne lub publiczne, i opis, jeśli chcesz. Dla aplikacji App Buildera preset, od którego zaczyna, i cel.
3. **Usługa**, dla pakietu QPKG i pakietu ze źródeł: jak pakiet się uruchamia i zatrzymuje, złożone z bloków. Wybierz
   preset (**Narzędzie wiersza poleceń** dowiązuje program w `/usr/bin`; **Usługa w tle** dodaje konto, folder danych
   na woluminie, program uruchamiany jako to konto, czekanie na jego port i zatrzymanie z limitem czasu; **Aplikacja
   WWW** dodaje przycisk Otwórz w App Center; **Pusty** ma tylko polecenia), potem zmień jego bloki. Karta każdego
   bloku pokazuje wiersze, które zapisuje w skrypcie, już w trakcie pisania, a odrzucona wartość jest zaznaczona pod
   swoim polem. Uruchomienie nigdy nie włącza Enable w App Center, a zatrzymanie nigdy go nie zmienia. Widok projektu
   zmienia bloki później.
4. **Budowanie.** Środowisko, w którym projekt się buduje, zapamiętane dla projektu (widok Projektu zmienia je później),
   albo żadne, a wtedy bvsd wybiera je przy każdym budowaniu. Krok najpierw proponuje środowisko, które wybrałby sam
   bvsd i w którym budują `bvs qbuild` oraz CI: Ubuntu 24.04 builder, bez niego Debian 12 builder, a bez obu inne
   środowisko Debian lub Ubuntu. Gdy Makefile danego szablonu podaje środowisko każdej aplikacji w wierszu ENV, krok
   mówi o tym i o żadne nie pyta, a projekt żadnego nie zapamiętuje. Dla pakietu ze źródeł krok mówi też, że adres
   źródła i sha256 w przepisie startowym są zastępcze: pierwsze budowanie kończy się błędem, dopóki przepis nie poda
   prawdziwego źródła twojego programu i sha256 tego pliku. Strona pokazuje workflow, który CI uruchamia przy każdym
   pushu. **Zbuduj zaraz po utworzeniu** jest włączone, dopóki go nie wyłączysz; przy pakiecie ze źródeł jest na początku
   wyłączone i możesz je włączyć. Gdy na NAS-ie nie ma środowiska Debian ani Ubuntu, krok to mówi: admin tworzy tam
   środowisko do budowania przyciskiem **Utwórz to środowisko**, a krok je potem proponuje; w przeciwnym razie wyłącz
   **Zbuduj zaraz po utworzeniu** i zbuduj projekt później.
5. **Publikowanie**, dla pakietu QPKG, aplikacji App Buildera albo strony WWW: **Wdrożenie testowe na tym NAS-ie**
   (admin, tylko pakiety podpisane; dla aplikacji **Aktywuj na tym NAS-ie**), **Opublikuj w repozytorium App Center tego
   NAS-a** i **Opublikuj na moim serwerze WWW** (admin) w jednym z [celów WWW](#własny-serwer-www-cele-www); **Dodaj
   cel** zapisuje tam nowy. Każde jest opcjonalne. Wybór publikacji staje się też przełącznikiem projektu **Opublikuj w
   repozytorium App Center** (widok projektu, Ustawienia): dopóki jest włączony, CI projektu publikuje tam przy każdym
   przebiegu.
6. **Podsumowanie** wymienia wszystkie odpowiedzi i kroki, które zostaną uruchomione. **Utwórz projekt** je wysyła.
7. **Postęp** śledzi zadanie każdego kroku: repozytorium i jego pierwszy commit, pierwsze budowanie, potem wdrożenie
   testowe, publikację w App Center i publikację w sieci, jeden po drugim. Wiersz pokazuje swój stan i koniec logu;
   kliknięcie pokazuje log. Nieudany krok mówi dlaczego i oferuje **Ponów**, a kolejne kroki czekają. Na końcu
   **Otwórz projekt**, **Otwórz w edytorze** i **Otwórz w Forgejo**. Przeładowanie strony lub powrót do jej karty
   odczytuje zadania ponownie i żaden krok nie jest wysyłany dwa razy. Opuszczenie strony zatrzymuje łańcuch; widok
   Projektu ma te same kroki.

Gdy bvsd odmawia utworzenia projektu (nazwa zajęta przez folder, aplikację lub repozytorium, właściciel, w którym nie
możesz tworzyć), strona wraca do kroku z tą odpowiedzią, pokazuje powód pod jej polem i zachowuje wszystkie inne
odpowiedzi. Wyłączony moduł (admin włącza go ze strony), brak konta w Forgejo i brak środowiska Debian lub Ubuntu dla
aplikacji App Buildera są objaśnione w Przeglądzie.

Co robi pierwsze zadanie:

- Tworzy repozytorium jako ty, prywatne, chyba że wybrałeś publiczne.
- Tworzy folder `projects/<nazwa>` w folderze danych z pierwszym commitem: wszystkimi plikami szablonu i workflow
  `.forgejo/workflows/build.yml`, na `main`. Commit jest twój (twoje imię i adres są ustawione jako autor w
  folderze), a skrypty szablonu są w nim wykonywalne. Wypycha ten commit do repozytorium jako ty. `origin` to adres
  HTTPS repozytorium, więc twój następny push trafia tam. Commit zawiera też [skill dla asystentów
  AI](#programowanie-z-asystentem-ai), `.claude/skills/qnap-dev/` i `AGENTS.md`. Folder jest we wspólnym folderze
  edytora: edytor każdego dewelopera otwiera `projects/`, niezależnie od tego, czy repozytorium jest prywatne, czy
  publiczne.
- Zapamiętuje dla projektu wybrane środowisko, chyba że Makefile danego szablonu podaje środowisko każdej aplikacji. Dla
  aplikacji App Buildera tworzy aplikację o nazwie projektu z recepturami presetu i wiąże ją z projektem, więc aplikacja
  może budować także receptury dodane do folderu `recipes/` projektu. Zapamiętuje też twój wybór publikacji jako
  przełącznik projektu Opublikuj w repozytorium App Center, zanim pierwszy przebieg workflow go odczyta.

Workflow działa na runnerze NAS-a przy każdym pushu, gdy moduł CI jest włączony, w środowisku wskazanym w workflow.
Workflow QPKG publikuje pakiet w [repozytorium pakietów](#repozytorium-pakietów), które przyjmuje tylko pakiety
podpisane kodem. Jeśli pierwszy commit lub jego push się nie uda po utworzeniu repozytorium, zadanie usuwa folder,
ponownie usuwa repozytorium i podaje przyczynę. Gdy przełącznik projektu **Opublikuj w repozytorium App Center** jest
wyłączony, `bvs repo publish` w workflow wypisuje komunikat zamiast publikować, a przebieg trwa dalej.

### Widok projektu

Każdy folder `projects/` w folderze danych ma własną stronę: projekt z kreatora, kopię przykładu z Projektów edytora albo repozytorium,
które tam sklonowałeś. Otwiera się z gałęzi **Projekty** paska bocznego Edytora, ze strony **Projekty**, z **Otwórz
projekt** na końcu Nowego projektu i z palety poleceń (**Otwórz projekt …** albo Ctrl+P i nazwa). Wymaga roli
dewelopera. Menu projektu na stronie Projekty ma też Zbuduj, Zbuduj ponownie, Opublikuj i publikację w celach WWW. To
samo menu włącza i wyłącza publikowanie przez CI w App Center.

Strona czyta folder i sama niczego w nim nie zmienia. Typ projektu ustala z jego plików: aplikacja App Buildera o
nazwie folderu, `qpkg.cfg` na górze (pakiet QPKG), `Makefile` na górze albo nic z tego, gdy projekt buduje jego
workflow CI. Gdy aplikacja App Buildera ma nazwę folderu, projekt z `qpkg.cfg` na górze albo z Makefile i `qpkg.cfg` w
`qpkg/` zachowuje własny sposób budowania, a Wdrożenie testowe i Publikowanie dalej biorą jego QPKG.

Pasek na górze zawiera kroki. Krok, który nie może się uruchomić, zachowuje swój przycisk, wyłączony, i mówi dlaczego
obok niego i pod paskiem. Krok, którego projekt w ogóle nie może wykonać, znika z paska, a jedno zdanie pod nim mówi, co
projekt tworzy i gdzie trafia wynik budowania: do folderu projektu dla programu, na stronę Jądra dla modułu jądra, na
stronę każdej aplikacji dla Makefile, który buduje tylko aplikacje App Buildera.

- **Zbuduj** działa jako zadanie w dolnym panelu. Pakiet QPKG buduje się przez `qbuild` z QDK, projekt z Makefile przez
  `make`, oba w środowisku budowania projektu. Log wymienia każdy plik `.qpkg`, który budowanie zapisało w `build/`, i czy ma
  podpis kodu. Aplikacja App Buildera buduje się jak na swojej stronie. Gdy domyślny cel Makefile uruchamia tylko
  wiersze `./bvsapp.sh create` i `./bvsapp.sh build`, Zbuduj robi to samo przez App Buildera: tworzy każdą aplikację z
  wiersza `create`, której jeszcze nie ma, a potem buduje aplikacje jedną po drugiej. Wiersz `create` ma postać
  `./bvsapp.sh create NAZWA PLIK-RECEPTUR [ENV] [LINK] [TARGET] [OTHERS]`. Taki Makefile wymienia swój domyślny cel i
  jego wymagania w `.PHONY` i zawiera tylko proste przypisania, każdą zmienną ustawioną raz, oraz proste reguły;
  Makefile z czymkolwiek innym (warunkiem, dołączeniem pliku) buduje się przez make. TARGET to `native`, `aarch64` albo
  `armv7`, więc jedno Zbuduj może zrobić aplikację dla tego NAS-a i drugą dla ARM 64-bit. OTHERS mówi, co aplikacja
  statyczna robi z recepturami, których nie da się zlinkować statycznie (`refuse` albo `bundle`). Tak utworzona
  aplikacja bierze też własne receptury projektu z `recipes/`, chyba że nazwa folderu nie jest nazwą projektu (na
  przykład ma spację). Gdy aplikacja o tej nazwie już istnieje z innym celem albo z innym projektem, a folder ma własne
  receptury, Zbuduj pozostaje wyłączone, a strona mówi, jak to naprawić. Przed pierwszym budowaniem krok podaje nazwy
  aplikacji, które buduje. Projekt z Makefile i pakietem w `qpkg/` tworzy plik pakietu przez `make qpkg` w terminalu
  edytora; dopóki go nie ma, Wdrożenie testowe, Publikowanie i Publikacja w sieci mówią „Wymaga pliku pakietu”.
- **Zbuduj ponownie** buduje od zera: `make clean`, potem `make`; dla pakietu QPKG najpierw znikają stare pliki `.qpkg`
  z `build/`. Makefile bez celu `clean` nie pozwala zbudować ponownie, a strona to mówi. Aplikacja App Buildera nie ma
  Zbuduj ponownie, podobnie jak Makefile, który tylko tworzy i buduje aplikacje App Buildera.
- **Eksportuj jako QPKG** (admin) jest dla aplikacji App Buildera, jak na stronie aplikacji. Publikowanie czeka wtedy na
  eksport, jak na stronie aplikacji, a **Otwórz aplikację** w nagłówku otwiera tę stronę.
- **Wdrożenie testowe** jest dla adminów. Instaluje najnowszy `.qpkg` na tym NAS-ie przez `qpkg_cli`, tak jak App
  Center instaluje plik, a zadanie czeka, aż App Center to potwierdzi. Pakiet zostaje zainstalowany, dopóki nie
  usuniesz go w App Center. Pakiet bez podpisu kodu jest odrzucany: wdrożenie testowe instaluje tylko pakiety
  podpisane i nigdy nie pomija kontroli podpisu App Center (w samym App Center właściciel NAS-a może dopuścić
  aplikacje bez podpisu; ta strona tego nie robi). Podpisz go certyfikatem w plikach (przypadek 1 w [Podpisywanie kodu
  QPKG](#podpisywanie-kodu-qpkg)). Pakiet samego Bucket & Code Server IDE jest odrzucany, bo jego instalacja
  zastąpiłaby działający pakiet. Dla aplikacji App Buildera wdrożenie testowe aktywuje jej zbudowaną wersję na tym NAS-ie.
- **Opublikuj** umieszcza najnowszy `.qpkg` w [repozytorium pakietów](#repozytorium-pakietów) na jego zasadach:
  publikacja admina staje się wersją na liście, publikacja dewelopera czeka na admina, a pakiet bez podpisu kodu jest
  odrzucany, chyba że repozytorium przyjmuje pakiety niepodpisane. Linia pod nim mówi, czy CI projektu też publikuje w
  repozytorium App Center; ten przycisk publikuje w obu przypadkach.
- **Publikacja w sieci** (admin) kopiuje najnowszy `.qpkg` albo folder strony `public/` (inaczej `dist/`) do [celu
  web](#własny-serwer-www-cele-www) i odczytuje go z powrotem przez adres publiczny celu. Dla strony WWW utworzonej
  kreatorem krok czeka na pierwsze budowanie, które zapisuje `public/`.

Projekt z Makefile wybiera cel obok **Zbuduj** (domyślny cel make, czyli pierwszy, chyba że wybierzesz inny); projekt
CMake buduje się albo buduje i uruchamia testy. Projekt modułu jądra buduje się w obszarze roboczym warsztatu jądra dla
działającego jądra, a takie budowanie należy do admina. Działający krok ma **Anuluj**, a zadanie zwykle kończy się od razu.
**Uruchom workflow** uruchamia ręcznie workflow CI projektu, gdy workflow ma wyzwalacz `workflow_dispatch`; w przeciwnym
razie przycisk jest wyłączony, a strona mówi dlaczego. Workflow każdego szablonu ma ten wyzwalacz. **Zainstalowane na
tym NAS-ie** wymienia pakiety zainstalowane przez wdrożenia testowe projektu, z **Usuń z tego NAS-a** dla admina. **Usuń
projekt**, w menu obok przycisków nagłówka albo w menu projektu na stronie Projekty, jest dla admina lub dewelopera,
którego konto Forgejo zawiera repozytorium. Najpierw wpisujesz nazwę projektu; folder znika, a repozytorium Forgejo,
aplikacja App Buildera o tej nazwie i zainstalowane pakiety zostają. Strona podaje potem link do repozytorium.

**Ustawienia** zawierają to, co bvsd zapamiętuje dla projektu poza jego folderem. Dla projektu z Makefile, CMake albo
QPKG pokazują środowisko, w którym działają Zbuduj i Zbuduj ponownie, i je zmieniają. Dla Makefile, który tylko tworzy i
buduje aplikacje App Buildera, wymieniają każdą aplikację ze środowiskiem, które podaje jej wiersz w Makefile. Projekt,
który tworzy pakiet albo którego workflow uruchamia `bvs repo publish`, ma tam przełącznik **Opublikuj w repozytorium
App Center**: gdy jest włączony, `bvs repo publish` w CI projektu dodaje jego pakiety do repozytorium; gdy jest
wyłączony, przebieg CI pomija ten krok z komunikatem. Projekt bez zapamiętanego wyboru (kopia przykładu, klon) ma go
włączonego. **Git** pokazuje gałąź, ile jest przed lub za zdalnym repozytorium według ostatniego fetcha (strona nigdy
nie robi fetcha), ostatni commit i zmienione ścieżki. **Pliki** wymienia jeden folder naraz; wybierz plik, aby go
przeczytać, a `.qpkg` się pobiera. Edycja zostaje w edytorze, a folder `.git` jest pominięty.

**Przepisy**, dla projektu z własnym folderem `recipes/`, wymienia przepisy, które biorą jego aplikacje: każdy z
wersją, z wersją z biblioteki, którą zastępuje, gdy ma nazwę przepisu z biblioteki, a pod spodem z tym, co znalazła
kontrola przepisów, z plikiem i wierszem. Przepis, który bvsd pomija, ma wiersz z powodem; gdy w ten sam sposób
pominięto więcej niż trzy, resztę liczy jeden wiersz. To, co kontrola znalazła w folderze `recipes` jako całości, ma
własny wiersz. Kliknięcie otwiera folder przepisu w edytorze; menu kopiuje też polecenie, które uruchamia tę samą
kontrolę w terminalu NAS-a: `bvs recipe lint` z folderem projektu. Karta pojawia się też, gdy `recipes` projektu jest
dowiązaniem, plikiem, folderem, którego bvsd nie może odczytać, albo folderem z samymi dowiązaniami, i mówi, dlaczego
żaden przepis nie jest z niego czytany.

**Usługa i pakiet** (pakiet QPKG albo projekt z Makefile i pakietem w `qpkg/`, na przykład pakiet ze źródeł) zawiera
skrypt uruchamiania i zatrzymywania pakietu w postaci bloków, pola jego `qpkg.cfg` (**Pakiet**) i pliki z nich
zapisane (**Skrypt**). **Zapisz** pokazuje różnice w każdym pliku, potem tworzy commit w repozytorium projektu; jeśli
projekt w międzyczasie się zmienił, zaplanuj ponownie. **Edytuj ręcznie** wyłącza bloki, więc skrypt edytujesz w
edytorze; **Podłącz bloki ponownie** pokazuje, co zastąpiłoby ponowne zapisanie. **Zaktualizuj bibliotekę** wnosi do
pakietu `bvs-qpkg.sh` z tego Bucket & Code Server IDE, z różnicami. Projekt utworzony przed blokami oferuje **Użyj bloków dla tego
skryptu**.

**Wersje** wymienia, na co można przełączyć projekt, a **Przełącz na tę wersję** to robi:

| Wersja | Co robi przełączenie |
|---|---|
| `.qpkg` w `build/` albo eksport App Buildera | wdrożenie testowe tego pliku (admin) |
| Wersja zbudowana w App Builderze | aktywuje ją (admin); App Builder zachowuje jedną zbudowaną wersję |
| Wersja w repozytorium pakietów | ustawia ją jako wersję na liście (admin) |
| Tag git | wybiera tag w folderze |

Kliknięcie wybiera wersję; jej menu także wdraża testowo, publikuje, publikuje w celu WWW albo pobiera `.qpkg`. Tag
jest wybierany jako odłączony HEAD, a **Wróć do main** w karcie Git wraca do gałęzi. Przełączenie jest odrzucane,
gdy folder ma niezatwierdzone zmiany; komunikat je wymienia, a ty najpierw zatwierdzasz je lub odkładasz w edytorze.
Zaznacz dwie wersje, a **Porównaj obie** wymienia pliki, które ma jedna, a nie ma druga, oraz pliki zmienione. Dwa
tagi albo dwa pakiety są porównywane po zawartości; pakiet z commitem tylko po rozmiarze. Na stronie **Pakiety** admin
instaluje też wersję z repozytorium na tym NAS-ie (**Zainstaluj na tym NAS-ie**) z kontrolami wdrożenia testowego.

### Własny serwer WWW (cele WWW)

Cel WWW to miejsce, w którym admin publikuje pakiet lub stronę WWW na własnym serwerze WWW: folder tego NAS-a (udział
Web systemu QTS, który udostępnia Web Server QTS, albo inny folder udostępniony) lub zdalne miejsce rclone (SFTP,
FTP, WebDAV, S3, skonfigurowane w `rclone.conf` kopii zapasowych), z adresem publicznym, który je udostępnia. Są na
stronie **Publikowanie**, w sekcji **Cele WWW**: **Dodaj cel**, **Zmień**, **Usuń** i **Sprawdź**, który kopiuje tam mały
plik, pobiera go z adresu publicznego i znowu go usuwa.

Cel w folderze to osobny folder w udziale (`/share/Web/mojastrona`, nigdy cały udział ani wolumin), poza folderem
danych, bo publikacja strony usuwa z celu to, czego strona już nie zawiera. Adres zaczyna się od `http://` lub
`https://` i nie zawiera użytkownika ani hasła.

Każda publikacja odczytuje plik z powrotem przez adres publiczny: `.qpkg` pakietu z jego `.sha256`, porównany po
sha256, albo `index.html` strony, porównany bajt po bajcie. Gdy adres nie udostępnia tego, co skopiowano, zadanie
kończy się błędem `web_check:` z obiema wartościami.

## Runnery CI

Moduł CI uruchamia Forgejo Actions: plik workflow w katalogu `.forgejo/workflows/` repozytorium uruchamia się na
runnerze, gdy coś trafi do repozytorium przez push. Moduł potrzebuje modułu Git. Administrator włącza go w
**Moduły**, **CI**, **Włącz**. Zadanie rejestruje w Forgejo własny runner NAS-a, uruchamia Forgejo ponownie z
włączonym Actions i uruchamia runner; kończy się, gdy runner połączy się z Forgejo.

### Runner na NAS-ie

- **Etykiety** runnera to `bucketvs`, `qnap-x86_64` (albo `qnap-aarch64`) i `self-hosted` oraz dodatkowe etykiety,
  które administrator ustawia na stronie CI. Zadanie, którego `runs-on` wskazuje jedną z nich, działa tutaj, na
  przykład `runs-on: bucketvs`.
- **Każde zadanie działa na samym NAS-ie, jako admin**, nie w kontenerze. Zadanie ma narzędzia pakietu (git, curl,
  make i inne), Node dla akcji JavaScript, takich jak `actions/checkout`, oraz `bvs env run <środowisko> --
  <polecenie>` do budowania w środowisku. Kto może wysłać plik workflow do repozytorium, może więc uruchamiać
  polecenia na NAS-ie jako admin. Konto z rolą użytkownika albo viewer nie może w Forgejo zakładać, forkować ani
  importować repozytoriów ani zakładać organizacji; developer i admin mogą. Viewer nigdy nie wypycha zmian. Gdy CI
  jest włączone, wypchnięcie przez konto z rolą użytkownika do repozytorium z włączonymi Actions jest odrzucane z podaniem
  powodu, tak samo wypchnięcie kluczem wdrożeniowym (deploy key). Wypchnięcie przez konto z rolą użytkownika albo kluczem
  wdrożeniowym do repozytorium, którego właścicielem lub administratorem jest konto z rolą niższą niż developer, też
  jest odrzucane, także gdy CI jest wyłączone: takie konto mogłoby samo włączyć Actions. bvsd wyłącza Actions w ciągu
  minuty w każdym repozytorium, którego właścicielem jest konto z rolą użytkownika lub viewer albo do którego takie konto może
  zapisywać, a także przed startem runnera, gdy CI zostaje włączone.
- **Co zostaje** to własna decyzja developera albo admina: włączenie Actions albo uruchomienie workflow w
  repozytorium, do którego konto z rolą użytkownika wcześniej zapisywało, albo nadanie takiemu kontu dostępu administratora do
  repozytorium, do którego wcześniej zapisywało. Developer, który scala albo buduje zmiany konta z rolą użytkownika, też uruchamia
  je jako admin.
- **Liczba zadań naraz** to `ci.capacity` (domyślnie 1). Zadanie, którego nie może przyjąć żaden runner online,
  czeka w Forgejo.
- **Repozytoria założone przed włączeniem CI** mają w Forgejo wyłączone Actions, więc wypchnięty workflow nic nie
  uruchamia: trzeba je włączyć w **Ustawieniach** repozytorium, **Jednostki** (Actions). Repozytoria założone przy
  włączonym CI mają je od razu.
- **Akcje**, takie jak `actions/checkout@v4`, są pobierane z `https://data.forgejo.org`, więc workflow, który ich
  używa, potrzebuje dostępu NAS-a do internetu. Runner nie ma pamięci podręcznej: `actions/cache` niczego nie
  znajduje, a zadanie idzie dalej.
- **Artefakty** (`actions/upload-artifact`) trafiają pod własny adres Forgejo (ten ze strony Git), więc NAS musi
  się z nim łączyć i ufać jego certyfikatowi, gdy to HTTPS.
- **Stan runnera** jest w `<data>/runner`, jego dziennik to **runner** w **Logi**. Wyłączenie modułu zatrzymuje
  runner i wyłącza Actions w Forgejo.

### Zewnętrzne runnery

Maszyna do budowania w innym miejscu (serwer, maszyna wirtualna) też może przyjmować zadania z tego Forgejo. Na
stronie CI, w części **Zarejestruj zewnętrzny runner**, administrator nadaje mu nazwę i dostaje polecenia dla Linuksa
x86_64 i aarch64. Pobierają one forgejo-runner 13.2.0 z code.forgejo.org, sprawdzają jego sumę sha256, zapisują
token runnera w pliku `runner-token` (czytelnym tylko dla właściciela) i uruchamiają runner z etykietą `linux-amd64`
(albo `linux-arm64`). Runner od razu jest na liście, offline do czasu uruchomienia poleceń. Token pojawia się tylko
wtedy. Taki runner wykonuje zadania na swojej maszynie, jako użytkownik, który go uruchomił, i łączy się z Forgejo
pod jego publicznym adresem.

## SVN i Bucket

Moduł SVN i Bucket udostępnia repozytoria Subversion pod `/svn/<nazwa>` i buckety pod `/bucket/<nazwa>`. Bucket to
dysk sieciowy (WebDAV), w którym każdy zapisany plik staje się rewizją, więc każdą wcześniejszą wersję pliku można
odczytać albo przywrócić. Administrator włącza moduł w **Moduły**; strona **SVN i Bucket** zakłada, pokazuje i
usuwa oba typy.

- **Konta** są z panelu. Uprawnienia ustawia się dla każdego repozytorium: odczyt i zapis, odczyt albo brak, dla
  każdego konta, plus domyślne dla kont spoza listy. Administratorzy zawsze czytają i zapisują; konta z rolą użytkownika i developer
  zapisują tam, gdzie mają odczyt i zapis; przeglądający (viewer) najwyżej czytają. Nowe repozytorium nie daje nikomu
  poza administratorami żadnych uprawnień, dopóki administrator ich nie ustawi (ikona osób w jego wierszu, **Uprawnienia do
  `<nazwa>`**).
  Poniżej roli admin strona pokazuje każdemu jego własny dostęp do każdego repozytorium i bucketu.
- **Błędne hasła** liczą się jak przy logowaniu do panelu: 5 błędnych haseł w ciągu 15 minut blokuje to konto z
  tego adresu, i sam adres, na 15 minut.
- **Uwierzytelnianie dwuskładnikowe.** Konto z uwierzytelnianiem dwuskładnikowym nie używa swojego hasła do SVN ani WebDAV. Zakłada na
  stronie swojego konta jedno **hasło aplikacji** (**Hasło aplikacji do WebDAV i SVN**): 25 losowych znaków,
  pokazane raz. Nowe zastępuje stare; zmiana hasła albo wyłączenie uwierzytelniania dwuskładnikowego je usuwa.
- **Usunięcie** repozytorium przenosi je do `<data>/trash`, skąd zadanie `trash` usuwa je po 7 dniach.

### Repozytoria SVN

```bash
svn checkout "https://<adres NAS-a>:9951/svn/<nazwa>"
svn commit -m "…"
```

Strona podaje polecenie checkout z adresem repozytorium. Za proxy adres idzie za publicznym URL-em, a z nazwą hosta
SVN jest to `/svn/<nazwa>` na tej nazwie. **Historia** repozytorium pokazuje jego rewizje; plik można odczytać w
dowolnej rewizji, porównać z inną i przywrócić jako nową rewizję (**Przywróć tę wersję**, dla kont z odczytem i
zapisem).

### Buckety

- **Zapis to commit.** Klient, który zapisuje plik, tworzy rewizję z nazwą konta i czasem; rewizja jest oznaczona
  jako zrobiona przez WebDAV. Wiele klientów zapisuje najpierw pusty plik, a potem dane, więc jeden zapis może dać
  dwie rewizje.
- **Pliki macOS.** Bucket założony z opcją **Odrzucaj pliki ._\* i .DS_Store z macOS** (domyślnie włączona) nie
  przechowuje plików `._*` i `.DS_Store`, które Finder zapisuje obok kopiowanych. Klient nie dostaje błędu, ale plik
  nigdy nie staje się rewizją. Usuwanie takich plików jest dozwolone.
- **Powrót do wersji.** Historia bucketu pokazuje wersje każdego pliku; **Przywróć tę wersję** zapisuje starą
  zawartość jako nową rewizję, więc nic nie ginie.

### Klienty WebDAV i ich ograniczenia

| Klient | Jak się połączyć | Uwagi |
|---|---|---|
| Finder w macOS | **Idź**, **Połącz z serwerem**, `https://<adres NAS-a>:9951/bucket/<nazwa>` | Finder blokuje plik na czas zapisu; bucket odpowiada na blokady WebDAV |
| rclone | zdalny zasób `webdav` z `vendor = other` i adresem bucketu | dobre do kopiowania ze skryptów |
| davfs2 (Linux) | `mount -t davfs https://<adres NAS-a>:9951/bucket/<nazwa> /mnt/<katalog>` | nazwa użytkownika z panelu i hasło albo hasło aplikacji |
| Cyberduck, WinSCP | połączenie WebDAV (HTTPS) z portem 9951, ścieżka `/bucket/<nazwa>` | |
| curl | `curl -T plik -u <użytkownik> https://<adres NAS-a>:9951/bucket/<nazwa>/plik` | jedno wysłanie, jedna rewizja (albo dwie, zob. wyżej) |
| Eksplorator Windows | **Mapuj dysk sieciowy** | WebClient w Windows wysyła hasło tylko przez HTTPS z certyfikatem, któremu Windows ufa: bucket trzeba postawić za proxy z prawdziwym certyfikatem ([Za odwrotnym proxy](#za-odwrotnym-proxy-plesk)); certyfikat samopodpisany nie wystarczy |

- Duże pliki nie są przerywane: nginx nie ustawia na `/bucket/` limitu rozmiaru i przekazuje wysyłany plik na
  bieżąco.
- Na plik, którego bucket nie przechowuje (plik macOS, jak wyżej), serwer WebDAV i tak odpowiada `201`, więc klient
  nie pokazuje błędu; pliku w buckecie nie ma.

### Pokazywanie repozytorium albo bucketu osobom bez konta

Administrator może udostępnić repozytorium SVN albo bucket przełącznikiem w jego wierszu na stronie **SVN i Bucket**
(**W sieci**). Każdy, kto zna adres, może je wtedy czytać w przeglądarce bez konta:

| Adres | Co pokazuje |
|---|---|
| `https://<adres NAS-a>:9951/svn-web/<nazwa>/` | repozytorium SVN |
| `https://<adres NAS-a>:9951/bucket-web/<nazwa>/` | bucket |

Link **Zobacz w sieci** w wierszu otwiera tę stronę. Pokazuje pliki i foldery w najnowszej albo starszej rewizji,
plik tekstowy z numerami wierszy (do 1 MiB) albo obraz, a każdy plik da się pobrać. **Historia** pokazuje rewizje
z autorami, opisami i zmienionymi plikami. Strona rewizji pokazuje różnice każdego zmienionego pliku (do 20 plików po najwyżej
1 MiB; pozostałe są linkami); dowolny plik można porównać między dwiema rewizjami.

- **Tylko do odczytu.** Te strony niczego nie zmieniają. Klienci WebDAV i `svn` nadal potrzebują konta, także przy
  publicznym repozytorium.
- **Tylko to, co publiczne.** Repozytorium, które nie jest publiczne, i nazwa, której nie ma, odpowiadają tak samo:
  „There is no public repository at this address”, więc strony nie zdradzają, jakie nazwy istnieją.
- **Limity.** Jeden adres może otworzyć 60 stron naraz, a potem 2 na sekundę, i pobierać 2 pliki jednocześnie;
  powyżej tego strona prosi, żeby spróbować za chwilę. Różnice są ucinane na 2 MiB.
- **Wyszukiwarki** są proszone, żeby nie indeksowały tych stron. Strony są po angielsku. Mają wygląd panelu, ciemny
  albo jasny, tak jak ustawiona jest przeglądarka odwiedzającego.
- Za proxy albo z nazwą hosta SVN adres idzie za adresem samego repozytorium (publiczny adres albo nazwa hosta SVN
  z tymi samymi ścieżkami).

## Edytor i narzędzia AI

Moduł Edytor uruchamia VS Code w przeglądarce (code-server) dla każdego konta z rolą developer lub admin.
Administrator włącza go w **Moduły**. Każde konto ma własną instancję: startuje, gdy konto otwiera edytor, i
zatrzymuje się po czasie bez ruchu.

**Terminal edytora to powłoka roota na NAS-ie.** W QTS procesy pakietu działają jako admin (uid 0), poza Forgejo i
httpd. Rolę developer należy dawać tylko osobom, którym dałoby się hasło administratora NAS-a.

### Otwieranie edytora

- **Otwórz edytor** na stronie Ustawienia edytora (albo `/ide/` na adresie panelu lub nazwa hosta edytora) otwiera środowisko w nowej
  karcie. Pierwsze żądanie uruchamia instancję; bez sesji najpierw pojawia się logowanie panelu.
- Pod opublikowaną główną nazwą hosta, dopóki edytor nie jest tam opublikowany, strona Ustawienia edytora, karta Edytor, Przegląd i
  **Otwórz powłokę** mówią, że edytor otwiera się tylko z sieci lokalnej, i dają odnośnik do jego adresu w sieci LAN
  (`https://<adres NAS-a>:<port HTTPS>/ide/`); adminowi dają też odnośnik do przełącznika na stronie **Publikowanie**.
- Edytor otwiera `<data>/projects`, gdzie przykłady z Projektów edytora zapisują swoje projekty. Ustawienia konta, rozszerzenia i
  historia terminala są w `<data>/ide/<konto>`, a jego folder domowy w `<data>/home/<konto>`.
- Terminal ma narzędzia pakietu na początku `PATH`: git, svn, node, npm, rsync, curl, bash i resztę.
  `bvs env shell <nazwa>` wchodzi do środowiska ze strony Toolchainy, a `eval "$(bvs use <id>)"` ustawia toolchain albo
  środowisko uruchomieniowe. **Otwórz powłokę** na stronie Toolchainy otwiera edytor z terminalem już w tym środowisku.
- Obszar **Edytor** i paleta poleceń otwierają edytor z powłoką w środowisku albo z folderem aplikacji. Edytor pokazuje
  się w karcie Edytor panelu; na telefonie otwiera się we własnym oknie przeglądarki. Tylko strony spod adresu
  panelu mogą pokazywać edytor u siebie: pakiet mówi to przeglądarce w każdej odpowiedzi edytora, więc inna witryna nie
  umieści terminala roota z edytora pod swoją stroną.
- **Czas bezczynności** (domyślnie 30 minut) zatrzymuje instancję bez ruchu z przeglądarki, ale nigdy wtedy, gdy
  działa program uruchomiony z jej terminali, agent AI uruchomiony przez rozszerzenie (`claude` z Claude Code for VS
  Code, Codex, Gemini CLI) albo coś, co z nich zostawiono uruchomione. 0 wyłącza zatrzymywanie. Konto może zatrzymać
  swoją instancję na stronie Ustawienia edytora; administrator może zatrzymać każdą.
- **Pamięć i procesy** na stronie Ustawienia edytora pokazuje, co uruchamia instancja i ile pamięci zajmuje na tle pamięci NAS,
  oznacza to, co ją podtrzymuje, kończy jeden proces przyciskiem **Zakończ** i mówi, dlaczego przyszło ostatnie
  zatrzymanie i co zakończyło. Agent AI czekający na polecenie też podtrzymuje instancję: zakończ go tam, gdy nie jest
  już potrzebny.
- Widok **Ports** w VS Code sięga do serwera deweloperskiego na NAS-ie pod `/ide/proxy/<port>/`; własne porty
  pakietu na pętli zwrotnej są tam odrzucane.
- **New recipe** w menu folderu `recipes` projektu albo folderu receptury w nim, w widoku **Explorer**, pyta o nazwę i
  zapisuje tam nową recepturę: `recipe.json` i `build.sh` z receptury startowej pakietu. W palecie poleceń jest to
  polecenie **Bucket: New recipe**; działa, gdy otwarty jest plik projektu z folderem `recipes`. Przy każdym zapisie
  pliku receptury edytor sprawdza receptury projektu przez `bvs recipe lint` i pokazuje wyniki w widoku **Problems**.
  Ostrzega o sha256 receptury startowej, dopóki nie wpiszesz prawdziwej sumy swojego źródła.

Edytor odpowiada tylko własnym stronom. WebSocket, formularz albo żądanie skryptu, które wysyła inna strona z sesją
konta, z innej witryny albo z innego portu NAS-a, dostaje 403 i nie uruchamia żadnej instancji, więc inna strona nie
dosięgnie też serwera deweloperskiego za widokiem **Ports**.

### Rozszerzenia

- Rozszerzenia instaluje się w widoku Extensions w edytorze (Ctrl+Shift+X). Pochodzą z Open VSX: przeglądarka wyszukuje w
  open-vsx.org i pokazuje stronę i ikonę każdego rozszerzenia z openvsx.eclipsecontent.org, a NAS pobiera samo
  rozszerzenie z open-vsx.org. Przeglądarka i NAS muszą do tego mieć dostęp do internetu.
- Pierwsze rozszerzenie każdego wydawcy pyta „Do you trust the publisher …?”. **Trust Publisher & Install** je
  instaluje; do tego czasu rozszerzenie pokazuje „Installing”.
- Każde konto trzyma swoje rozszerzenia w `<dane>/ide/<konto>/extensions` i zostają one po aktualizacji pakietu.
- Pakiet ma zestaw rozszerzeń i formaterów, który edytor instaluje każdemu kontu przy jego pierwszym uruchomieniu i
  ponownie po aktualizacji pakietu z nowym zestawem. Dzięki niemu edytor koloruje i formatuje (**Format Document** w
  palecie poleceń) Markdown, skrypty powłoki, C, C++, Rust, Go, Pascal, Python, Perl, PHP, JSON, YAML, TOML,
  konfigurację nginx, CSS, HTML, JavaScript i TypeScript. ShellCheck zaznacza błędy w skryptach powłoki, pliki
  jednostek systemd mają kolorowanie i podpowiedzi, plik `.editorconfig` projektu ustala wcięcia, kody kolorów takie
  jak `#ff8800` czy `rgb(0, 128, 0)` są w każdym pliku pokazane w swoim kolorze, a **Hex Editor: Open Active File in
  Hex Editor** w palecie poleceń pokazuje bajty pliku.
- Rust formatuje się, gdy na stronie Toolchainy jest zainstalowane środowisko uruchomieniowe Rust, a edytor konta
  uruchomi się ponownie; do tego czasu Format Document w pliku Rust zgłasza, że żądanie się nie powiodło.
- Na NAS-ie z procesorem ARM (pakiet aarch64) Pascal, Perl i PHP są kolorowane, ale nie formatowane: pakiet nie ma dla
  niego kompilacji Free Pascala, Perla ani Mago.
- Rozszerzenie z zestawu, które konto usunie w widoku Extensions, zostaje usunięte, także po aktualizacji pakietu, a
  to, które konto zaktualizuje, zostaje w nowszej wersji. Domyślny formater wybrany dla języka (**Format Document
  With…**, potem **Configure Default Formatter…**) zostaje wyborem konta.
- Każdy plik `.conf` otwiera się jako konfiguracja nginx. Nazwa języka na pasku stanu przełącza plik na inny język.
- Ruff, który formatuje Pythona, jeden raz mówi, że nie ma zainstalowanego rozszerzenia środowiska Pythona. Formatuje
  także bez niego.
- W terminalu edytora `code-server --install-extension <wydawca.nazwa>` też instaluje rozszerzenie z Open VSX.
- Niektóre rozszerzenia rysują własne widoki, na przykład panel Claude Code for VS Code, edytor szesnastkowy i podgląd
  Markdown. Przeglądarka pokazuje te widoki tylko wtedy, gdy ufa certyfikatowi panelu. Przy certyfikacie z podpisem
  własnym, przyjętym tylko przez stronę ostrzeżenia przeglądarki, zostają puste, a karta Edytor i strona Ustawienia edytora to mówią.
  Naprawia to każda z tych dróg:
  - zaufaj certyfikatowi NAS-a na urządzeniu: **Pobierz certyfikat** na stronie Sieć albo w tym komunikacie, a potem
    dodaj go do zaufanych certyfikatów systemu lub przeglądarki ([Certyfikat](#certyfikat));
  - admin dodaje certyfikat dla nazwy NAS-a na stronie Sieć;
  - otwórz edytor przez opublikowany pełny host, gdy admin go tam opublikuje.
- Claude Code for VS Code (wydawca Anthropic) instaluje się z widoku Extensions, a jego panel pyta, jak zalogować
  się do Anthropic. **Claude Code: Open in Terminal** w palecie poleceń uruchamia Claude Code w terminalu, co działa
  przy każdym certyfikacie. Własne funkcje czatu VS Code są na start wyłączone (`chat.disableAIFeatures`); to
  ustawienie nie dotyczy tego rozszerzenia.

### Claude Code, Gemini CLI i Codex CLI

W sekcji **Narzędzia AI wiersza poleceń** na stronie Ustawienia edytora każde konto instaluje, aktualizuje i usuwa te narzędzia
dla siebie:

| Narzędzie | Instalacja | Polecenie |
|---|---|---|
| Claude Code | instalator Anthropic (`claude.ai/install.sh`), npm, gdy ten zawiedzie | `claude` |
| Gemini CLI | `npm install -g @google/gemini-cli` | `gemini` |
| Codex CLI | `npm install -g @openai/codex` | `codex` |

- Trafiają do folderu domowego konta (`~/.local/bin`, `~/.npm-global/bin`), który jest na `PATH` terminala; inne
  konto ich nie widzi.
- Każda instalacja to zadanie z dziennikiem na stronie Zadania. Logowanie do każdego narzędzia (konto albo klucz
  API) odbywa się w samym narzędziu, w terminalu edytora.

### Programowanie z asystentem AI

Każdy projekt utworzony przez **Nowy projekt** zawiera skill dla asystentów AI: `.claude/skills/qnap-dev/` i plik
`AGENTS.md`, który na niego wskazuje. Skill, po angielsku, zawiera wytyczne do wszystkiego, co dotyczy NAS-a QNAP: czym
jest on w powłoce (główny system plików w RAM-dysku, BusyBox, brak systemd i menedżera pakietów, `getcfg` i `setcfg`),
pamięć masowa i ścieżki, jak budować, podpisywać i publikować tym pakietem (`bvs env run`, `bvs ldd`, `bvs qbuild`, App
Builder, warsztat jądra, repozytorium pakietów), porty, kopie zapasowe oraz czego nie wolno proponować, na przykład
`apt` na NAS-ie albo dopuszczania aplikacji bez podpisu w App Center. Mówi wprost, że podpisywanie kluczem sprzętowym
nie jest obsługiwane ani testowane.

- Uruchamiaj asystenta w folderze projektu (`cd <data>/projects/<nazwa>`), a nie w `projects/`: każde narzędzie
  szuka w folderze, w którym startuje.
- Claude Code wczytuje skill z `.claude/skills/`, gdy zadanie go potrzebuje. Codex CLI czyta `AGENTS.md`. Gemini
  CLI czyta `AGENTS.md`, gdy plik `.gemini/settings.json` projektu zawiera
  `{"context": {"fileName": ["AGENTS.md", "GEMINI.md"]}}`.
- Pakiet trzyma aktualny skill w `/usr/bin/bucketvs/share/skills/qnap-dev`. Projekt utworzony, zanim pakiet go
  miał, albo projekt skopiowany z galerii Przykłady dostaje go przez skopiowanie tego folderu do `.claude/skills/`
  projektu.
- Skill powołuje się na bazę wiedzy QNAP Club Polska (forum.qnap.net.pl) w sprawie tego, co NAS ma, a czego nie ma.
- Build pakietu zapisuje też skill jako `qnap-skill-<wersja>.zip`, te same pliki, do użytku poza edytorem: rozpakuj go do
  `.claude/skills/` projektu albo do `~/.claude/skills/`. Tekst skilla jest na licencji CC BY 4.0 (jego `LICENSE`):
  można go udostępniać i przerabiać z dopiskiem "QNAP guidelines skill by QNAP Club Poland / qnaputilities.com, CC BY
  4.0".

## Toolchainy i środowiska

Strona Toolchainy pobiera to, co jest potrzebne do budowania oprogramowania na NAS-ie:

| Typ | Co to jest | Przykłady |
|---|---|---|
| System plików root | system Linux do rozpakowania w środowisku | Debian 12 i 13, Ubuntu 22.04 i 24.04, Alpine 3.22 |
| Cross-toolchain | kompilator, który buduje dla innego procesora | toolchainy Bootlin dla aarch64, x86-64 i armv7, z glibc albo musl |
| Środowisko uruchomieniowe | język lub narzędzie do budowania | Python, Go, Node.js, Zig, Rust, CMake, Ninja, Java (JDK) |
| QNAP | własne narzędzia QNAP | QDK 2.5.0, dla [`bvs qbuild`](#budowanie-qpkg-przez-bvs-qbuild) i eksportu jako QPKG |
| Źródło jądra | drzewo jądra Linux dla [warsztatu jądra](#warsztat-jądra) | Linux 6.6.129 z kernel.org; wydanie GPL jądra QNAP dla QTS 5.2.3 |

Pozycje dla innego procesora są ukryte; „Pokaż pozycje dla innych procesorów” je wyświetla, ale nie da się ich
zainstalować. Toolchainy Bootlin działają na x86_64, więc NAS z procesorem ARM nie ma cross-toolchaina.

### Katalog

Pakiet ma wbudowany katalog. Podpisany zdalny katalog pod `https://pub.qnaputilities.com/bucketvs/catalog.json`
może dodawać pozycje bez nowego pakietu. bvsd używa go tylko wtedy, gdy jego podpis ed25519 pasuje do klucza
wbudowanego w pakiet i gdy nie jest starszy niż katalog w użyciu. „Odśwież katalog” pobiera go od razu, a wbudowane
zadanie cron „Odświeżanie katalogu toolchainów” pobiera go codziennie o 04:15.

### Instalowanie i usuwanie

Instalowanie należy do admina; jego postęp widać na stronie Zadania:

1. Archiwum jest pobierane do `toolchains/cache/` w folderze danych. Przerwane pobieranie wznawia się przy
   ponownym uruchomieniu instalacji.
2. Jego sha256 jest porównywane z katalogiem. Niezgodność usuwa plik.
3. Cross-toolchain albo środowisko uruchomieniowe jest rozpakowywane do `toolchains/<typ>/<id>`, a archiwum
   usuwane. System plików root i źródło jądra zostają jako archiwum; każde środowisko i każdy obszar roboczy jądra
   rozpakowuje własną kopię. QNAP publikuje swoje wydanie GPL jądra w dwóch częściach: obie są pobierane, każda
   sprawdzana własnym sha256, a potem łączone.

Pełny wolumin przerywa instalację błędem „no space left on” z punktem montowania woluminu.

„Usuń” usuwa zainstalowaną pozycję. Środowiska utworzone z systemu plików root są kopiami i zostają.

### Używanie toolchaina lub środowiska uruchomieniowego

`bvs use <id>` wypisuje wiersze `export` (na przykład `PATH`), które włączają w powłoce zainstalowany
cross-toolchain albo środowisko uruchomieniowe:

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

Strona Toolchainy pokazuje te same wiersze w „bvs use” przy każdej zainstalowanej pozycji.

### Środowiska

Środowisko to system Linux rozpakowany z systemu plików root do `envs/<nazwa>` w folderze danych. Można je
zmieniać: w środowisku z Debianem albo Ubuntu działa `apt install build-essential`, a to, co zainstalujesz,
zostaje w nim.

- „Nowe środowisko” (admin) najpierw pobiera system plików root, gdy nie jest zainstalowany, a potem go rozpakowuje.
- Dopóki nie ma żadnego środowiska, lista proponuje Ubuntu 24.04 builder, w którym budują się przykłady aplikacji, pod
  jednym przyciskiem (admin): jego zadanie widać na liście, a środowisko pojawia się, gdy zadanie się skończy. Nic nie
  powstaje samo.
- Nazwa ma od 1 do 64 znaków: małe litery, cyfry, `.`, `_` lub `-`, na początku litera albo cyfra.
- Gotowe środowisko z galerii Przykładów instaluje też pakiety przez apt-get, zanim środowisko się pojawi:
  Ubuntu 24.04 builder (domyślne środowisko do budowania) i Debian 12 builder, oba w wersji full, z narzędziami App
  Buildera i patchelf; Clang/LLVM (LLVM 18), Fortran (gfortran 13), Kernel builder, .NET 8 SDK i Kotlin (Kotlin
  1.3.31), wszystkie na Ubuntu 24.04. Glibc każdego z nich to najwyżej 2.39, więc to, co w nim zbudujesz, działa na
  każdym NAS-ie, na którym działa pakiet ([Na jakich modelach NAS działa aplikacja](#na-jakich-modelach-nas-działa-aplikacja)).
  Gdy apt-get się nie powiedzie, żadne środowisko nie zostaje. Z powłoki: `bvs env create <nazwa> --from
  <id-systemu-plików-root> --package <nazwa> --package <nazwa>`.
- „Sklonuj” tworzy pełną kopię pod nową nazwą, na przykład żeby wypróbować zmianę bez ruszania oryginału.
- „Eksportuj” pakuje środowisko do `toolchains/exports/<nazwa>-<czas>.tar.zst`.
- „Usuń” usuwa środowisko. bvsd odmawia, dopóki działa w nim powłoka albo proces. Pliki w `projects/` nie należą
  do środowiska i zostają.

W środowisku:

- `projects/` z folderu danych jest widoczny jako `/projects`, a powłoka startuje w nim;
- `HOME` to `/root` środowiska, a `LANG` to `C.UTF-8`;
- zamontowane są `/proc`, `/sys` tylko do odczytu i mały własny `/dev`, a `resolv.conf` i `hosts` NAS-a są
  kopiowane do środowiska;
- montowania należą do własnej przestrzeni nazw montowań powłoki i znikają, gdy ona się kończy, więc na NAS-ie nic
  nie zostaje zamontowane.

To samo z powłoki (przykłady wywołują `bvs` po nazwie, jak mogą to zrobić zadanie cron i sesja SSH na NAS-ie, gdy
pakiet działa):

```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` i `bvs env run` zaczynają w folderze, z którego je uruchomiono. Folder w `projects/` katalogu
danych to ten sam folder w `/projects` wewnątrz środowiska. Każdy inny folder (kopia repozytorium w zadaniu CI,
folder w udziale) jest montowany w `/work` i polecenie zaczyna tam. Uruchomione z `/` zaczynają w `/projects`.

### Budowanie QPKG przez bvs qbuild

`bvs qbuild` buduje pakiet QPKG z folderu, w którym je uruchomiono (folderu z `qpkg.cfg`, takiego jak ten, który
tworzy szablon QPKG w Nowym projekcie). Uruchamia `qbuild` QNAP-a wewnątrz środowiska Debian lub Ubuntu. qbuild
wywołuje programy systemowe pełnymi ścieżkami, a w środowisku te ścieżki prowadzą do programów środowiska zamiast do
własnych programów NAS-a. Pakiet trafia do `build/` w tym folderze.

```bash
bvs tc install qdk-2.5.0-x86_64      # raz; na NAS-ie ARM qdk-2.5.0-aarch64
cd /share/CACHEDEV1_DATA/BucketVS/projects/MyTool
bvs qbuild                           # --env <nazwa> wybiera środowisko
```

Używa środowiska `ubuntu2404-builder` (przykład „Ubuntu 24.04 builder”), a bez niego `debian12-builder`, a bez niego
innego środowiska Debian lub Ubuntu, i potrzebuje w nim `rsync`. Gdy czegoś brakuje, zatrzymuje się i podaje
polecenie, które to dodaje, na przykład `bvs env run ubuntu2404-builder -- apt-get install -y rsync`. Argumenty po
`bvs qbuild` trafiają do qbuild. Zanim qbuild ruszy, `bvs qbuild` mówi, który [przypadek podpisywania
kodu](#podpisywanie-kodu-qpkg) dotyczy folderu.

Środowisko ma własne pliki i narzędzia. Wszystko w nim działa jako root, widzi każdy proces na NAS-ie i korzysta
z sieci NAS-a.

### Podpisywanie kodu QPKG

App Center sprawdza podpis kodu pakietu przy instalacji. Do podpisania potrzebny jest certyfikat podpisywania kodu
wydany przez QNAP. `bvs qbuild` w pierwszym wierszu podaje jeden z trzech przypadków, na przykład `bvs: code signing,
case 1 (a certificate as files): …`:

| Przypadek | W folderze leży | Co się dzieje |
|---|---|---|
| 1, certyfikat w plikach | `certificate` i `private_key`, opcjonalnie `ca_certs` (łańcuch) | qbuild podpisuje pakiet przez `openssl`; środowisko potrzebuje `openssl` i `bsdextrautils` |
| 2, certyfikat na kluczu sprzętowym | `certificate_hsm` | nieobsługiwany i nietestowany: `bvs qbuild` zatrzymuje się, zanim cokolwiek zbuduje |
| 3, bez certyfikatu | żadnego z tych plików | pakiet powstaje bez podpisu |

- **Przypadek 1.** Połóż trzy pliki obok `qpkg.cfg`. Gdy w środowisku brakuje programu, `bvs qbuild` podaje
  polecenie, na przykład `bvs env run ubuntu2404-builder -- apt-get install -y openssl bsdextrautils`. Odmawia folderu
  z tylko jednym z plików `certificate` i `private_key` (qbuild ostrzegłby i zbudował pakiet bez podpisu) oraz
  folderu, w którym któryś z nich jest pusty. Tych plików nie trzymaj w gicie: `.gitignore` szablonu QPKG je
  wymienia. W CI trzymaj je jako sekrety repozytorium i zapisuj do folderu tylko na czas budowania.
- **Przypadek 2.** qbuild odczytałby miejsce klucza na tokenie ze zmiennych, które nie docierają do niego wewnątrz
  środowiska, a pakietu nie testowano z tokenem. Zbuduj bez `certificate_hsm` i podpisz pakiet na komputerze z
  tokenem własnymi narzędziami QDK; skill QNAP dla asystentów AI (`references/signing.md`) opisuje drogę QDK, której
  ten pakiet nigdy nie uruchomił.
- **Przypadek 3.** Na jednostce z QTS 6.1 `qpkg_cli -m` pakietu bez podpisu niczego nie zainstalował, a
  `sh <plik>.qpkg` w folderze udostępnionym zainstalował go i uruchomił. [Repozytorium pakietów](#repozytorium-pakietów) przyjmuje
  go tylko wtedy, gdy admin dopuszcza pakiety bez podpisu, a App Center na innym NAS-ie instaluje go tylko wtedy,
  gdy właściciel tego NAS-a dopuszcza aplikacje bez ważnego podpisu cyfrowego, czego ten pakiet nigdy nie zaleca.
  **Eksportuj jako QPKG** zawsze daje ten przypadek.
- `qbuild --sign` dodaje podpis GnuPG, który nie jest podpisem kodu czytanym przez App Center i repozytorium.

## App Builder

App Builder kompiluje wybrane programy do jednego folderu, prefiksu aplikacji (domyślnie `/usr/app/my_app_prefix`),
w środowisku.

Zakładka **Przykłady** w **Aplikacjach** ma gotowe aplikacje, środowiska, workflow CI, zadania cron i Projekty edytora.
**Zobacz kod** na karcie przykładu pokazuje, co przykład tworzy, zanim go użyjesz, dla każdej roli: pliki projektu
edytora, plik workflow, polecenie zadania cron, a dla aplikacji jej receptury w kolejności budowania z plikami `build.sh`
i `recipe.json` każdej z nich. Podgląd jest tylko do odczytu i pokazuje pliki tekstowe; środowisko nie ma plików,
więc jego karta nie ma tego przycisku. Użycie przykładu wymaga roli developer, a środowiska roli admin: karta
środowiska nie ma dla developera przycisku **Użyj tego przykładu**.

### Receptury

Każdy program powstaje z receptury. Receptura podaje swoje źródło z sha256, receptury, od których zależy, narzędzia
do budowania potrzebne w środowisku i sprawdzenie, które dowodzi, że wynik działa (na przykład
`bin/bash --version`). Są receptury bibliotek (zlib, openssl, ncurses, readline i inne), powłok (bash, zsh,
busybox), narzędzi (coreutils, curl, git, jq, rsync, tmux, htop, nano i inne), serwerów (nginx, redis, openssh,
dropbear) i języka (lua).

- Receptura jest budowana dopiero po recepturach, od których zależy.
- Ponowne budowanie używa wyniku receptury, dopóki nie zmieniły się jej dane wejściowe (źródło, receptura, jej
  zależności, kompilator i biblioteka C środowiska, prefiks oraz to, czy linkuje się dynamicznie, czy statycznie)
  i dopóki każdy zainstalowany przez nią plik jest w prefiksie.
- Tryb linkowania decyduje, jak programy znajdują swoje biblioteki. **dynamic** daje każdemu programowi RUNPATH
  `<prefiks>/lib`. **relocatable** zapisuje go względem folderu samego programu (`$ORIGIN/../lib`), więc drzewo
  znajduje swoje biblioteki w dowolnym folderze; ścieżki wkompilowane w programy, na przykład folder terminfo, nadal
  wskazują prefiks. **bundle** działa jak relocatable i dodatkowo kopiuje do `lib` biblioteki, które programy
  ładują ze środowiska budowania (na przykład libstdc++), więc aplikacja potrzebuje tylko biblioteki C NAS-a.
  **static** linkuje statycznie receptury, które to obsługują. Zmiana między dynamic, relocatable i bundle niczego
  nie buduje od nowa.
- Aplikacja statyczna odrzuca receptury, które nie linkują się statycznie (openssh, dropbear i tmux), i podaje
  powód przy każdej, chyba że ustawiono budowanie ich jako bundle. Strona nowej aplikacji i **Zmień** oferują
  wszystkie cztery tryby, a dla aplikacji statycznej także ten wybór.
- Program statyczny, który wyszukuje użytkowników, grupy albo nazwy hostów, szuka ich tylko we własnych plikach
  NAS-a `/etc/passwd`, `/etc/group`, `/etc/hosts` i w DNS, niezależnie od tego, co podaje `/etc/nsswitch.conf`,
  więc nie zna kont z domeny ani z LDAP. Gdy te konta są potrzebne, buduj takie programy dynamicznie.
  Panel ostrzega, gdy statyczna aplikacja zawiera któryś z nich.
- Po ostatniej recepturze sprawdzenie każdej receptury jest wykonywane jeszcze raz na gotowym prefiksie.
- Pliki konfiguracyjne w `etc/` prefiksu przetrwają ponowne budowanie: edytowany plik zostaje, a nowa wersja
  domyślna jest zapisywana obok jako `<plik>.bvs-new`.

### Aktualizacje przepisów

Nowe i zaktualizowane przepisy przychodzą też jako podpisany katalog z pub.qnaputilities.com, więc trafiają na NAS
przed następnym wydaniem Bucket & Code Server IDE. bvsd raz dziennie sprawdza, czy jest nowszy katalog. Używa go tylko wtedy, gdy
podpis pasuje do klucza wbudowanego w bvsd, każdy plik zgadza się z listą katalogu, a jego przepisy są nowsze niż te
dostarczone z pakietem; katalog, który nie przejdzie sprawdzenia, nigdy nie jest używany. Gdy katalog jest w
użyciu, Nowa aplikacja oznacza przepisy, które przyniósł, jako **nowy w katalogu** albo **zaktualizowany przez
katalog**.

Karta **Biblioteka przepisów** na stronie Aplikacje pokazuje, której biblioteki używa budowanie (**Biblioteka z
pakietu** z wersją pakietu albo **Katalog** z wersją katalogu), oraz ostatnie sprawdzenie i jego wynik.
Administrator ma tam dwa przyciski; drugi pojawia się, gdy katalog jest w użyciu:

- **Sprawdź aktualizacje przepisów** sprawdza od razu, bez czekania na codzienne sprawdzenie.
- **Użyj biblioteki z pakietu** wraca do przepisów dostarczonych z Bucket & Code Server IDE. Katalog zostaje odłożony, dopóki nie
  pojawi się nowszy; wtedy budowanie używa nowszego.

Po przełączeniu przebudowa używa ponownie każdego przepisu, którego pliki się nie zmieniły. Przed każdym budowaniem
bvsd jeszcze raz sprawdza pliki katalogu. Jeśli któryś zmienił się na NAS-ie, budowanie zatrzymuje się, zanim
cokolwiek się uruchomi, a dalej używany jest poprzedni katalog albo biblioteka z pakietu.

### Budowanie aplikacji

**Aplikacje**, **Nowa aplikacja** (rola developer) pyta o nazwę, prefiks (`/usr/app/<nazwa>`, chyba że wybierzesz
inny), środowisko, w którym aplikacja się buduje (środowisko Debian albo Ubuntu; galeria Przykładów zakłada
„Ubuntu 24.04 builder”, które strona wybiera jako pierwsze), cel (architektura tego NAS-a albo aarch64 lub armv7 jako
kompilacja skrośna), na jakich modelach NAS aplikacja działa, tryb linkowania i przepisy. Przepisy, od których zależą, dochodzą w kolejności budowania.

Aplikacja może też brać przepisy jednego projektu, który ma własny folder `recipes/`. Wybierz ten projekt w polu
**Przepisy z projektu** na kroku **Pakiety**. Jego przepisy są na liście pierwsze, pod nazwą projektu i z oznaczeniem
**projekt**, a za nimi są przepisy z biblioteki. Przepis projektu o nazwie przepisu z biblioteki zastępuje go w tej
aplikacji i pokazuje, którą wersję z biblioteki zastępuje. Przepis, który projekt pomija, jest wymieniony z powodem.
Wybrany przepis, którego nie ma na liście projektu, jest wymieniony pod polem wyboru i pominięty w aplikacji; wraca,
gdy wybierzesz listę, która go ma. Strona aplikacji podaje nazwę projektu, a programista otwiera z niej widok
projektu. Viewer widzi przepisy ostatniego budowania aplikacji w kolejności z jego logu, każdy ze stanem i logiem;
przed pierwszym budowaniem nie ma kolejności do pokazania.

Każdy tryb linkowania ma swoją kartę: rysunek tego, gdzie programy znajdują biblioteki, koszt trybu (rozmiar,
aktualizacje, gdzie aplikacja działa) i przepisy, które ten tryb obsługują. Zanim wybierzesz przepisy, karty liczą
całą bibliotekę przepisów; gdy wrócisz do pierwszego kroku, liczą przepisy twojego budowania. Dla aplikacji statycznej
wybierasz, co się dzieje z przepisami, których nie da się zlinkować statycznie: budować je jako pakiet albo je pominąć.

- **Zbuduj** (albo **Zbuduj ponownie**) uruchamia zadanie: pobiera każde źródło i sprawdza jego sha256, instaluje w
  środowisku narzędzia potrzebne przepisom, buduje w kopii poprzedniej zbudowanej wersji aplikacji i sprawdza każdy program.
- Budowanie, które się nie uda, zostanie anulowane albo przerwane, zostawia aplikację bez zmian: aktywacja, pobieranie i
  eksport dalej używają najnowszej wersji, która przeszła.
- Naraz trwa jedno budowanie danej aplikacji, a budowanie czeka, gdy działają programy aktywnej aplikacji (najpierw trzeba
  je zatrzymać).
- Strona aplikacji pokazuje kolejność budowania, stan i dziennik każdego przepisu, pliki pod prefiksem i archiwum
  najnowszej udanej wersji (**Pobierz**, plik `.tar.zst`).
- **Zmień** na stronie aplikacji ustawia inne środowisko, modele NAS, na których aplikacja działa, tryb linkowania,
  projekt, z którego bierze przepisy, albo przepisy; stosuje je następne budowanie.
- **Zależności**, karta strony aplikacji, pokazuje, czego potrzebuje każdy plik ELF najnowszej zbudowanej wersji i gdzie
  loader NAS-a to znajduje; pliki są czytane, nic nie jest uruchamiane. Brakująca biblioteka, wersja, której
  biblioteka nie definiuje, albo RUNPATH wskazujący poza aplikację są na liście wyników razem z naprawą. Programista
  uruchamia naprawę jej przyciskiem: **Dodaj … i zbuduj**, **Przełącz na … i zbuduj**, **Zbuduj ponownie**,
  **Załataj RUNPATH**, **Ustaw interpreter**, **Wybierz inne środowisko** albo **Buduj tylko dla tego NAS**. Naprawa bez przycisku (instalacja
  pakietu w środowisku, budowanie na inną maszynę) jest zdaniem do wykonania samodzielnie. Pod wynikami każdy plik i
  to, czego potrzebuje, jest narysowane jako graf (programy, biblioteki aplikacji, biblioteki NAS-a i brakujące w
  kolumnach; linia kreskowana to wersja, której biblioteka nie ma, kropkowana to biblioteka nigdzie nieznaleziona)
  albo jako drzewo; wybierz plik, aby zobaczyć jego dane. Na NAS-ie to samo wypisuje `bvs deps <aplikacja>`.
- **Inspektor ELF** (Toolchainy, rola developer) sprawdza jeden plik: ze zbudowanej aplikacji, z folderu projektów albo ze
  środowiska, a administrator dowolny plik na NAS-ie, tak jak działałby na tym NAS-ie albo w środowisku.
  `bvs ldd <plik>` robi to samo na NAS-ie (`--env <nazwa>` dla środowiska).
- **Każde budowanie** robi to samo sprawdzenie swojego wyniku, zanim zastąpi nim aplikację, i kończy się błędem przy
  problemie, który zatrzymuje program na tym NAS-ie, albo przy programie, który wymaga nowszej glibc, niż pozwala
  cel aplikacji; komunikat zadania podaje problem i poprawkę.
- **Narzędzie łatek** (karta Zależności) ustawia RUNPATH (`lib` aplikacji, postać `$ORIGIN` albo folder aplikacji)
  lub interpreter programu wybranych plików najnowszej zbudowanej wersji, za pomocą patchelf ze środowiska, na kopii, która
  zastępuje zbudowaną wersję tylko wtedy, gdy wszystko się uda. **Podgląd** pokazuje każdy plik przed zmianą i po niej oraz
  biblioteki, których nowy RUNPATH nie znajduje w aplikacji; **Zastosuj łatkę** ją wykonuje. Następne budowanie znów
  zapisuje RUNPATH w trybie linkowania aplikacji.

Budowanie Qt uruchamia własne narzędzia Qt (moc, rcc i, do tłumaczeń, lrelease z Linguista). Dlatego w kompilacji
skrośnej przepisy Qt (`qtbase`, `qttools`) najpierw budują te narzędzia dla tego NAS-a, do `host/` pod prefiksem, a
potem Qt dla celu; przepisy budowane na Qt uruchamiają narzędzia stamtąd. Nic na celu nie uruchamia `host/`. Należy
on do zbudowanej aplikacji, więc archiwum i **Eksportuj jako QPKG** też go zawierają.

### Własne przepisy

Gdy w bibliotece przepisów nie ma przepisu na program, projekt może mieć własny. Przepisy trafiają do folderu
`recipes/` projektu, w formacie biblioteki, i budują je tylko aplikacje, które wskazują ten projekt. Pakiet powstaje tak
w pięciu krokach:

1. **Utwórz projekt.** W [Nowym projekcie](#nowy-projekt) szablon **Package from source** (pakiet ze źródeł) tworzy
   folder z przepisem startowym nazwanym jak projekt, Makefile dla dwóch aplikacji (jednej dla procesora tego NAS-a,
   drugiej dla aarch64, kompilowanej skrośnie na NAS-ie x86_64) i skrypt startu i zatrzymania pakietu z kroku
   **Usługa**.
2. **Napisz przepis.** **New recipe** w edytorze dodaje kolejny przepis z tego samego przepisu startowego
   ([Otwieranie edytora](#otwieranie-edytora)). Wpisz do `recipe.json` prawdziwy adres źródła programu i jego sha256 (`curl
   -fsSL <adres> | sha256sum` w terminalu edytora wypisuje sumę), a kroki budowania do `build.sh`. Folder przepisu zawiera
   `recipe.json`, `build.sh` i plik vendor wskazany w `recipe.json`, nic więcej, więc łatka trafia do `build.sh` jako
   here-document.
3. **Sprawdź go.** Każdy zapis pliku przepisu sprawdza przepisy projektu i pokazuje wyniki w widoku **Problems**;
   `make lint` w terminalu i karta **Przepisy** w [widoku projektu](#widok-projektu) dają te same wyniki. Kontrola
   czyta `recipe.json` i parsuje `build.sh` przez `bash -n`. Nigdy nie uruchamia przepisu.
4. **Zbuduj.** **Zbuduj** w widoku projektu tworzy obie aplikacje, gdy ich brakuje, i buduje je jedną po drugiej.
   Budowanie czyta przepisy projektu raz, na starcie, więc plik zapisany później czeka na następne budowanie. Przepis
   projektu o nazwie przepisu z biblioteki zastępuje go tylko w aplikacjach tego projektu, a log budowania o tym mówi.
5. **Spakuj i zainstaluj.** `make qpkg` w terminalu edytora pakuje najnowsze udane budowanie każdej aplikacji w pakiet
   przez QDK z Toolchainów i pomija aplikację, która go nie ma. Gdy pliki podpisu kodu leżą w `qpkg/` obok `qpkg.cfg`
   albo w folderze wskazanym przez `QPKG_CERT_DIR`, pakiet jest podpisany ([Podpisywanie kodu
   QPKG](#podpisywanie-kodu-qpkg)), a **Wdrożenie testowe** w widoku projektu (admin) instaluje go na tym NAS-ie.

Kilka zasad dotyczy przepisów projektu:

- Przepis, który projekt pomija (dodatkowy plik w jego folderze, dowiązanie, plik ponad limit rozmiaru), jest
  wymieniony z powodem na karcie Przepisy i przez kontrolę, a budowanie, które go potrzebuje, zostaje odrzucone.
- Gdy folder projektu zmieni nazwę albo zniknie, jego aplikacje się nie budują: budowanie odpowiada „Nie ma projektu
  «…»”, dopóki **Zmień** na stronie aplikacji nie ustawi nowej nazwy projektu albo **Brak: tylko biblioteka**.
- Przepisy projektu działają jako root w środowisku budowania, tak jak przepisy biblioteki. Każdy programista może je
  pisać i budować, tak jak każdy programista może uruchomić Makefile projektu przez Zbuduj. Viewer widzi nazwy
  przepisów aplikacji w jej logu budowania, nigdy pliki przepisów.
- Przepis projektu zostaje w projekcie. Do biblioteki przepisów i do podpisanego katalogu, który pobiera każdy NAS,
  trafia tylko przez sprawdzoną zmianę w samym Bucket & Code Server IDE.
- Przepis, którego budowanie potrzebuje pakietów npm, Go albo Cargo, wymienia je w pliku vendor. Tę listę piszą
  `npm-vendor.py`, `go-vendor.py` i `cargo-vendor.py` z `/usr/bin/bucketvs/share/bucketvs/tools/`; potrzebują
  środowiska uruchomieniowego Python z [Toolchainów](#toolchainy-i-środowiska).

### Na jakich modelach NAS działa aplikacja

Program zbudowany z glibc uruchamia się tylko na NAS-ie, którego glibc jest co najmniej tak nowa jak najnowsza wersja,
o którą program prosi. Pakiet zakłada glibc 2.39 lub nowszą na każdym NAS-ie, na którym działa (QTS 6.0, QuTS hero
6.0 i nowsze): to jego próg glibc. 2.39 to glibc Ubuntu 24.04 i QuTS hero 6.0.1, odczytana z własnego
`/lib/libc.so.6` tego firmware'u; QNAP nie podaje wersji glibc, a jego informacje o wydaniach nie wymieniają QTS 6.0.
Testowe NAS-y z QTS 6.1.0 i QuTS hero 6.1.0 mają 2.43.

**Działa na** w nowej aplikacji i w **Zmień** wybiera jedno z dwóch:

- **Każdy NAS, na którym działa pakiet** (domyślnie): żaden program nie może wymagać glibc nowszej niż 2.39. Taką
  aplikację buduj w środowisku z glibc 2.39 lub starszą, na przykład w Ubuntu 24.04 builder (2.39) albo Debian 12
  builder (2.36). To wybór dla **Eksportuj jako QPKG** i repozytorium pakietów.
- **Tylko ten NAS**: programy mogą wymagać glibc tego NAS-a, dla oprogramowania, które potrzebuje nowszej. Taka
  aplikacja jest budowana dla architektury tego NAS-a i nigdy nie jest eksportowana jako QPKG.

Kompilacja skrośna (aarch64 albo armv7) jest zawsze dla każdego NAS-a. Nowa aplikacja ostrzega, gdy glibc
wybranego środowiska jest nowsza, niż aplikacja pozwala. Po każdym budowaniu bvsd odczytuje wersję glibc, o którą
prosi każdy program i biblioteka, także w kompilacji skrośnej. Plik ponad limitem kończy budowanie błędem, a poprawki
są podane: inne środowisko (**Wybierz inne środowisko**), tryb linkowania static albo **Buduj tylko dla tego NAS**.
Strona aplikacji mówi, jakiej glibc wymaga jej najnowsza udana wersja, a karta **Zależności** wymienia każdy plik
ponad limitem.

Na NAS-ie `bvs tree-deps <prefiks> --tree <folder> --glibc-max 2.39` robi to samo sprawdzenie dowolnego drzewa
aplikacji.

### Aktywacja aplikacji na NAS-ie

**Aktywuj** (admin) robi z prefiksu dowiązanie do najnowszej udanej wersji aplikacji, więc jej programy działają na
NAS-ie z `/usr/app/<nazwa>/bin`. Aktywacja nigdy nie podmienia czegoś, czego sama nie utworzyła: folder, plik albo
inne dowiązanie pod prefiksem jest odrzucane z podaniem powodu. Dysk systemowy QTS powstaje od nowa przy każdym
starcie, więc pakiet odtwarza dowiązania przy każdym uruchomieniu. **Dezaktywuj** (admin) usuwa dowiązanie;
usunięcie aplikacji najpierw ją dezaktywuje i przenosi do kosza na 7 dni. Developer może usunąć aplikację tylko
wtedy, gdy nie jest aktywna. Aplikacji zbudowanej dla innej architektury nie da
się tu aktywować; jej archiwum można pobrać na NAS tego rodzaju.

### Eksport aplikacji

**Eksportuj jako QPKG** (admin) pakuje najnowszą udaną wersję jako osobny pakiet QPKG, który instaluje aplikację na
innym NAS-ie bez tego pakietu. Odmawia dla aplikacji zbudowanej tylko dla tego NAS-a i zatrzymuje się przed
pakowaniem, gdy program ze zbudowanej wersji wymaga glibc nowszej niż 2.39 (zobacz [Na jakich modelach NAS działa
aplikacja](#na-jakich-modelach-nas-działa-aplikacja)). Potrzebuje QDK: zainstaluj „QNAP QDK 2.5.0” na stronie
Toolchainy, karta QNAP. Dopóki QDK nie jest zainstalowane, przycisku nie ma, a strona aplikacji mówi, skąd je wziąć.
Eksport uruchamia qbuild w środowisku aplikacji, tak samo jak [`bvs qbuild`](#budowanie-qpkg-przez-bvs-qbuild). Każde
budowanie instaluje tam `rsync`; dla aplikacji zbudowanej ostatnio starszą wersją pakietu odmowa podaje polecenie, które
go dodaje. Wyeksportowany pakiet tworzy dowiązanie prefiksu przy starcie i usuwa je przy zatrzymaniu. Nie jest
podpisany certyfikatem kodu: log zadania podaje [przypadek 3](#podpisywanie-kodu-qpkg), tak samo jak wiersz pod
przyciskiem. W sesji SSH `/etc/init.d/<nazwa>.sh disable` i `enable` ustawiają przełącznik wyeksportowanego pakietu w
App Center i zostawiają dowiązanie prefiksu bez zmian. Przy każdym starcie wyeksportowany pakiet dopisuje też wiersz do
`/etc/profile` i `/root/.profile`, który dodaje `bin` i `sbin` aplikacji do `PATH` logowania przez SSH, za własnymi
poleceniami NAS-a, dla admina i każdego innego konta, które może się logować; usunięcie pakietu zabiera ten wiersz.

## Repozytorium pakietów

Pakiet prowadzi repozytorium App Center: opublikowane w nim pakiety QPKG można instalować na innych NAS-ach z App
Center, jak aplikacje z każdego repozytorium.

- **Adres.** Podaje go `bvs repo list` (i `GET /api/v1/repo`): `<baza>/repo/repo.xml`, gdzie `<baza>` to opublikowany
  nazwa hosta pakietów (a bez niej opublikowana główna nazwa hosta), gdy pakiet jest opublikowany w internecie
  ([Publikacja w internecie](#publikacja-w-internecie-cloudflare-tunnel)), w przeciwnym razie publiczny URL, jeśli jest
  ustawiony, a jeśli nie, `http://<adres NAS-a>:9950` (`https://…:9951`, gdy zwykły HTTP
  tylko przekierowuje). Na drugim NAS-ie: App Center, **Ustawienia**, **Repozytorium aplikacji**, **Dodaj** i ten
  adres.
- **Publikowanie.** `bvs repo publish <plik.qpkg>` (na NAS-ie, jako root) albo `POST /api/v1/repo/packages` z plikiem
  jako treścią żądania (rola developer). Architektura wynika z nazwy pliku QDK `<nazwa>_<wersja>_<arch>.qpkg`
  (`x86_64`, `arm_64`, `arm-x41`) albo z `--arch`. Nazwa, wersja, opis i autor pochodzą z samego pakietu;
  `PUT /api/v1/repo/packages/<nazwa>/meta` i `…/icon` (plik PNG, admin) zmieniają to, co pokazuje App Center.
  Pakiet nie może się nazywać `default`: tak nazywa się własna ikona repozytorium.
- **Tylko podpisane pakiety.** Pakiet bez podpisu kodu jest odrzucany, bo App Center instaluje takie pakiety tylko
  na NAS-ie, który na to zezwala. Repozytorium uznaje pakiet za podpisany, gdy jego blok podpisu kodu jest podpisem
  PKCS#7 z certyfikatem i podpisującym. Nie sprawdza tego podpisu ani jego certyfikatów względem certyfikatów
  QNAP-a; robi to App Center przy instalacji pakietu. Administrator może włączyć w ustawieniach
  `repo.accept_unsigned` dla sieci, w której jest to wiadome; takie pakiety mają oznaczenie w opisie. Pakiety
  eksportowane przez App Builder nie są podpisane, więc wymagają tego ustawienia.
- **Wersje.** Każda wersja zostaje do pobrania, ale App Center widzi jedną wersję pakietu: tę, którą wystawił
  administrator. Pierwsza wersja pakietu opublikowana przez administratora jest wystawiona od razu, kolejna, gdy
  administrator ją ustawi (`POST /api/v1/repo/packages/<nazwa>/latest` albo `bvs repo publish --latest`). Wersja
  opublikowana przez developera, także pierwsza wersja nowego pakietu, nie jest nigdzie wystawiona, dopóki
  administrator nie ustawi wersji jako najnowszej: każdy NAS, który dodał repozytorium, instaluje wystawioną wersję
  jako root. Usunięcie wersji (admin) usuwa wszystkie jej architektury; gdy była tą wystawioną, żadna wersja nie jest
  wystawiona, dopóki administrator nie wybierze innej.
- **Wysyłanie.** Repozytorium kopiuje dwa wysłane pliki naraz i odrzuca taki, po którym na woluminie danych
  zostałoby mniej niż 256 MiB wolnego miejsca. Repozytorium nie wchodzi do kopii zapasowej: po przywróceniu trzeba
  opublikować pakiety ponownie.
- **W panelu.** Strona **Pakiety** pokazuje adres do skopiowania oraz pakiety z wersjami i informacją, czy są podpisane;
  programista wysyła tam plik QPKG, a administrator ustawia najnowszą wersję i usuwa wersję. Karta **Podpisywanie kodu**
  pokazuje [trzy przypadki](#podpisywanie-kodu-qpkg), liczbę wystawionych wersji bez podpisu oraz przełącznik
  administratora dla pakietów bez podpisu, który przed włączeniem pyta o potwierdzenie. Strona aplikacji pokazuje jej
  potok (budowanie, eksport jako QPKG, publikacja) z przyciskiem **Opublikuj**.

## Warsztat jądra

Warsztat jądra buduje moduły jądra dla jądra, na którym działa NAS, sprawdza każdy z nich względem tego jądra
i je ładuje. QNAP nie opublikował źródeł swoich obecnych jąder (jego wydania GPL kończą się na QTS 5.2.3), więc
warsztat odczytuje to, co działające jądro pokazuje o sobie, i konfiguruje drzewo jądra tak, żeby do niego
pasowało.

### Co jest potrzebne

- Środowisko Kernel builder z galerii Przykładów (Ubuntu 24.04 z GCC 13 i narzędziami do budowania jądra).
  Moduł trzeba zbudować GCC w tej samej wersji głównej co jądro; warsztat to porównuje.
- Rola admina, a w terminalu root: polecenia `bvs kernel` korzystają z gniazda CLI bvsd albo ładują moduły.

### Profil

`bvs kernel profile` pokazuje, czym jest działające jądro: jego wydanie (`6.6.129-qnap`), kompilator, którym QNAP
je zbudował, vermagic własnych modułów QNAP, rozmiar `struct module` w nich i miejsce funkcji init i exit, czy
moduły muszą być podpisane, oraz ustawienia, które dostaje drzewo jądra, żeby do niego pasowało.

### Obszar roboczy

`bvs kernel prepare` tworzy obszar roboczy działającego jądra w `kernels/<wydanie>` folderu danych:

1. Bierze źródło jądra, które katalog ma dla tej wersji (`bvs kernel sources`): własne drzewo GPL QNAP, gdy QNAP je
   opublikował, a w przeciwnym razie drzewo z kernel.org, w którym brakuje zmian QNAP. `--source` wybiera inne.
2. Konfiguruje drzewo: konfiguracją QNAP dla tego modelu, gdy drzewo jest od QNAP, a w przeciwnym razie
   ustawieniami z profilu. `--option NAZWA=m` dodaje ustawienie, na przykład sterownik.
3. Przygotowuje drzewo do budowania modułów (`--full` buduje zamiast tego całe jądro, co trwa dużo dłużej i pozwala
   sprawdzeniu zobaczyć symbole eksportowane przez jądro).
4. Sprawdza, czy drzewo buduje dokładnie dla wydania działającego jądra.

Działa jako zadanie na stronie Zadania. Nieudane przygotowanie zostawia drzewo z poprzedniego.

### Budowanie modułu

- Własny moduł to folder w `projects/` z plikiem `Kbuild`. `bvs kernel make` w tym folderze (albo
  `bvs kernel make --project <folder>`) go buduje. Przykład „Kernel module” jest takim projektem.
- Sterownik jądra, którego QNAP nie dostarcza, powstaje z jego katalogu w drzewie jądra:
  `bvs kernel make --dir drivers/usb/serial`. Przykład „USB serial driver ch341” buduje tak sterownik ch341; jego ustawienie dodaje najpierw
  `bvs kernel prepare --option USB_SERIAL=m --option USB_SERIAL_CH341=m`.

Moduły z budowania są zapisywane w `kernels/<wydanie>/modules/`. Modułu, który QNAP dostarcza dla tego jądra, nie ma
tam: ładuje się własna kopia QNAP. `bvs kernel make --build-only` niczego tam nie zapisuje: moduł zostaje tam, gdzie
powstał. Tak budują workflow CI szablonów jądra z Nowego projektu.

### Sprawdzenie i ładowanie

`bvs kernel check <moduł>` porównuje moduł z działającym jądrem: vermagic, rozmiar `struct module` i miejsce funkcji
init i exit w nim (względem własnych modułów QNAP), czy wymagany jest podpis, moduły, od których zależy, i każdy
symbol, którego potrzebuje. Każde ustalenie mówi, co się nie zgadza i co zrobić. Kod wyjścia 3 oznacza, że modułu
nie wolno ładować.

`bvs kernel load <moduł>` wykonuje sprawdzenie jeszcze raz i ładuje moduł, a najpierw moduły, od których zależy
(własne QNAP z NAS-a albo Twoje). Moduł z błędem w sprawdzeniu nigdy nie jest ładowany. `bvs kernel unload <moduł>`
go usuwa; moduł w użyciu zostaje załadowany. Każde załadowanie i usunięcie jest zapisywane w
`log/kernel-modules.log`.

Jądro QNAP nie wymienia swoich symboli danych, więc sprawdzenie nie może wyszukać symboli takich jak `jiffies`;
podaje je jako ostrzeżenia. Jeśli któregoś brakuje, jądro odrzuca moduł przy ładowaniu.

### Moduły w aplikacji

Aplikacja może wskazać moduły z obszaru roboczego. Aktywacja aplikacji ładuje je, zanim powstanie jej link, a każdy
start pakietu ładuje je ponownie, gdy panel już działa; strona Jądra pokazuje ładowanie, które jeszcze trwa.
Dezaktywacja najpierw je usuwa; moduł w użyciu zostawia aplikację aktywną.

- Jeśli NAS uruchomił się ponownie w trakcie ładowania modułów, następny start nie ładuje żadnego i pokazuje
  powiadomienie: załaduj je ręcznie albo aktywuj aplikację jeszcze raz.
- Jeśli NAS zatrzymał się bez uporządkowanego zatrzymania pakietu (awaria, zanik zasilania albo twardy reset) w
  uruchomieniu, w którym pakiet ładował moduły, następny start tych modułów nie ładuje, bo któryś z nich mógł być
  przyczyną awarii. Powiadomienie i strona Jądra je wymieniają. Załadowanie modułu ręcznie albo ponowna aktywacja
  jego aplikacji pozwala startowi znów go ładować. Zwykłe wyłączenie albo ponowne uruchomienie NAS-a zatrzymuje
  pakiet w porządku i niczego nie zmienia.
- Moduł, którego start nigdy się nie kończy (sterownik czekający na swój sprzęt), nie blokuje panelu. Po minucie
  powiadomienie go wymienia; inne ładowania i usuwania czekają na niego, a kończy go dopiero ponowne uruchomienie
  NAS-a.

Po aktualizacji QTS do innego jądra moduły trzeba zbudować ponownie w obszarze roboczym nowego jądra. Tak samo
po przywróceniu do innego folderu danych: `kernels/` nie wchodzi do kopii zapasowej.

### W panelu

Strona **Jądra** pokazuje profil działającego jądra, źródła jądra z katalogu, obszar roboczy i jego moduły.
Administrator przygotowuje tam obszar roboczy (źródło, środowisko, dodatkowe ustawienia, całe jądro albo nie) i
buduje moduły z folderu projektu albo z katalogu drzewa; oba działania są zadaniami. **Sprawdź** (developer) pokazuje
każde ustalenie z naprawą; **Załaduj** i **Wyładuj** (admin) robią to samo co `bvs kernel load` i `unload`, a
odrzucone ładowanie pokazuje sprawdzenie, które je zatrzymało. Plik `.ko` w dowolnym miejscu folderu danych można
sprawdzić po ścieżce.

## Zadania cron i operacje

Strona Zadania ma dwie listy: zaplanowane zadania (cron) i operacje (długie zadania, które wykonuje bvsd, na
przykład instalacje i przenoszenie danych).

### Zaplanowane zadania

Developer tworzy i zmienia własne zadania; admin zmienia wszystkie. Zadanie ma:

- Harmonogram: pięć pól cron (`0 3 * * *`), sześć z sekundami na początku albo skrót (`@daily`, `@hourly`,
  `@every 6h`). Przedrostek `CRON_TZ=<strefa>` wybiera inną strefę czasową; bez niego zadanie działa w strefie
  czasowej NAS-a, pokazanej na górze strony.
- Polecenie: wykonywane jako root przez `bash -lc` z pakietu. Kilka wierszy działa jako jeden skrypt, a jego wyjście
  staje się logiem uruchomienia.
- Miejsce działania: na NAS-ie albo w środowisku (z `bash` środowiska albo z `/bin/sh`, gdy środowisko nie ma
  basha).
- Folder roboczy: względem `projects/` w folderze danych albo pełna ścieżka. W środowisku widoczny jest tylko
  `projects/`, jako `/projects`.
- Limit czasu: w sekundach (0: bez limitu). Uruchomienie, które po nim nadal trwa, jest zatrzymywane i oznaczane
  jako przekroczenie limitu czasu.
- Gdy poprzednie uruchomienie jeszcze trwa: następne jest pomijane albo czeka (czeka najwyżej jedno).
- Po błędzie: nieudane albo przerwane po limicie czasu uruchomienie wysyła [powiadomienie](#powiadomienia).

Każde zadanie przechowuje ostatnie 100 uruchomień z końcówką ich wyjścia (4 KB). Pełny log uruchomienia jest
przechowywany 30 dni.

Zadanie działa jako root w imieniu swojego właściciela, z jego folderem domowym jako `HOME`, więc działa tylko wtedy,
gdy właściciel jest włączonym developerem albo adminem. Gdy właściciel jest wyłączony albo ma rolę użytkownika lub viewer,
każdy termin zadania jest zapisywany jako pominięte uruchomienie z powodem, np. „its owner ola is disabled”, a
**Uruchom teraz** odpowiada tym samym powodem. Powiadomienie nie jest wysyłane. Uruchomienie, które trwa, gdy
właściciel zostaje wyłączony albo dostaje rolę niższą niż developer, jest zatrzymywane. Zadanie znowu działa, gdy
konto zostanie włączone albo znowu będzie developerem. Usunięcie konta usuwa jego zadania. Zadania wbudowane działają
bez względu na to, co dzieje się z kontami.

Gdy polecenie się kończy, procesy uruchomione przez nie w tle działają dalej. Po przekroczeniu limitu czasu, usunięciu
zadania albo zatrzymaniu pakietu cała grupa procesów uruchomienia dostaje SIGTERM, a po 5 sekundach SIGKILL.

W noce zmiany czasu zadania działają według reguły crona z Debiana. Zadanie z `*` na początku pola minut lub
godzin (także `*/N`), `@hourly` albo `@every` liczy upływ czasu: jesienią działa w obu przejściach powtórzonej
godziny, a wiosną pominięta godzina nie nadchodzi. Każde inne zadanie wykonuje każdy swój termin raz: termin
w powtórzonej godzinie wykonuje się raz, a termin w pominiętej godzinie zaraz po zmianie.

Każde zalogowane konto, także `viewer`, widzi polecenia zadań i wyjście ich uruchomień. Nie wpisuj haseł ani tokenów
w polecenie: niech polecenie czyta je z pliku w `<data>/etc`, który czyta tylko root (`chmod 600`).

### Zadania wbudowane

| Zadanie | Kiedy | Co robi |
|---|---|---|
| Konserwacja | codziennie o 03:30 | usuwa z `tmp/` w folderze danych to, czego nic nie zmieniło przez 10 dni; rotuje `log/audit.log` po przekroczeniu 10 MB |
| Opróżnianie kosza | codziennie o 03:45 | usuwa z `trash/` w folderze danych to, co zostało tam przeniesione ponad 7 dni temu (na przykład folder domowy usuniętego konta) |
| Odświeżanie katalogu toolchainów | codziennie o 04:15 | pobiera podpisany katalog toolchainów |
| Rotacja logów nginx | co godzinę, w 7. minucie | zmienia nazwę `nginx-access.log` albo `nginx-error.log` na `.1`, gdy przekroczy 20 MB (pięć starszych plików zostaje), i każe nginxowi ponownie otworzyć logi |

Admin może zmienić harmonogram, limit czasu, zachowanie przy trwającym uruchomieniu i powiadomienie zadania
wbudowanego, a także je wyłączyć. Jego nazwa i polecenie się nie zmieniają.

### Operacje

Instalacje, zadania środowisk, przenoszenie danych i włączanie modułów są wykonywane jako operacje. Lista pokazuje
ostatnie 50, od najnowszej, z postępem i logiem. Operacje i ich logi są przechowywane 30 dni. Operacja, która trwała,
gdy pakiet się zatrzymał, ma stan „przerwane”. Developer anuluje własne operacje; admin anuluje każdą.

## Kopie zapasowe

Strona **Kopie zapasowe** (admin) ustawia, co jest kopiowane, kiedy, dokąd trafiają kopie i ile ich zostaje. **Zrób
kopię teraz** robi kopię w każdej chwili. Na nowej instalacji kopie według harmonogramu są wyłączone; po włączeniu
**Kopie według harmonogramu** kopia powstaje domyślnie codziennie (wbudowane zadanie cron `backup`). Zostaje najnowsza
kopia z każdego z ostatnich 7 dni, 4 tygodni i 6 miesięcy, a zawsze najnowsza udana. Naraz trwa jedna kopia,
weryfikacja, przywracanie albo usuwanie.

Dwie kopie z jednego dnia liczą się jako jeden dzień: gdy nowsza się skończy, wcześniejsza z tego dnia jest usuwana.
**Zrób kopię teraz** po południu zastępuje więc poranną kopię z harmonogramu; jeśli stan z rana jest ważny, najpierw
trzeba skopiować tę kopię w inne miejsce.

### Co zawiera kopia

Kopia to folder `<data>/backups/<id>/` z plikiem `manifest.json`, który wymienia każdy plik z jego sha256:

| Część | Co |
|---|---|
| ustawienia | `etc/`: ustawienia, konta, sekrety TOTP, klucz TLS, klucze kopii zapasowych |
| Git | zrzut Forgejo: repozytoria, wiki, obiekty LFS, załączniki i jego baza |
| SVN, buckety | kopia każdego repozytorium, przyrostowa: rewizja już skopiowana nie jest kopiowana ponownie |
| ustawienia VS Code | ustawienia VS Code każdego konta, skróty klawiszowe, fragmenty kodu (snippets) i lista jego rozszerzeń (bez plików rozszerzeń: widok Extensions instaluje je ponownie) |
| środowiska | środowiska ze strony Toolchainy (domyślnie wyłączone: da się je utworzyć ponownie) |
| projekty | każdy folder projektu z historią Gita i plikami jeszcze niezatwierdzonymi, bez `node_modules` |
| foldery HOME | folder HOME każdego konta, bez pamięci podręcznych i instalacji samych narzędzi AI (przycisk Zainstaluj je przywraca) |
| App Builder | ustawienia każdej aplikacji i historia jej budowań; budowanie tworzy pliki aplikacji ponownie |
| repozytorium pakietów | opublikowane pakiety, ich ikony i indeks, który czyta App Center |

Te cztery części są w każdej kopii, niezależnie od tego, co zaznaczono na stronie. Folder, który nie zmienił się od
poprzedniej kopii, jest dowiązany do jej egzemplarza i nie zajmuje miejsca.

Kopia staje się widoczna dopiero, gdy każda część jest zapisana i sprawdzona; błąd albo pełny wolumin usuwa
niedokończony folder i zostawia wcześniejsze kopie bez zmian. **Sprawdź** czyta kopię w całości: sha256 każdego
archiwum, `svnadmin verify` każdej rewizji, `git fsck` każdego repozytorium i sprawdzenie spójności bazy Forgejo.

### Miejsca docelowe

Każda kopia trafia do każdego miejsca docelowego ze strony:

- **Folder lokalny** na tym NAS-ie: kopia taka, jaka jest, z niezmienionymi plikami dowiązanymi do poprzedniej kopii,
  więc każda kopia zajmuje tylko to, co się zmieniło. Nieszyfrowana. Na tych samych dyskach co dane chroni przed
  pomyłkami, nie przed awarią woluminu.
- **rsync** (`użytkownik@host:ścieżka`) przez SSH. **Loguj się kluczem kopii zapasowych pakietu**: strona pokazuje
  klucz publiczny do dopisania w `authorized_keys` na drugiej maszynie.
- **rclone** (`zdalny:ścieżka`, zasób z `<data>/etc/rclone.conf`): chmura i wszystko, do czego sięga rclone.

Kopia, która opuszcza NAS (rsync, rclone), musi być szyfrowana: włącz **Szyfruj kopie, które opuszczają NAS** i
**Utwórz klucz**. Kopie są szyfrowane programem age do publicznej połowy klucza; **tajna połowa jest pokazana raz**,
bvsd jej nie przechowuje. Bez niej żaden NAS, także ten, nie przywróci zaszyfrowanej kopii: trzeba ją przechować
poza tym NAS-em. Nowy klucz szyfruje kopie od tej chwili; starsze kopie wymagają starego klucza tajnego.

Miejsce docelowe, które zawiedzie, jest wymienione w wyniku kopii; sama kopia pozostaje udana.

`rclone.conf` zawiera dane logowania do zasobów zdalnych. Zanim użyje go kopia albo **Test**, bvsd ustawia go (i klucz
kopii) tak, by czytał go tylko root (tryb 600), jeśli tak nie jest, a log zadania o tym mówi.

### Przywracanie

**Przywróć** przy kopii przywraca wszystko, jedno repozytorium (repozytorium SVN, bucket albo repozytorium Git) albo
jeden folder projektu:

- Nic nie jest podmieniane przed sprawdzeniem: każde archiwum wobec jego sha256, każda kopia SVN przez
  `svnadmin verify`. Kopia, która nie przejdzie, zatrzymuje przywracanie, zanim cokolwiek żywego się zmieni.
- Podmieniane żywe dane trafiają do `<data>/trash` na 7 dni, więc samo przywrócenie da się cofnąć.
- **Jedno repozytorium** albo **jeden folder projektu** wraca, podczas gdy moduły działają dalej. Folder projektu
  usunięty przez pomyłkę wraca z historią i plikami jeszcze niezatwierdzonymi.
- **Wszystko** zatrzymuje moduły, podmienia dane i uruchamia bvsd ponownie na przywróconych ustawieniach: każda
  sesja się kończy, a konta i hasła są takie jak w kopii. Folder projektu albo HOME utworzony po kopii zostaje.

## Powiadomienia

bvsd zapisuje każde powiadomienie w Dziennikach systemu QTS. Może też wysyłać je do komunikatorów i poczty przez
adresy URL shoutrrr (Telegram, Discord, Slack, e-mail, zwykły webhook i inne).

| Powiadomienie | Kiedy | Przełącznik dla adresów URL |
|---|---|---|
| błąd zadania | uruchomienie zadania cron z powiadomieniem po błędzie nie udało się albo przekroczyło limit czasu; operacja się nie udała | `notify.on_job_failure` |
| kopia zapasowa | wynik każdej kopii zapasowej, weryfikacji i przywracania, także udanych; zaplanowana kopia, która nie mogła wystartować | `notify.on_backup` |
| pętla restartów | proces wszedł w pętlę restartów | `notify.on_crashloop` |
| certyfikat | dodanemu certyfikatowi zostało 30 dni lub mniej; nginx wrócił do certyfikatu z podpisem własnym; powstał nowy certyfikat z podpisem własnym | `notify.on_cert_expiry` |
| system | ustawienia odrzucone przy starcie, znalezione niedokończone przenoszenie danych, zakończone pozostawione procesy, konfiguracja, której nie dało się zapisać | wysyłane zawsze |

- Przełączniki decydują tylko o tym, co idzie na adresy URL. Dzienniki systemu QTS dostają każde powiadomienie.
- To samo powiadomienie wychodzi najwyżej raz na 10 minut. Następne po tym czasie podaje, ile wstrzymano.
- Każdy adres URL dostaje własne `shoutrrr send` z limitem 10 sekund. Błąd trafia do logu tylko ze schematem adresu,
  bo reszta adresu zawiera jego token.

Adresy i przełączniki są w sekcji **Powiadomienia** na stronie System; przełączniki są domyślnie włączone. Admin
zmienia je tam. Pozostałe konta widzą przełączniki i tylko schemat każdego adresu, bo reszta adresu zawiera jego
token. Admin NAS-a może je też ustawić przez gniazdo 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=<id czatu>"]}}' \
  http://bvsd/api/v1/settings
```

## Logi i paczka diagnostyczna

Każdy log to `log/<nazwa>.log` w folderze danych. Logi bvsd i każdego procesu, którego pilnuje, są rotowane po
20 MB, a pięć starszych plików zostaje. `nginx-access.log` i `nginx-error.log` mają te same limity, sprawdzane przy
każdym starcie bvsd i co godzinę przez zadanie „Rotacja logów nginx”, więc każdy z nich może urosnąć ponad 20 MB
o ruch z co najwyżej godziny. Zadanie „Konserwacja” rotuje `audit.log`, gdy przekroczy 10 MB, i zostawia trzy
starsze pliki. Strona Logi (admin) pokazuje ostatnie wiersze każdego logu, filtruje je i pokazuje nowe wiersze na
żywo:

| Log | Kto go zapisuje |
|---|---|
| `bvsd` | sam bvsd: starty, zatrzymania, zastosowane ustawienia, problemy |
| `nginx` | własne wyjście nginx |
| `nginx-access`, `nginx-error` | każde żądanie i błędy nginx |
| `audit` | każde żądanie na gnieździe CLI |
| po jednym na proces modułu | program każdego modułu |

Strona System pokazuje wersję, platformę, strefę czasową i każdy proces, którego pilnuje bvsd. „Pobierz paczkę
diagnostyczną” (admin) tworzy jedno archiwum do zgłoszenia błędu:

- ustawienia z zamaskowanym hasłem SMTP i każdym adresem URL shoutrrr;
- wygenerowany `nginx.conf`, dane certyfikatu bez klucza, woluminy;
- stan każdego modułu, procesu i ostatnich operacji;
- ostatnie 2000 wierszy każdego logu, z każdym ciągiem zapytania zastąpionym przez `?(query removed)`.

Nigdy nie zawiera haseł, sesji, tokenów ani kluczy.

## Aktualizacje

bvsd czyta `https://pub.qnaputilities.com/bucketvs/latest.json` przy starcie i co 24 godziny. Gdy plik podaje nowszą
wersję, strona System ją pokazuje razem z odnośnikiem z tego pliku. bvsd tylko informuje o nowej wersji: nigdy nie
pobiera ani nie instaluje pakietu. Nieudane sprawdzenie niczego w panelu nie zmienia.

### Instalowanie nowej wersji

Nowy pakiet instaluje się na starym, z App Center albo przez **Instalację ręczną**. Instalacja zatrzymuje bvsd,
podmienia pliki programu i uruchamia go ponownie; folder danych, ustawienia, konta i wszystkie repozytoria zostają,
tak samo sesje panelu. Instancje edytora zatrzymują się razem z bvsd, więc otwarte terminale edytora się kończą; instancja
startuje znowu, gdy konto otworzy edytor.

**Z wersji, która używała portów 6050, 6051 i 6052.** Wcześniejsze wersje nasłuchiwały na 6050 (HTTP), 6051 (HTTPS)
i 6052 (Git przez SSH). Pierwszy start nowej wersji przenosi każdy z nich na 9950, 9951 i 9952 i mówi o tym w
powiadomieniu; port wybrany samodzielnie zostaje. Zmień to, co wskazuje stare porty: zakładki, proxy w Plesku na
6050 i zdalne repozytoria git, na przykład `ssh://git@<NAS>:6052/…` (`git remote set-url`).

### Usuwanie, ponowna instalacja i uszkodzony pakiet

Twoja praca zostaje, gdy pakiet zostanie usunięty, zainstalowany ponownie albo się uszkodzi. Wszystko, co tworzysz,
leży w folderze danych: konta i ustawienia, repozytoria Git i SVN, buckety, foldery projektów, foldery HOME,
ustawienia i rozszerzenia VS Code, aplikacje App Buildera, środowiska, repozytorium pakietów i kopie zapasowe. Folder
instalacji (`<wolumin>/.qpkg/bucketvs`) zawiera tylko programy i jeden plik, który wskazuje folder danych.

- **Usunięcie, potem ponowna instalacja.** Usunięcie pakietu kasuje folder instalacji, a zostawia folder danych i
  `/etc/config/bucketvs-data-root`, plik, który go wskazuje. Następna instalacja czyta ten plik i startuje na tym
  samym folderze danych: te same konta i hasła, te same projekty i repozytoria, razem z zalogowanymi sesjami.
- **Instalacja na zainstalowany pakiet.** Ta sama albo nowsza wersja podmienia pliki programów; folderu danych nie
  dotyka.
- **Uszkodzony pakiet.** Gdy folder instalacji jest uszkodzony (brakuje programu, folder usunięto ręcznie),
  zainstaluj pakiet ponownie z App Center albo przez **Instalacja ręczna**. Instalacja znajduje folder danych tak
  samo. bvsd, który wciąż działa z usuniętych plików, zostaje zatrzymany przy starcie i rusza ten zainstalowany.
- **Co kasuje dane.** Tylko usunięcie samego folderu danych. Żeby celowo zacząć od zera, usuń pakiet, usuń folder
  danych, potem zainstaluj pakiet: pierwszy start tworzy nowy folder danych, jak opisuje [Gdzie powstaje folder
  danych](#gdzie-powstaje-folder-danych).

Awaria dysku to inna sprawa: folder danych leży na dyskach samego NAS-a. Trzymaj kopie zapasowe na innej maszynie
([Kopie zapasowe](#kopie-zapasowe)); każda kopia zawiera Twoje foldery projektów, foldery HOME, ustawienia App
Buildera i repozytorium pakietów.

## Polecenie bvs

`bvs` to wiersz poleceń pakietu, w `/usr/bin/bucketvs/bin`. Jest na `PATH` każdego procesu, który uruchamia bvsd,
także zadań cron. Gdy pakiet działa, `/usr/bin/bvs` jest dowiązaniem do niego, więc w sesji SSH na NAS-ie też można
go wywołać po nazwie; zatrzymanie pakietu usuwa to dowiązanie.

Każde polecenie, które łączy się z bvsd przez gniazdo CLI, wymaga uprawnień root, bo gniazdo jest tylko dla roota:
`user`, `jobs`, a gdy bvsd działa, także `tc install`, `tc remove` oraz `env create`, `clone`, `export` i `delete`.
Uprawnień root wymagają też `env shell`, `env run` i `qbuild`, bo uruchamiają `bvs-chroot`. Uruchomione przez innego
użytkownika polecenie, które potrzebuje gniazda, kończy się komunikatem „bvsd's CLI socket … is open to root only:
run bvs as root”.

```text
bvs env list
bvs env create <nazwa> --from <id-systemu-plików-root> [--package <nazwa>]...
bvs env shell <nazwa>
bvs env run <nazwa> -- <polecenie> [<argument>...]
bvs env clone <nazwa> <nowa-nazwa>
bvs env export <nazwa>
bvs env delete <nazwa>
bvs tc list
bvs tc install <id>
bvs tc remove <id>
bvs use <id>
bvs jobs
bvs user list
bvs user password <nazwa>
bvs user totp-reset <nazwa>
bvs guide [--path] [en|pl|de|fr|it|nl|sv|ja|zh-TW]
bvs qbuild [--env <nazwa>] [<argument-qbuild>...]
bvs kernel profile [--json]
bvs kernel sources
bvs kernel prepare [--source <id>] [--env <nazwa>] [--option NAZWA=WARTOŚĆ]... [--full]
bvs kernel make [--project <folder> | --dir <katalog-drzewa>] [--build-only] [<cel>...]
bvs kernel check <plik.ko | moduł>
bvs kernel load <moduł>
bvs kernel unload <moduł>
```

- Gdy bvsd działa, instalacje i zadania środowisk idą do bvsd przez jego gniazdo CLI, więc panel je pokazuje, a `bvs`
  śledzi je do końca. Bez bvsd wykonują się w samym procesie `bvs`; `--local` przed poleceniem robi tak celowo.
- `bvs jobs` i polecenia `user` wymagają działającego bvsd.
- `bvs guide` otwiera ten przewodnik programem `less` z pakietu w terminalu, a w innym przypadku lub z `--path`
  wypisuje ścieżkę jego pliku. Bez podanego języka kieruje się pierwszą ustawioną z `LC_ALL`, `LC_MESSAGES` i
  `LANG`: `pl_PL.UTF-8` otwiera przewodnik polski, `de_DE.UTF-8` niemiecki, `zh_TW` i `zh_HK` chiński tradycyjny, a
  język, w którym przewodnika nie ma (`C`, `zh_CN`), angielski.

## Pliki i foldery

| Ścieżka | Co to jest |
|---|---|
| `/usr/bin/bucketvs` | programy pakietu (dowiązanie do jego folderu instalacji) |
| `/usr/bin/bucketvs/etc/data-root` | ścieżka folderu danych |
| `/usr/bin/bucketvs/var/run/bvsd.sock` | gniazdo API panelu (używa go nginx) |
| `/usr/bin/bucketvs/var/run/bvsd-cli.sock` | gniazdo polecenia `bvs`, tylko dla roota |
| `<folder danych>/etc/` | ustawienia, konta, sesje, tokeny, certyfikaty, wygenerowany `nginx.conf` (poufne pliki z trybem 600) |
| `<folder danych>/log/` | logi |
| `<folder danych>/tmp/` | pliki tymczasowe każdego procesu |
| `<folder danych>/home/<konto>/` | folder domowy każdego konta |
| `<folder danych>/jobs/` | operacje, zadania cron i ich uruchomienia, z logami |
| `<folder danych>/toolchains/` | pamięć podręczna pobrań, zainstalowane toolchainy i środowiska uruchomieniowe, eksporty |
| `<folder danych>/envs/` | środowiska |
| `<folder danych>/projects/` | obszary robocze, widoczne w środowiskach jako `/projects` |
| `<folder danych>/trash/` | usunięte elementy, przechowywane 7 dni |

Moduły dodają własne foldery; wymieniają je ich części.
