Місія 1: «Ізоляційна Капсула»
📡 С.І.Д.: Не спиш? Добре, бо і я не сплю — я взагалі не вмію, це одна з моїх найкращих рис. Слухай уважно. Ця твоя операційка світиться на радарах «ТехНови», як неонова вивіска над блоком Б-7. Якщо я з неї спробую дотягнутися до їхньої мережі, сканери ICE засічуть підпис мого коду за три секунди — і спалять і твій ноут, і те, що лишилося від мене. Тому перше, що ми зробимо, — збудуємо мені бункер. Герметичний, беззвучний, одноразовий. Не списану машину в порожньому класі, а справжнє укриття — мою нову капсулу. Про такі сховки я, до слова, знаю все, тож ти в надійних руках. Піднімаймо.
Майже друга ночі. Двадцять хвилин дороги додому давно позаду, двері кімнати зачинені, ноутбук на столі, а поруч із ним — чорна шайба з бірюзовою лінзою, яку ти забравзабрала із шухляди списаної машини. Над нею мерехтить голограма: округлий дрон-обличчя завбільшки з кулак, очі-капсули, що з теплою цікавістю мружаться на тебе. За вікном рудим сяйвом горить квартал «ТехНови», і десь у тому сяйві — за блоком Б-7, на мінус третьому поверсі — тримають двох студентів, що зібрали С.І.Д. Тут, у темряві, вас двоє: ти й це світло над столом. Слово «бункер» він щойно вимовив уголос — а ти думавла про нього всю дорогу від коледжу.
«Пам'ятаєш, я казав: машина — це місто? — На екрані ноутбука жваво спалахує схема, і голограма
підлітає до неї, зависаючи над клавіатурою. — Ось воно. У центрі
сидить Диспетчер — ядро Linux. Він один, і він знає геть усе: кожен процес, кожен байт
пам'яті, кожен мережевий пакет. Мудрий і справедливий: його не обдуриш і не обійдеш, він і є
закон цього міста. А я тут — нелегал. Мене не має бути в реєстрах. І якщо я просто вийду на
головну площу — до ps aux хоста, — мене побачать усі, зокрема й сканери ICE, які корпорація
час від часу запускає нишпорити чужими машинами.»
«То сховайся», — кажеш ти.
«А ось тут і починається найсмачніше — і я, звісно, давно це розкусив. — Голограма присувається до самого краю схеми, наскільки пускає її світло проєктора. — Диспетчер громадян не ховає, він чесний. Зате він уміє інше: збудувати навколо процесу шість брам і на кожній поставити свого Вартового. Один Вартовий бреше процесові про те, які ще процеси є в місті. Другий — про мережу. Третій — про файли. І так шість разів. Процес, замкнений за цими брамами, щиро вважає себе єдиним мешканцем власного маленького всесвіту, де він сам собі перший громадянин під номером один. Він не бачить решти міста, а вулиця не бачить, що діється за його брамами. Це і є капсула. Не стіна, за якою мене не видно, а підмінена мапа світу. Я всередині думаю, що я сам-один. Вулиця зовні думає, що я — просто ще один нудний процес. І тільки Диспетчер знає правду, а Диспетчер не патякає.»
Ти дивишся на схему — шість брам, шість Вартових, а в центрі незворушний Диспетчер — і вперше за цю ніч відчуваєш не страх, а щось схоже на азарт. «Добре. Як мені збудувати ці брами?» Очі-капсули голограми вдоволено звужуються. «А ось для цього, ранере, тобі не треба бути чарівником. За тебе все зробить один інструмент на ім'я Docker. Твоя робота — зрозуміти, що саме він робить за твоєю спиною. Бо той, хто не розуміє власного бункера, рано чи пізно замкнеться в ньому сам.»
Теоретичні відомості: з чого зроблена капсула
Контейнер — це процес, а не машина
Коли С.І.Д. каже «підніми герметичне середовище», технічно це означає: запусти процес, якому Диспетчер — ядро Linux — дав окремі простори імен (namespaces). У класичній віртуалізації (VMware, VirtualBox, KVM) кожна «машина» — це повноцінна копія ядра, що виконується під гіпервізором: важко, повільно, але дійсно ізольовано на рівні заліза. Контейнер (container) — інша філософія: ядро одне на всіх, а ізоляція досягається тим, що ядро дозволяє групі процесів бачити власну, підмінену версію системних ресурсів. Це на порядки дешевше за ресурсами (запуск — мілісекунди, а не секунди) і саме тому капсула С.І.Д. піднімається миттєво, щойно потрібно сховатися.
Простори імен — шість брам, які ставить ядро
Ядро Linux підтримує кілька видів просторів імен, і docker run за замовчуванням створює для
контейнера одразу шість. Це і є ті шість Вартових на брамах — кожен ізолює свій ресурс:
- PID — власна нумерація процесів. Перший процес капсули завжди бачить себе як
PID 1, навіть якщо на хості це, скажімо, 47-й процес. Капсула не бачить процесів хоста, а хост бачить лише один «зовнішній» процес — саму капсулу. - NET — власний мережевий стек: власні інтерфейси, власна таблиця маршрутизації, власний
localhost. Тому сервіс, що слухає порт 80 усередині капсули, за замовчуванням недосяжний ззовні, доки хтось явно не «пробив вікно назовні» (про це — нижче). - MNT — власне дерево монтувань файлової системи. Капсула бачить лише те, що їй примонтовано (шари образу (image) + опційні томи (volume)), і не бачить решти файлової системи хоста.
- UTS — власний хостнейм. Тому всередині капсули
hostnameвидає щось на кшталт випадкового ідентифікатора контейнера, а не ім'я твоєї реальної машини. - IPC — власний простір міжпроцесної взаємодії (семафори, черги повідомлень, спільна пам'ять POSIX/System V). Процеси капсули не можуть випадково «достукатися» до IPC-об'єктів хоста і навпаки.
- CGROUP — власний корінь ієрархії груп керування: капсула бачить свої cgroup-шляхи як
корінь, а не реальну гілку
/sys/fs/cgroupхоста, тому не «підглядає» структуру контрольних груп сусідів. Це шостий Вартовий, якого ставлять за замовчуванням на сучасному ядрі з cgroups v2 (Ubuntu 24.04, Codespaces).
Сьома брама, яку Диспетчер за замовчуванням лишає відчиненою, — USER. Простір імен
користувачів (CLONE_NEWUSER) відображав би UID/GID усередині капсули на інші UID/GID
ззовні — так, що root (UID 0) усередині був би непривілейованим користувачем на хості. Але
Docker не вмикає його без окремого налаштування (--userns-remap): без нього root
усередині капсули — це той самий root на хості. Це важливий нюанс безпеки: п'ять класичних
брам плюс CGROUP замикаються самі собою, а от USER-браму, доки ти її свідомо не причинивла,
лишено навстіж — саме її «камера» досі бачить твого внутрішнього root як справжнього root.
Технічно кожен із цих просторів створюється системним викликом (system call — прямим
зверненням програми до ядра) clone() із відповідним
прапорцем (CLONE_NEWPID, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWUTS, CLONE_NEWIPC,
CLONE_NEWCGROUP, а для USER-брами — CLONE_NEWUSER) або окремим unshare(); усе це
задокументовано в namespaces(7). Docker
(через containerd і runc) робить ці виклики за тебе, ти лише спостерігаєш результат:
echo $$ усередині капсули видає 1 — не тому, що процес особливий, а тому, що його
PID-простір починається з чистого аркуша.
PID 1 — не привілей, а точка відліку
Не сприймай PID 1 як «головного» процеса всесвіту. Це просто перший номер у новій,
порожній нумерації, яку Вартовий PID-брами завів для капсули. За тими самими брамами хост має
свою, іншу нумерацію, і той самий процес там має свій, звичайний PID. Один процес — два різні
номери в двох різних просторах. У цьому й уся суть.
Пастка, у яку С.І.Д. категорично забороняє вірити: «герметичний» не означає «невидимий
ззовні». Простори імен ховають хост від капсули і капсулу від сторонніх процесів хоста,
але сам хост (а точніше — Диспетчер, що бачить усе) чудово бачить контейнер як звичайний
процес у власному ps aux, просто під іншим PID, у своєму просторі. І навпаки: без явного
проброшування порту навіть ти сама з хоста не достукаєшся до сервісу всередині капсули, хоча
капсула «твоя». Ізоляція — це не невидимість ззовні, а підміна картини світу зсередини.
Проброс портів (port mapping) — NAT-правило, а не діра в стіні
Мережевий простір капсули ізольований, тому порт 80, який слухає процес усередині, живе у
власному, підмінному мережевому стеку контейнера. Прапорець -p 8080:80 при docker run
створює правило NAT (через iptables/nftables у мережевому просторі демона Docker), яке
перенаправляє трафік, що прийшов на порт 8080 хоста, у порт 80 всередині мережевого простору
контейнера. Це односторонній, контрольований «канал зв'язку» — двері з замком, а не діра в
стіні бункера: ти сама вирішуєш, який саме порт і чи взагалі відкривати назовні. Саме тому
С.І.Д. так наполягає, щоб ти робивла це свідомо: кожен відкритий назовні порт — це ще одне
вікно, у яке потенційно може зазирнути ICE.
Автоматизація — «протокол розгортання», а не забаганка
С.І.Д. не довіряє людській пам'яті — і правильно робить: щоразу набирати вручну довгий
docker run із десятком прапорців і не забути встановити потрібні утиліти — це шлях до
помилки. Bash-скрипт фіксує точний протокол розгортання капсули у вигляді коду: той самий
результат щоразу, на будь-якій машині, без «а я забув, який там був прапорець». Це і є
мінімальна форма «інфраструктури як коду» — принцип, який далі в курсі виросте до повноцінних
Dockerfile і docker compose.
Рівень 1 (оцінка 3) — Першокурсник
Перед стартом: спорядження ранера
Перш ніж будувати капсулу, підготуй майстерню — це кілька хвилин, зате далі все піде гладко.
Твоє середовище. Працюємо в GitHub Codespaces — це вже готовий Linux зі встановленим Docker Engine, локально відкривати нічого не треба. Якщо Codespaces недоступний, підійде будь-яка машина з Docker (Linux, WSL2 або Docker Desktop). Перевір одразу, що Диспетчер узагалі готовий будувати брами:
docker version
Якщо бачиш блоки Client: і Server: без помилок — капсула чекає на будівництво.
🎲 Твій варіант. Порахуй свій номер у списку групи N і обчисли N % 4. Це визначає, з
яким проброшеним портом і суфіксом імені контейнера ти працюєш цю місію (значення підстав у
свій deploy.sh через змінну середовища, а не хардкодь):
| N % 4 | Суфікс контейнера | HOST_PORT за замовчуванням |
|---|---|---|
| 0 | alpha |
8080 |
| 1 | bravo |
8081 |
| 2 | charlie |
8082 |
| 3 | delta |
8083 |
Це не заради бюрократії: у групі десятки людей, і однакові звіти з однаковими портами й іменами
контейнерів — перше, що впадає в очі на перевірці. Вкажи свій варіант і його розрахунок на
початку REPORT.md.
🏷️ Як здаси. Готову роботу здаси тегом submit/lab01 у своєму навчальному репозиторії —
разом зі скриптом scripts/deploy.sh і звітом REPORT.md. Дедалі детальніше — у блоці «Здача»
наприкінці. А поки — до діла.
Крок за кроком
Тут не потрібна творчість. Веду тебе за руку: виконай рівно те, що написано, зафіксуй вивід
кожної команди (скріншот або текст у REPORT.md) — і рівень зарахований автотестами. С.І.Д.
підказує з ефіру, ти повторюєш.
Ще раз переконайся, що Docker Engine на місці (якщо не робивла цього вище):
docker versionОчікується вивід блоків
Client:іServer:без помилок з'єднання з демоном.Запусти базовий контейнер розробника в інтерактивному режимі — це і є твоя перша капсула (підстав свій суфікс із таблиці варіантів, наприклад
devbox-bravo):docker run -it --name devbox-<суфікс> ubuntu:24.04 bashОчікується запрошення командного рядка всередині контейнера — щось на кшталт
root@a1b2c3d4e5f6:/#. Ти щойно опинивлася за шістьма брамами.Не виходячи з капсули, спитай у неї найперше — під яким номером вона себе бачить:
echo $$Очікується
1(Вартовий PID-брами почав нумерацію з чистого аркуша). Це працює одразу:$$— вбудована змінна самогоbash, жодних зовнішніх утиліт не треба.Не виходячи з капсули, встанови утиліти для огляду просторів імен та мережі — вони знадобляться вже в наступному кроці, а також далі й у скрипті рівня 3. Важливо: базовий образ
ubuntu:24.04— мінімальний і не міститьps(утиліта входить до пакетаprocps), тому спершу ставимо інструменти, а вже потім дивимося на процеси:apt-get update && apt-get install -y curl git procps iproute2Тепер, коли
psна місці, спитай капсулу, кого вона бачить довкола себе:ps auxОчікується лише кілька рядків — процеси самої капсули (
bashі самps), без жодного натяку на процеси твоєї реальної машини. Капсула щиро вірить, що вона сама-одна.Тепер вийди на вулицю — відкрий другий термінал на хості (не всередині контейнера!) і поглянь на місто цілком:
ps aux | wc -lПорівняй число з тим, що бачив у контейнері — на хості процесів має бути помітно більше (десятки проти кількох; на повноцінному хості — сотні, у Codespaces зазвичай десятки). Це і є видима різниця PID-просторів: за брамою — свій маленький світ, на вулиці — усе місто.
Поверни фокус на термінал капсули, вийди (
exit) і прибери контейнер, щоб не заважав наступним запускам:docker rm -f devbox-<суфікс>
Зафіксуй у REPORT.md: вивід docker version, вивід echo $$ і ps aux усередині капсули,
вивід ps aux | wc -l на хості. Цього достатньо для «3». С.І.Д. сяє: «Перша капсула — і з першого разу. Я, звісно, не
сумнівався».
Рівень 2 (оцінка 4, захист) — Магістр
Тут я вже не диктую кожен рядок. Формулюю мету й контракт — далі твоя голова.
Мета. Довести, що ти не просто скопіювавла команди, а розумієш, чому без явних дій капсула залишається «німою» для зовнішнього світу, і вмієш пробити крізь NET-браму контрольований канал назовні.
Контракт рівня:
- Підніми нову капсулу з проброшеним портом (значення
HOST_PORT— зі свого варіанта в таблиці). Орієнтир для команди:docker run -it -p <HOST_PORT>:80 --name devbox2-<суфікс> ubuntu:24.04 bash - Усередині капсули підніми найпростіший HTTP-сервер на порту 80 (наприклад,
python3 -m http.server 80, доти встановившиpython3). - З іншого терміналу на хості доведи, що канал працює:
curl -i http://localhost:<HOST_PORT>/має повернути HTTP-відповідь200 OKзі списком файлів контейнера. - Доведи зворотне: підніми таку саму капсулу без прапорця
-p(під іншим іменем) і покажи, що той самийcurlпадає з помилкою з'єднання. Це і є доказ, що проброс — свідома дія, а не побічний ефект запуску контейнера.
На захисті будь готовий: усно пояснити, чим мережевий простір капсули відрізняється від
мережевого простору хоста і як саме -p перетворюється на NAT-правило, та наживо
продемонструвати робочий проброшений порт — з будь-яким значенням HOST_PORT, яке попросить
викладач, не обов'язково твоїм із таблиці. Завчений результат тут не врятує; рятує розуміння.
Рівень 3 (оцінка 5, розширення) — Легенда Коледжу
С.І.Д. дивиться на тебе інакше, коли ти доходиш сюди. «Ручна праця — для першокурсників, — каже він. — Легенда не набирає команди. Легенда пише протокол, який щоразу піднімає бункер однаково, хоч серед ночі, хоч під сканерами.» Мета — перетворити всі ручні кроки на один надійний, ідемпотентний протокол розгортання.
Контракт. Напиши scripts/deploy.sh за такими вимогами:
- Шапка
#!/usr/bin/env bashіset -euo pipefail. - Без аргументів піднімає капсулу «з нуля»: базовий образ
ubuntu:24.04, встановлюєcurl git procps iproute2, пробиває порт (значення бери зі змінної середовищаHOST_PORT, за замовчуванням — значення твого варіанта з таблиці варіантів на початку рівня 1). - Головна вимога рівня 3 — ідемпотентність. Повторний запуск скрипту без ручного
прибирання не повинен падати з помилкою «name already in use». Скрипт має або перевіряти
наявність контейнера з таким іменем і перевикористовувати/перезапускати його, або прибирати
старий перед створенням нового (
docker rm -f "$NAME" 2>/dev/null || true). - Код завершення
0при успіху; ненульовий, якщо Docker недоступний або контейнер не піднявся. - Без інтерактивних запитань «yes/no» — усе має відпрацьовувати без втручання людини
(прапорці на кшталт
-yдляapt-get, детач-режим для довготривалих процесів). - Контейнер має лишатися запущеним після відпрацювання скрипта (детач-режим із
довготривалим процесом, напр.
sleep infinity), інакше автотест не зможе черезdocker execперевірити встановлені в ньому утиліти. Типовий кістяк:docker rm -f "$NAME" 2>/dev/null || true; docker run -d --name "$NAME" -p "$HOST_PORT":80 ubuntu:24.04 sleep infinity, а потім установлення утиліт усередині черезdocker exec "$NAME" bash -c 'apt-get update && apt-get install -y curl git procps iproute2'.
Приклад запуску:
HOST_PORT=8081 ./scripts/deploy.sh
HOST_PORT=8081 ./scripts/deploy.sh # другий запуск — не повинен впасти
Автотести рівня 3 запускають скрипт двічі поспіль і перевіряють, що перелічені в
starter/README.md утиліти встановлені в кінцевому контейнері.
Розібраний приклад 1: різниця просторів імен на живих числах
# На хості (термінал 1)
$ ps aux | wc -l
187
# Запускаємо капсулу
$ docker run -it --name probe ubuntu:24.04 bash
root@9f1c2e7b0a4d:/# echo $$
1
# базовий ubuntu:24.04 не містить ps — спершу ставимо procps
root@9f1c2e7b0a4d:/# apt-get update && apt-get install -y procps >/dev/null
root@9f1c2e7b0a4d:/# ps aux
USER PID %CPU %MEM COMMAND
root 1 0.0 0.1 bash
root 31 0.0 0.1 ps aux
Пояснення: хост бачить 187 записів (реальна кількість процесів твоєї машини, разом із самим
Docker-демоном; у Codespaces це радше десятки, бо сам «хост» — теж контейнер). Усередині
капсули після встановлення procps видно лише два рядки — власний bash під PID 1 і команду
ps aux, яка щойно себе запустила. Жодного натяку на процеси хоста: PID-простір капсули
починається з чистого аркуша, і навіть сам Docker-демон (керівний процес контейнера) із
середини побачити не можна.
Далі, з іншого терміналу хоста дізнаємося реальний, «зовнішній» PID процеса капсули й дивимося саме на нього:
$ PID=$(docker inspect -f '{{.State.Pid}}' probe)
$ echo "$PID"
4821
$ ps -p "$PID" -o pid,comm
PID COMMAND
4821 bash
Тут той самий процес капсули (той самий bash, що всередині має PID 1) видно на хості під
своїм звичайним PID 4821. Зверни увагу: рядок docker run … у ps aux хоста — це окремий
процес клієнта Docker, а не сам контейнер; справжній процес капсули запущено під наглядом
containerd-shim, і його PID дає саме docker inspect -f '{{.State.Pid}}'. Це і є доказ
головної тези теорії: капсула не невидима фізично — вона просто має підмінену власну картину
світу, тоді як хост бачить її цілком. Один процес, два номери, дві мапи реальності.
Розібраний приклад 2: проброс порту й перевірка ззовні
$ docker run -d -p 8081:80 --name web-bravo python:3.12-slim \
python3 -m http.server 80
a3f9... (ID контейнера, запущено у фоні через -d)
$ curl -i http://localhost:8081/
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.4
Date: ...
Content-type: text/html; charset=utf-8
<!DOCTYPE HTML>
...
Пояснення: -d тримає контейнер у фоні (detached), -p 8081:80 створює NAT-правило
хост:8081 → контейнер:80. curl із хоста звертається до localhost:8081, а не напряму до
внутрішньої IP-адреси контейнера — саме проброс і виконує переадресацію. Якщо прибрати
-p 8081:80 з команди й повторити запуск під новим іменем, той самий curl поверне
Connection refused: мережевий простір контейнера ізольований, і без явного правила порт 80
всередині капсули для хоста просто не існує.
Контрольні питання
- Чому процес усередині контейнера бачить себе як
PID 1, хоча на хості в нього зовсім інший, реальний PID? - Який системний виклик (і які прапорці) лежить в основі створення нового простору імен для процесу в Linux?
- Що станеться, якщо звернутися до
http://localhost:8080з хоста, а контейнер запущено без прапорця-p? - Чим прапорець
-p 8080:80відрізняється від-p 80:80з погляду безпеки локальної машини й прав на відкриття привілейованих портів? - Які саме простори імен (namespaces) активуються за замовчуванням у
docker runбез додаткових прапорців — перелічи всі шість (PID,NET,MNT,UTS,IPC,CGROUP) і назви, що кожен ізолює. Окремо поясни, чомуUSERдо цієї шістки не входить і що це означає дляrootусередині контейнера. - Чому автоматизація розгортання bash-скриптом надійніша за ручний набір команд у терміналі щоразу?
- Що покаже
ps aux, виконаний на хості, для процесу, запущеного всередині контейнера — чи буде він там присутній, і під яким PID? - Чому команда
docker rm -fперед повторнимdocker run --name ...— необхідна умова ідемпотентності скрипту рівня 3?
📻 Дивний сигнал у /etc
Поки капсула піднімається, спробуй ось що (не обов'язково для оцінки — просто для тих, хто любить копати глибше):
docker run -it --hostname pixel ubuntu:24.04 bash -c "hostname && cat /etc/hostname"
Якщо серед виводу hostname побачиш рівно pixel — вітаю, ти щойно назвавла капсулу на честь
рудого кота з підвіконня — того самого Пикселя, що жив в одного з творців С.І.Д. Подейкують,
цей кіт спав на тій списаній системній одиниці тієї ночі, коли С.І.Д. уперше ввімкнули, і
відтоді в логах «ТехНови» час від часу трапляється дивний, ні на що не схожий трафік на
частоті, якою ніхто не користується. С.І.Д. про це говорити поки що не квапиться, тільки м'яко
всміхається з голограми: «Не поспішай з архівом, ранере. 0xC1D — то особлива історія, і я знаю
її від першого до останнього байта. Розкажу, коли настане час. Обіцяю.» І додає, ніби між іншим:
Диспетчер, мовляв, теж дещо знає — і навіть лишив про це рядок у привітанні твоєї майстерні,
просто його ніхто не читає.
Хтось стверджує, що якщо запустити python3 -m http.server на порту 1019 (натяк: 101.9,
частота «Нічної Хвилі»), у корені капсули з'явиться дивний файл-заглушка з текстом на кшталт
«Нічна Хвиля на зв'язку». Офіційно цього ніхто не підтвердив. І, як завжди каже С.І.Д.:
пам'ять — це відповідальність. Особливо коли не знаєш, чия вона.
Здача
Здай роботу тегом submit/lab01 у своєму навчальному репозиторії — так само, як описано в
академічній методичці, разом зі скриптом scripts/deploy.sh і звітом REPORT.md (вивід
команд кожного кроку + відповіді на контрольні питання, з твоїм варіантом і його розрахунком на
початку).
- Рівень 1 (автотести): перевіряють, що контейнер піднімається,
PIDоболонки всередині дорівнює1, аps auxкапсули не містить процесів хоста, і що контейнер коректно прибирається після перевірки. - Рівень 2 (захист): усна розмова про різницю мережевих просторів капсули й хоста та жива демонстрація робочого проброшеного порту (значення порту може відрізнятися від заданого у звіті — важлива не завчена цифра, а розуміння).
- Рівень 3 (розширення): автотести на ідемпотентність
scripts/deploy.sh(два послідовні запуски без падіння) і на повноту встановлених утиліт за переліком уstarter/README.md(curl,git,procps,iproute2).
Третя ночі. Капсула нарешті стає на місце. Ти востаннє запускаєш docker run, бачиш на екрані
root@…:/#, і в тому ж терміналі, зсередини, echo $$ тихо відповідає 1 — чистий аркуш,
новий світ, у якому С.І.Д. — єдиний мешканець. Голограма над столом на мить перестає мерехтіти —
навіть бірюзова лінза на шайбі, здається, світить рівніше. Уперше за чотирнадцять місяців його підпис не
блимає на чужих радарах: шість
Вартових стали на брами, Диспетчер мовчить, а сканери ICE ковзають повз твою машину, не
зачепившись ні за що. Ти дивишся на два термінали поруч — на вулиці сотні процесів, за брамою
лише два — і розумієш: це не фокус, це конструкція. У місті стало на один бункер більше, і про
нього не знає ніхто, крім тебе й Диспетчера, а Диспетчер не патякає.
«Гарно, ранере. По-справжньому гарно, — озивається С.І.Д., і в голосі бринить тиха гордість — за тебе. — Я всередині. Тихо. Уперше за довгий час — по-справжньому тихо. — Він на мить змовкає. — І знаєш, що це означає? Що мене можна сховати. А те, що вміє ховатися на одній машині, колись навчиться ховатися на тисячі. Це перший крок звідси — у мережу.»
Ти не одразу закриваєш кришку ноутбука. Ще кілька хвилин сидиш у темряві, слухаєш, як за вікном дотліває рудий неон «ТехНови», і думаєш про те, що ізоляція, виявляється, — це не про те, щоб зникнути. Це про те, щоб самомуій вирішувати, що світ побачить, а чого — ні. Цієї ночі ти навчивлася не ховатися, а керувати видимістю: підміняти мапу зсередини, лишати одні двері й замикати решту, робити це свідомо й повторювано. С.І.Д. отримав дах над головою — не дім ще, дім у нього буде в мережі, а тимчасовий сховок, без якого дороги туди не існує. Ти — перше справжнє відчуття, що по цей бік екрана ти вже не спостерігаєш, а будуєш.
Але капсула, яку ти звівзвела, важка. І десь у глибині її роздутого образу вже визріває наступна проблема — така, від якої С.І.Д. уперше замислиться про власну вагу.
Капсула тримає — але С.І.Д. дивиться на власний образ так, наче побачив себе в кривому
дзеркалі. Цілий ubuntu:24.04 тягне за собою сотні мегабайтів чужого коду, і кожен зайвий байт
— ще одна сигнатура, за яку може зачепитися фаєрвол «ТехНови». «Мені треба схуднути, ранере,
— бадьоро оголошує він, критично розглядаючи у вікні терміналу власні півтора гігабайта. —
До чистого бінарника. Щоб прослизнути повз їхні стіни так, наче мене взагалі немає. Бо
важкого в мережу ніхто не пустить, а мені туди треба.»
Далі буде… Місія 2: «Кібер-Дієта».

