gvx: Uruchamianie kontenerów Linux, Docker i Kubernetes w jednym nieuprzywilejowanym procesie

Wprowadzenie: kontenery o wzmocnionym zabezpieczeniu bez roota i demonów hosta
Uruchamianie kontenerów Linux, docker build lub Kubernetes na maszynie deweloperskiej, runnerze CI lub węźle chmury wielodostępowej zazwyczaj wymusza niewygodny kompromis w zakresie bezpieczeństwa:
- W systemie Linux standardowe runtime'y OCI (
runc,crun,runsc) wymagają albo uprawnieńroot, udostępnienia gniazda/var/run/docker.sockhosta, flagi--privileged(CAP_SYS_ADMIN), albo przestrzeni nazw użytkowników (unshare(CLONE_NEWUSER)), które poszerzają powierzchnię ataku jądra hosta. - W systemach Windows i macOS silniki kontenerów (
Docker Desktop,Podman Machine,Lima,OrbStack) uruchamiają pełne jądro maszyny wirtualnej Linux w ramach WSL2, Hyper-V lubVirtualization.frameworki utrzymują trwały demon działający w tej maszynie wirtualnej.
gvx (CLI rungv oraz Go SDK z.gfire.dev/gvx/pkg/gvx) stanowi bezpieczną alternatywę bez uprawnień. Zbudowany jako śledzący upstream fork przestrzeni użytkownika jądra Linux gVisor firmy Google (Sentry) oraz stosu sieciowego w przestrzeni użytkownika (netstack), gvx uruchamia niezmodyfikowane pliki binarne linux/amd64 i linux/arm64 w formacie ELF64 oraz kontenery OCI bezpośrednio w jednym, nieuprzywilejowanym, samozablokowanym procesie hosta w systemach Windows, macOS i Linux.
Gwarancja bezpieczeństwa (trzypierścieniowa obrona w głąb):
gvxnigdy nie osłabia granic bezpieczeństwa systemu operacyjnego hosta, nigdy nie instaluje sterowników jądra i nigdy nie wymagaroot,sudo,--privilegedna hoście aniCLONE_NEWUSER. Zanim wykona choćby jedną instrukcję gościa,gvxblokuje własny proces hosta przy użyciu Windows Restricted Tokens i Job Objects, macOS Seatbelt (deny default) lub LinuxPR_SET_NO_NEW_PRIVSoraz BPFseccomp. Wszystkie wywołania systemowe gościa, wirtualne uprawnieniaUID 0, montowania (VFS2) i tablice sieciowe (netstack) są ściśle ograniczone wewnątrz bezpiecznej pod względem pamięci jądra GoSentry.
| Runtime | System operacyjny hosta | Zimny start | Zużycie pamięci | Wymagane uprawnienia administratora lub sudo | Demon maszyny wirtualnej w tle | Bezpośrednie uruchamianie obrazu OCI |
|---|---|---|---|---|---|---|
gvx (whpx / hvf) | Windows (amd64, arm64) i macOS (arm64) | ~20 ms | ~30 MiB | Nie | Nie | Tak |
gvx (systrap) | Windows (amd64, arm64) i Linux (amd64, arm64) | ~25 do 65 ms | ~31 MiB | Nie | Nie | Tak |
| WSL2 / Docker Desktop | Windows i macOS | 800 do 2500 ms | > 512 MiB | Tak (konfiguracja początkowa) | Tak | Wymaga demona |
| QEMU (pełny system) | Windows i macOS | 1500 do 4000 ms | > 256 MiB | Nie | Nie | Wymaga obrazu dysku |
Na systemach Windows i macOS nie uruchamia się żadne jądro maszyny wirtualnej Linux. Na hoście nie działa żaden demon w tle. Na systemie operacyjnym hosta nie są wymagane uprawnienia root, sudo, przestrzenie nazw użytkowników (CLONE_NEWUSER), mount(2), sterowniki TUN/TAP ani CAP_NET_ADMIN. Wewnątrz tego nieuprzywilejowanego procesu gvx uruchamia Docker-in-Docker (docker:dind), docker build, docker compose oraz wysoko dostępny, wieloWęzłowyKubernetes (k3s z osadzonym etcd i wtyczką CNI Calico).
Cykl życia kontenera i trzypierścieniowa blokada hosta
Każda piaskownica gvx działa w jednym nieuprzywilejowanym procesie hosta i realizuje trzyfazowy cykl życia: wstępne otwarcie wszystkich wymaganych deskryptorów plików i gniazd hosta, zablokowanie procesu systemu operacyjnego hosta za pomocą pkg/hostlockdown oraz przejście do pętli wykonawczej Sentry działającej w procesie.

Przed przekazaniem sterowania do pierwszej instrukcji gościa gvx wstępnie otwiera buforowane deskryptory warstw obrazów .erofs, deskryptory plików woluminów .ext4, uchwyt *os.Root katalogu /workspace oraz gniazda sieciowe hosta, a następnie wywołuje hostlockdown.Apply():
- Windows NT (
lockdown_windows.go): wywołujeAdjustTokenPrivilegeszSE_PRIVILEGE_REMOVED, aby usunąć uprawnienia w miejscu, wiąże token ograniczonego podszywania sięDISABLE_MAX_PRIVILEGEna niskim poziomie integralności (S-1-16-4096), przypisuje proces do anonimowego Job Object zJOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1(blokując tworzenie procesów potomnych) oraz włączaDisallowWin32kSystemCalls = 1przezSetProcessMitigationPolicy. - macOS XNU (
lockdown_darwin.go): ustawiaRLIMIT_NPROC = 0i stosuje profil Seatbelt (sandbox_init_with_parameters) rozpoczynający się od(version 1)(deny default), który blokujeprocess-fork,process-exec*oraz wszystkie nowe otwarcia plików hosta. - Linux (
lockdown_linux.go+pkg/gvx/syscallfilter_linux.go): ustawiaPR_SET_NO_NEW_PRIVSi instaluje listę dozwolonych wywołań BPFseccompSentry dla wszystkich wątków (SECCOMP_FILTER_FLAG_TSYNC) wraz z jawnymi regułamiEPERMdlaexecve,execveat,clone,openatisocket.
Przechwytywanie wywołań systemowych między platformami i stronicowanie 4 KB (pkg/sentry/platform)
Pliki binarne Linux ELF64 zakładają ziarnistość stron pamięci 4 KB (0x1000) przy mapowaniu segmentów przez mmap(MAP_FIXED). Tymczasem VirtualAlloc i MapViewOfFile3 w systemie Windows wymuszają wyrównanie do 64 KB (0x10000), a macOS na Apple Silicon (darwin/arm64) używa stron hosta o rozmiarze 16 KB (0x4000). Ponadto każdy system operacyjny wymaga innego mechanizmu, aby przechwycić instrukcje Linux SYSCALL (amd64) i SVC #0 (arm64) bez uruchamiania maszyny wirtualnej Linux.
gvx rozwiązuje oba problemy w czterech backendach wykonawczych, które implementują pkg/sentry/platform.Context:

1. Sprzętowy silnik Windows: platform/whpx (WinHvPlatform.dll)
W systemach windows/amd64 i windows/arm64 platform/whpx tworzy lekką partycję hipernadzorcy za pomocą WHvCreatePartition, bez uruchamiania jądra systemu gościa:
- Dekoplер stronicowania 64 KB na 4 KB:
whpxrejestruje każdą 64-megabajtową arenępgalloc.MemoryFilew partycji jednorazowo, za pomocąWHvMapGpaRange. Kolejne wywołaniaMapFileiUnmapmodyfikują wyłącznie tablice stron gościa wewnątrz partycji, egzekwując dokładne uprawnieniaR/W/Xna poziomie 4 KB, mimo ziarnistości alokacji 64 KB w systemie Windows. - Wymiana rejestrów bez wywołań API przez
CommPage:- Na
amd64instrukcjaSYSCALLwykonana w Ring-3 gościa wchodzi do stubuMSR_LSTARdziałającego w Ring-0 wewnątrz partycji (stubgen_x86.go), który zapisuje rejestry GPR ścieżki szybkiej (RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP) we wspólnejCommPageo rozmiarze 4 KB i wykonujeHLT, aby zakończyć działanieWHvRunVirtualProcessor. - Na
arm64instrukcjaSVC #0wykonana w EL0 gościa wchodzi do wektorów EL1 (stubgen_a64.go), zapisujeX0..X8, SP_EL0, ELR_EL1, PSTATEwCommPagei odczytuje z niezamapowanego adresu GPA zero, aby wywołać wyjście pamięciowe z powrotem do Sentry.
- Na
2. Sprzętowy silnik macOS: platform/hvf (Hypervisor.framework)
Na Apple Silicon (darwin/arm64) platform/hvf uruchamia wątki kontenerów wewnątrz vCPU Hypervisor.framework (hv_vm_create, hv_vcpu_run):
- Dwuetapowa translacja MMU 4 KB nad 16 KB: Stage-2 (
hv_vm_map) mapuje fragmenty pliku pamięci o rozmiarze 1 GiB do przestrzeni Intermediate Physical Address (IPA) jednorazowo, z ziarnistością stron hosta 16 KB, z uprawnieniamiHV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC. Wewnątrz vCPU stub EL1bluepill(bluepill_arm64.go) konfigurujeTCR_EL1.TG0 = 0b00(granula 4 KB) wTTBR0_EL1i wypełnia wpisy tablic stron Stage-1 o rozmiarze 4 KB z dokładnymi uprawnieniamiR-XlubRW-(UXN=1). Ponieważ MMU ARM64 wymusza przecięcie uprawnień Stage-1 i Stage-2, sprzęt przechwytuje błędy uprawnień na dokładnych granicach 4 KB. - Pułapka wywołania systemowego: instrukcja
SVC #0wykonana w EL0 gościa trafia doVBAR_EL1w EL1, zapisuje rejestry we wspólnejCommPageo rozmiarze 4 KB i wykonujeHVC #0, aby powrócić zhv_vcpu_rundo wątku Go Sentry.
3. Rezerwowy tryb użytkownika Windows: platform/systrap (winsystrap)
Gdy sprzętowa wirtualizacja jest niedostępna (na przykład wewnątrz zagnieżdżonych maszyn wirtualnych w chmurze), winsystrap uruchamia kod gościa w trybie użytkownika w systemach windows/amd64 i windows/arm64:
- Izolacja sprzętowa
CR3/TTBR0_EL1: wątki gościa działają wewnątrz izolowanych podprocesów stubowych uruchamianych z wygenerowanego pliku wykonywalnego o rozmiarze 1 KB (stubimage_xplat.go), ograniczonych tokenem o niskim poziomie integralności i Job Object. - Łatanie binarne w miejscu i IPC
winSysmsg: naamd64moduł łatający (patcher_amd64_xplat.go) zastępuje 2-bajtowe instrukcjesyscall(0f 05) 5-bajtowymi skokamijmp rel32(lub pułapkami punktu przerwaniaCC 90, gdy 5-bajtowe okno jest niedostępne), kierującymi do wyspy ścieżki szybkiej. Naarm64instrukcjasvc #0jest łatana gałęziąBdo slotu wyspy. Wyspa zapisuje rejestry we wspólnej stroniewinSysmsgi synchronizuje się z Sentry za pomocąWaitOnAddressorazWakeByAddressSingle, podczas gdy niełatane pułapki i błędy stron zatrzymują działanie na obiekcie debugowania Windows (WaitForDebugEventEx).
4. Silnik trybu użytkownika Linux: platform/systrap
Na linux/amd64 i linux/arm64 systrap uruchamia wątki gościa (sysmsg), które przechwytują wywołania systemowe gościa za pomocą SECCOMP_RET_TRAP (SIGSYS) i łatają binarnie miejsca syscall w trampolinę we wspólnej pamięci (sysmsg), synchronizowaną przez SYS_FUTEX. Jednocześnie wątek stubowy syscallThread (syscall_thread.go) wykonuje operacje na przestrzeni adresowej (mmap, munmap, clone) za pośrednictwem SECCOMP_RET_USER_NOTIF (_RUN_SECCOMP_LOOP). Gdy gvx działa wewnątrz zewnętrznej piaskownicy gVisor (runsc), w której pojedynczy slot SECCOMP_FILTER_FLAG_NEW_LISTENER zwraca EBUSY, gvx automatycznie przełącza syscallThread na pętlę futex we współdzielonej pamięci (_RUN_SYSCALL_LOOP), co zostało szczegółowo opisane w części 3 tej serii.
Jak gvx mapuje wywołania systemowe Linux gościa na wirtualne podsystemy i uchwyty hosta
Gdy silnik platformy przechwyci instrukcję SYSCALL (amd64) lub SVC #0 (arm64) gościa i zapisze rejestry gościa we wspólnej stronie 4 KB (CommPage, sysmsg lub winSysmsg), sterowanie wraca do goroutine'a zadania Go Sentry (pkg/sentry/kernel/task_syscall.go). Zamiast przekazywać wywołania systemowe Linux do jądra systemu operacyjnego hosta, Task.doSyscall() zapisuje oryginalny stan rejestrów (SyscallSaveOrig()), ocenia wszelkie filtry BPF seccomp gościa oraz zatrzymania ptrace, po czym przekazuje numer wywołania systemowego przez linux.AMD64 lub linux.ARM64 (pkg/sentry/syscalls/linux/linux64.go, obejmujące około 370 wywołań systemowych Linux) do czterech wewnętrznych domen:

| Domena wywołań systemowych | Reprezentatywne wywołania systemowe Linux gościa | Wewnętrzny podsystem gvx (pkg/sentry) | Interakcja z granicą systemu operacyjnego hosta (po hostlockdown) |
|---|---|---|---|
| A. Procesy, przestrzenie nazw, montowania i synchronizacja | clone, execve, unshare, setns, mount, umount2, pivot_root, setuid, capset, futex, epoll_wait, pipe2 | kernel.Task, wirtualne poświadczenia, tablica futex oraz VFS2 w pamięci (procfs, sysfs, cgroupfs v2, tmpfs, overlayfs) | Zero wywołań systemowych hosta (100% stanu Go w pamięci; całkowicie niewidoczne dla systemu operacyjnego hosta) |
B. Pamięć wirtualna (MM) | mmap, munmap, mremap, mprotect, brk, madvise, memfd_create | mm.MemoryManager (drzewo VMA 4 KB) oraz pgalloc.MemoryFile | Dwukierunkowe aktualizacje tablic stron 4 KB przez WHvMapGpaRange, hv_vm_map + TTBR0_EL1 lub stubowe mmap/mprotect |
C. Pliki i pamięć masowa (VFS2) | openat, pread64, pwrite64, getdents64, statx, renameat2, setxattr, mknod, inotify_add_watch | Router tabeli montowań vfs.VirtualFilesystem (fsimpl/erofs, fsimpl/ext4lite, fsimpl/hostfs v2) | Dwukierunkowe pread/pwrite na 1 przypiętym deskryptorze .erofs lub 1 deskryptorze .ext4, albo ograniczone względne przejścia po *os.Root |
| D. Sieć i Netfilter | socket, bind, listen, connect, sendmsg, recvmsg, netlink, setsockopt(IPT_SO_SET_REPLACE) | pkg/tcpip w przestrzeni użytkownika (lo, eth0, docker0, veth, vxlan, wireguard, ipset, iptables-legacy) | Ruch wewnątrz piaskownicy nie wykonuje żadnych wywołań systemowych hosta; ruch zewnętrzny mapuje się na net.Dial/net.Listen (-net=nat) lub 1 gniazdo UDP (-net=wg) |
Gdy handler zwraca (rval, ctrl, err), Task.doSyscallInvoke() przekształca każdy błąd w ujemny errno Linux (-ExtractErrno(err, sysno)), zapisuje wartość zwrotną z powrotem do współdzielonej strony rejestrów (RAX na amd64, X0 na arm64) i wznawia wykonywanie kodu gościa.
Pamięć masowa (VFS2) i dwutorowy stos sieciowy w przestrzeni użytkownika (netstack)
Wszystkie operacje plikowe i sieciowe gościa są obsługiwane wewnątrz Go Sentry przez trzy systemy plików VFS2 oraz dwa tory pkg/netbackend:

| Silnik pamięci masowej | Rola montowania w gvx | Zasób bazowy hosta | Wywołania hosta openat / NtCreateFile podczas działania | Skanowania WdFilter.sys (Windows Defender) dla pojedynczych plików |
|---|---|---|---|---|
fsimpl/erofs | Dolne warstwy obrazów OCI tylko do odczytu | Pojedynczy plik <sha256>.erofs na warstwę | 0 | 0 |
fsimpl/ext4lite | Nazwane woluminy (-v name:/path) | Pojedynczy plik <name>.ext4 na wolumin | 0 | 0 |
fsimpl/hostfs v2 | Obszar roboczy hosta (-workspace, -v /host:/guest) | Przypięty katalog hosta *os.Root | O(depth) na każde nieobsłużone z pamięci podręcznej otwarcie | 1 na zamknięcie pliku hosta |
Dlaczego ext4lite ma znaczenie dla Dockera, baz danych i menedżerów pakietów
Rozpakowywanie lub zapisywanie dziesiątek tysięcy plików kontenerów bezpośrednio na systemie plików NTFS w Windows lub APFS w macOS uruchamia skanowanie przez minifiltry antywirusowe (WdFilter.sys), kolizje wynikające z rozróżniania wielkości liter oraz brak rozszerzonych atrybutów POSIX.
pkg/sentry/fsimpl/ext4lite implementuje czysto w Go system plików ext4 wewnątrz jednego wstępnie otwartego pliku hosta (%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4 w systemie Windows, ~/Library/Caches/gvx/volumes/<name>.ext4 w macOS lub ~/.cache/gvx/volumes/<name>.ext4 w Linux), z następującymi cechami:
- Bloki 4 KiB i dynamiczne grupy bloków po 16 MiB (
meta_bg): rosną dynamicznie bez zacięć wynikających z alokacji plików rzadkich (fallocatew Linux,fcntl(F_PREALLOCATE)w macOS,FileAllocationInfow Windows) i kompaktują nieużywane końcowe grupy przy czystym odmontowaniu. - Dziennik JBD2 w trybie uporządkowanym (inode 8): atomowe zatwierdzanie transakcji z sumami kontrolnymi CRC-32 i blokami revoke, a także odzyskiwanie za pomocą
fsckLite. - Obsługa warstwy górnej
overlayfs: implementuje rozszerzone atrybutytrusted.overlay.*isecurity.capability, wymazania za pomocą urządzeń znakowych (0:0), raportowanied_typewpisów katalogów orazRENAME_EXCHANGE.
Praktyczne obciążenia: Docker Engine, Kubernetes (k3s + Calico) i Go SDK
Instalacja rungv: gotowe wydania z atestacją SLSA lub go install
Gotowe archiwum rungv możesz pobrać dla linux/amd64, linux/arm64, darwin/arm64, windows/amd64 lub windows/arm64 z GitHub Releases (każde zbudowane przez potok wydań SLSA z pakietem pochodzenia Sigstore i plikiem SHA256SUMS) albo zainstalować bezpośrednio ze źródeł za pomocą go install:
1# Opcja A: Pobierz gotowe wydanie i zweryfikuj pochodzenie builda SLSA
2gh attestation verify rungv-v0.2.0-linux-amd64.tar.gz --repo gfire-sigs/gvx \
3 --signer-workflow gfire-sigs/gvx/.github/workflows/release.yml \
4 --source-ref refs/tags/v0.2.0 --deny-self-hosted-runners
5shasum -a 256 -c SHA256SUMS --ignore-missing
6tar -xzf rungv-v0.2.0-linux-amd64.tar.gz
7
8# Opcja B: Zainstaluj bezpośrednio przez Go (Linux / Windows)
9CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
W systemie macOS (
darwin/arm64) gotowe archiwum wydania jest już podpisane uprawnieniemcom.apple.security.hypervisor(xattr -d com.apple.quarantine rungvusuwa kwarantannę przeglądarki). Jeśli budujesz lokalnie za pomocągo installlubgo buildna macOS, podpisz plik binarny poleceniemcodesign --sign - --entitlements entitlements.plist --force $(which rungv).
1. Uruchamianie nieuprzywilejowanego Docker Engine (docker:dind)
Zgodnie z docs/docker-engine.md możesz uruchomić niezmodyfikowany kontener docker:dind wewnątrz rungv w systemie Windows, macOS lub Linux i sterować nim za pomocą lokalnego CLI docker:
1# 1. Utwórz katalog hosta dla przekazywanego gniazda Unix
2mkdir -p ~/.gvx
3
4# 2. Uruchom docker:dind w piaskownicy gvx z woluminem ext4lite
5rungv run \
6 -p unix:~/.gvx/docker.sock:2375 \
7 -p 127.0.0.1:9000-9019:9000-9019 \
8 -v gvx-docker:/var/lib/docker \
9 -e DOCKER_TLS_CERTDIR= \
10 docker:dind --tls=false
W drugim terminalu skieruj lokalne CLI docker na gniazdo piaskownicy:
1docker context create gvx --docker host=unix://$HOME/.gvx/docker.sock
2docker context use gvx
3
4docker info --format '{{.Driver}} {{.OperatingSystem}}'
5# Wynik: overlayfs Alpine Linux ... (containerized)
6
7docker build -t myapp ./app
8docker compose up -d
2. Uruchamianie Kubernetes (k3s + Calico) w nieuprzywilejowanych piaskownicach
Ponieważ gvx implementuje /dev/kmsg, cgroup v2 (zabójca OOM oparty na memory.max oraz ograniczanie przepustowości cpu.max), /proc/diskstats, urządzenia netlink wireguard i vxlan, ipset oraz iptables-legacy (addrtype, conntrack, statistic, recent), niezmodyfikowany obraz rancher/k3s osiąga stan Ready w obrębie pojedynczego procesu rungv:
1mkdir -p ~/.gvx/k3s
2
3rungv run \
4 -e IPTABLES_MODE=legacy \
5 -p 127.0.0.1:6443:6443 \
6 -p 127.0.0.1:30000-30019:30000-30019 \
7 -v k3s:/var/lib/rancher \
8 --mount type=bind,src=$HOME/.gvx/k3s,dst=/output \
9 rancher/k3s:v1.31.4-k3s1 server \
10 --disable=traefik,servicelb \
11 --write-kubeconfig=/output/kubeconfig.yaml \
12 --write-kubeconfig-mode=644
Możesz także połączyć wiele węzłów gvx przez WireGuard (flannel-backend=wireguard-native lub siatka wg0) z osadzonym etcd w wysokiej dostępności albo uruchomić Calico (calico-vxlan.yaml z FELIX_IPTABLESBACKEND=Legacy), aby w pełni w przestrzeni użytkownika egzekwować NetworkPolicy Kubernetes.
3. Osadzanie kontenerów Linux w Go za pomocą z.gfire.dev/gvx/pkg/gvx
Aplikacje Go mogą osadzać kontenery Linux bezpośrednio, bez zewnętrznego demona kontenerów:
1package main
2
3import (
4 "context"
5 "fmt"
6 "os"
7
8 "z.gfire.dev/gvx/pkg/gvx"
9)
10
11func main() {
12 ctx := context.Background()
13 sb, err := gvx.New(ctx, gvx.Config{
14 Platform: gvx.PlatformAuto,
15 NetworkMode: gvx.NetModeNAT,
16 OCIImage: "alpine:3.20",
17 WorkspacePath: "./repo",
18 Argv: []string{"/usr/bin/git", "status"},
19 Stdout: os.Stdout,
20 Stderr: os.Stderr,
21 })
22 if err != nil {
23 fmt.Fprintf(os.Stderr, "init sandbox: %v\n", err)
24 os.Exit(1)
25 }
26 defer sb.Close()
27
28 exitCode, err := sb.Run(ctx)
29 if err != nil {
30 fmt.Fprintf(os.Stderr, "run sandbox: %v\n", err)
31 os.Exit(1)
32 }
33 os.Exit(exitCode)
34}
Dalsza lektura w tej serii
- gvx: Uruchamianie kontenerów Linux, Dockera i Kubernetes w jednym nieuprzywilejowanym procesie (ten wpis)
- Uruchamianie Dockera, docker build i docker compose bez roota i przestrzeni nazw użytkowników: jak uruchomić
docker:dindw zablokowanych piaskownicach, w których zablokowane sąsudo,unshare(CLONE_NEWUSER),mount(2)i/dev/fuse. - Uruchamianie Docker-in-Docker wewnątrz gVisor i zagnieżdżonych piaskownic z gvx: dlaczego
apt-get install docker.iooraz zagnieżdżonerunsckończą się niepowodzeniem wewnątrzgVisorna pięciu barierach jądra oraz jakgvxuruchamia zagnieżdżonydocker:dindprzy użyciu pętli futex we współdzielonej pamięci. - Szybkie izolowane środowiska Docker dla rolloutów uczenia ze wzmocnieniem agentów AI z gvx: skalowanie
SWE-benchi rolloutów uczenia ze wzmocnieniem agentów z setkami równoległych nieuprzywilejowanych instancjidocker:dindoraz 100-milisekundowymi resetami woluminów.ext4z kopiowaniem przy zapisie.