DD — Азбука: терминал, git, docker, ssh, JSON, .env, порты, как читать ошибку
Микро-модуль 0.5 — операционный ликбез перед стройкой. Не теория курса, а инструменты в руках: без них ни один «живой якорь» курса не откроется, потому что каждый якорь начинается со слов «зайди на сервер и посмотри...».
Медицинская рамка: прежде чем говорить о консилиумах и иммунитете (модули 2 и 4), студент-медик должен уметь держать скальпель, читать анализы и мыть руки. Это тот самый уровень. Азбука — не про то, «как устроен организм», а про то, как физически войти в палату, открыть карту пациента и не перепутать шприцы.
0. Зачем это вообще нужно
У тебя уже есть завод: 16+ контейнеров на Hetzner VPS (), Caddy на входе, git-репозитории для каждого сервиса. Всё это построено вместе с ИИ — но чтобы проверять живое, не код (твой закон №4), нужны голые руки на терминале, а не только просьба к ассистенту. Пока эти восемь навыков не в мышечной памяти, у тебя фактически есть только один канал управления заводом — диалог с ИИ, и если он вдруг ошибается или недоступен, ты не можешь сам заглянуть под капот и проверить его слова. Это ровно та зависимость, которую этот модуль закрывает: не «замени ИИ», а «получи собственные глаза и руки рядом с ним».
Каждый из восьми пунктов ниже привязан к твоему заводу и заканчивается «живым якорем» — конкретной командой, которую можно набрать прямо сейчас, без ожидания следующего урока. Важная оговорка сразу: это НЕ курс программирования — писать код с нуля тебе для целей этой системы не нужно (этим занимается ИИ). Нужно ровно то, что нужно врачу-администратору клиники: уметь ЗАЙТИ в нужный кабинет, ПРОЧИТАТЬ карту и ПОНЯТЬ, что в ней написано, — не самому оперировать.
1. Терминал — окно прямого разговора с компьютером
По-человечески: обычно ты нажимаешь на иконки — это как объясняться с врачом жестами. Терминал — это разговор напрямую словами: ты печатаешь команду, компьютер отвечает текстом. Дольше учиться, но точнее и быстрее, когда освоился — как переход с жестов на профессиональный медицинский язык.
Как называется по-настоящему (English): terminal / command line / shell. На Windows — PowerShell или cmd, на Mac/Linux — bash или zsh. Когда ты просишь Claude Code что-то сделать, он у тебя за спиной именно печатает такие команды.
Минимум, чтобы не потеряться:
- pwd — «где я сейчас» (print working directory).
- ls (Mac/Linux) или dir (Windows) — «что лежит в этой папке»; ls -la — показать и скрытые файлы (у .env имя начинается с точки не случайно — по умолчанию он спрятан от обычного списка).
- cd имя_папки — «зайти в папку», cd .. — «выйти на уровень выше», cd ~ — «вернуться домой».
- cat имя_файла (Mac/Linux) или type имя_файла (Windows) — «показать содержимое файла целиком прямо в терминале», не открывая редактор.
- mkdir имя — создать папку. rm файл / rm -rf папка — удалить; это как ампутация без наркоза — нет корзины, нет отмены, дважды проверяй путь перед Enter.
- Клавиша Tab — автодополнение имени файла/папки на середине ввода: печатаешь первые буквы, жмёшь Tab — экономит опечатки.
- Стрелка вверх — повторить прошлую команду (не перепечатывать); история команд — твой личный «анамнез» действий за сессию.
- Ctrl+C — «прервать», если команда зависла или ты передумал.
Для любопытных: терминал — это просто текстовый интерфейс к shell (командному интерпретатору: bash/zsh на Linux/Mac, PowerShell на Windows), который сам вызывает программы. Когда твой скилл
.autodeploy.ps1выполняется Stop-хуком, это буквально та же самая PowerShell-оболочка, только запущенная без тебя, в фоне, по тому же принципу «команда → ответ», который ты только что освоил руками.
Текстовый редактор прямо в терминале — пригодится на сервере. Через ssh у тебя чаще всего НЕТ VS Code, только терминал, поэтому для быстрой правки .env или .json на VPS нужен встроенный редактор: nano файл — самый дружелюбный (подсказки горячих клавиш прямо внизу экрана, Ctrl+O сохранить, Ctrl+X выйти); vim файл — мощнее, но требует привычки (главное, что нужно новичку: нажать i, чтобы начать печатать, Esc, затем :wq и Enter, чтобы сохранить и выйти — иначе легко застрять). Для разовых правок на сервере nano вполне достаточно.
Живой якорь: открой терминал на своём ПК и набери ssh root@ — уже наберёшь пароль/ключ и окажешься «внутри» сервера завода (подробнее в §4).
2. Git — журнал изменений, который не врёт
По-человечески: представь историю болезни пациента — каждая запись врача датирована, подписана, и её нельзя незаметно стереть, можно только дописать новую. Git — то же самое для кода: каждое изменение — отдельная «запись в карте» (commit), с автором, датой и объяснением, что изменилось.
Как называется по-настоящему (English): git — система контроля версий (version control), программа, которая работает на твоём компьютере. GitHub — отдельный, самый популярный САЙТ-хранилище для таких карт (роль «общего архива больницы»); git и GitHub — не одно и то же, первое — инструмент, второе — один из возможных удалённых складов для его результатов. Репозиторий (repo) — папка с полной историей проекта; origin — стандартное имя для «того самого удалённого склада», откуда ты pull-ишь и куда push-ишь. У тебя таких репозиториев 26+ (твой git-map).
Пять команд, которые реально нужны:
- git clone <адрес> — «скопировать чужую/удалённую карту болезни себе» — забрать репозиторий целиком в первый раз.
- git pull — «подтянуть свежие записи» — забрать изменения, которые появились на сервере/у другого человека (или у ИИ, работавшего без тебя).
- git status — «что изменилось с последней записи, но ещё не зафиксировано» — самая частая команда, спроси её первой, если сомневаешься.
- git diff — «показать построчно, что именно поменялось» — как сравнение двух анализов бок о бок.
- git commit -m "текст" + git push — «зафиксировать запись в карте и отправить её на общий сервер».
Как это у тебя устроено: Stop-хук (auto-commit-push-deploy-hook) в конце сессии Claude Code сам делает commit + push за тебя для тронутых репозиториев — это то же самое действие, просто автоматизированное. Понимание команд нужно, чтобы проверить, что хук сделал, а не гадать.
Ещё две вещи, которые пригодятся раньше, чем кажется:
- .gitignore — файл-«список того, что НЕ записывать в карту болезни»: секреты (.env), временные файлы, папки с гигабайтами сгенерированных видео. Если чего-то важного нет в git status, но оно должно быть — проверь, не попало ли оно туда случайно.
- Конфликт слияния (merge conflict) — когда два человека (или ты и ИИ параллельно) поправили одну и ту же строку по-разному, git сам не решает, кто прав, а показывает оба варианта прямо в файле между маркерами <<<<<<< и >>>>>>> — это не поломка, а запрос на твоё врачебное решение, какую версию оставить.
Для любопытных:
branch— отдельная параллельная «черновая карта», где можно пробовать рискованное лечение, не трогая основную (main);merge— слияние черновика обратно в основную запись, когда способ подтвердился. Именно так планируется изолировать варианты в веере MVP консультант-фабрики (Урок 9.3) — каждый explorer-агент работает в своей ветке/worktree, не наступая на соседа. В модуле 3.3 курса это же станет полноценной аналогией «git-памяти агента» (main.md / commit.md / log.md).
Живой якорь: cd в любую папку сервиса на ПК (например, D:\PROJECTS\channel-hq) → git log --oneline -5 — увидишь последние 5 «записей в карте болезни» этого сервиса.
3. Docker и контейнер — герметичная палата, а не общая казарма
По-человечески: представь, что каждому пациенту нужна своя стерильная палата со своим воздухом, оборудованием и лекарствами — чтобы вирус (баг, конфликт версий) из одной палаты не заразил остальные. Контейнер — это ровно такая изолированная палата для одной программы: у неё своя копия всего необходимого, и она не мешает соседним палатам, даже если они на одном этаже (сервере).
Как называется по-настоящему (English): Docker — платформа контейнеризации; container — запущенный экземпляр; image — «рецепт», по которому контейнер собирается; docker-compose — «список назначений на несколько палат сразу», который описывает, как несколько контейнеров работают вместе (у тебя — один docker-compose.yml на сервис). Volume — отдельная, ВНЕШНЯЯ по отношению к контейнеру полка для хранения файлов: если контейнер пересоздать (новый образ, свежий деплой) — сам контейнер «одноразовый» и стирается вместе со всем, что внутри, а вот подключённый volume переживает пересоздание. Это ровно причина, по которой твои генераторы (omni/photo) хранят проекты не «где попало» в контейнере, а в закреплённом volume /app/projects — твоя же запись в базе ошибок про то, что /app/backend/projects пуст и никогда не был локацией, это как раз путаница «временная папка контейнера» vs «постоянный volume».
Три команды, которые ты будешь набирать чаще всего (твой закон №4 — «проверяй живое»):
- docker ps — «кто сейчас дышит» — список работающих контейнеров, их имена, порты, сколько времени живут.
- docker logs <имя_контейнера> — «журнал наблюдений за пациентом за последнее время» — вывести, что контейнер печатал (тут чаще всего видно причину поломки).
- docker exec -it <имя_контейнера> bash — «зайти прямо в палату» — открыть терминал ВНУТРИ контейнера, посмотреть его файлы или переменные (printenv) изнутри, не веря докладам снаружи.
# твой пример из 06-errors-and-gotchas.md — "код умеет X, но фича выключена":
docker exec videopipeline-backend-1 printenv | grep FEATURE_FLAG
Как выглядит «рецепт» (упрощённый docker-compose.yml одного из твоих сервисов):
services:
channel-hq:
build: .
ports:
- "127.0.0.1:8030:8030" # снаружи видно только с самого сервера
env_file: .env # сюда подтягиваются секреты из §6
networks:
- factory # общая сеть, чтобы видеть memory-bank по имени
networks:
factory:
external: true
Три вещи, на которые смотреть в первую очередь при разборе чужого/нового сервиса: ports (какая дверь и кому видна — сверься с §7), env_file (откуда секреты — §6), networks (с кем сервис может «разговаривать» напрямую по имени).
Ещё несколько команд, которые встречаются в твоих же граблях (06-errors-and-gotchas.md):
- docker restart <имя> — «перезапустить палату» — самый грубый инструмент, применять с оглядкой: твоя же грабля гласит «не рестартить shared-контейнер (governor, генераторы) — бьёт по параллельным сессиям», рестарт годится только когда контейнер реально idle.
- docker compose up -d --build — «пересобрать и поднять по свежему рецепту» — то, что делает твой типовой деплой-цикл после git pull в папке сервиса на сервере.
- docker inspect <имя> — «полная карта пациента»: какой образ, какие порты, какая политика авто-рестарта, к каким сетям подключён — полезно, когда docker ps даёт слишком мало.
- docker cp file <c>:/app/file — «подложить файл в палату без пересоздания», твой же приём для статики без рестарта (не роняет активные соединения).
Как это у тебя устроено: все 16+ сервисов завода — это отдельные контейнеры на общей docker-сети factory (имя сети как общая «больничная инфраструктура» — трубы, провода — по которой палаты обмениваются сигналами, не выходя наружу). У каждого сервиса, кроме того, есть и свой персональный *_default bridge — как отдельная внутренняя разводка внутри одной палаты (её собственные под-контейнеры вроде базы данных). docker ps — первая команда, которую стоит набирать, когда что-то «не работает», прежде чем спрашивать ИИ.
Для любопытных: контейнер — не полноценная виртуальная машина (это была бы отдельная больница со своими стенами и фундаментом), а изолированный процесс с урезанными правами на том же ядре ОС хоста — легче и быстрее VM, запускается за секунды, но изоляция менее строгая (общее ядро — общий риск). Это ровно причина, почему для ночного строителя приложений курс рекомендует ОТДЕЛЬНЫЙ Docker-контекст (а в идеале — более строгую изоляцию вроде microVM) без секретов завода — модуль 4.4.
Живой якорь: ssh root@ → docker ps — увидишь свой завод живьём: channel-hq, memory-bank, resource-governor и остальных.
4. SSH — как физически попасть внутрь сервера
По-человечески: VPS — это как удалённая клиника, куда ты не можешь прийти ногами. SSH — защищённый телефонный/видео-канал, через который ты «оказываешься» внутри и можешь отдавать команды так, будто сидишь за тем компьютером лично.
Как называется по-настоящему (English): SSH (Secure Shell) — зашифрованный протокол удалённого доступа к терминалу другого компьютера.
Как заходишь ты:
ssh root@
root — имя пользователя (в твоём случае — администратор сервера), — адрес VPS. У тебя это уже настроено без пароля — по ключу (файл-«отпечаток пальца», который лежит на твоём ПК и подтверждает личность автоматически, BatchMode проходит без вопросов). Как только зашёл — ты «внутри», и все команды из §1–§3 работают уже НА СЕРВЕРЕ, а не на твоём ПК.
Для любопытных: SSH по умолчанию слушает порт 22 🟢 [стандарт, не число «на глаз» — общеизвестный факт протокола]. Ключевая пара (публичный/приватный ключ) надёжнее пароля: приватный ключ никогда не покидает твой ПК, сервер проверяет подпись, а не сам секрет — украсть пароль перебором на порядки проще, чем подделать подпись без приватного ключа.
Ещё два полезных приёма поверх голого ssh:
- scp файл root@ — «переслать файл по тому же защищённому каналу», без отдельной программы для передачи файлов; для папок добавь -r.
- Файл ~/.ssh/config на твоём ПК — «телефонная книга серверов»: один раз прописываешь алиас (Host factory → адрес, пользователь, путь к ключу), дальше просто ssh factory вместо длинного адреса каждый раз.
Отдельно стоит понимать разницу между твоими двумя «удалёнными точками»: сам VPS-сервер (прод, всё живёт там) и reverse-ssh туннель с домашнего ПК (openmontage :8053) — это SSH, вывернутый наизнанку: не ты заходишь на чужую машину, а твой ПК сам держит постоянное соединение с сервером, чтобы сервер мог достучаться ОБРАТНО до твоего локального сервиса (нужен только пока ПК включён — это ровно то ограничение, которое курс отмечает в модуле 8.1: домашняя машина не должна быть в критическом пути).
Живой якорь: зайди по ssh и набери hostname && uptime && df -h — увидишь имя сервера, сколько он «не спал» без перезагрузки и сколько места осталось на диске (твой завод сейчас на 91% — полезно проверять регулярно, это буквально первый пункт роадмапа первой недели курса).
5. API и JSON — как сервисы разговаривают друг с другом
По-человечески: API — это регистратура клиники: ты не бродишь по кабинетам сам, а подаёшь стандартный бланк-запрос в окошко («хочу анализ крови такого-то пациента») и получаешь стандартный бланк-ответ. JSON — это язык, на котором написаны эти бланки: структурированный, машиночитаемый, но и человеку понятный при небольшой привычке.
Как называется по-настоящему (English): API (Application Programming Interface) — контракт, по которому одна программа просит другую что-то сделать или отдать. JSON (JavaScript Object Notation) — формат данных: фигурные скобки {} = «карточка полей», квадратные [] = «список карточек», всё в парах "ключ": значение.
Пример из твоего завода (запрос твоего own broker'а — резюмирован из §08-contracts):
{
"jobId": "img-8271",
"status": "done",
"download_url": "https://photo.rbrstack.com/media/img-8271.png",
"cost_credits": 1
}
Читается буквально: «работа с номером img-8271 готова, вот ссылка на файл, стоила 1 кредит». Если тебе нужно ЧТО-ТО поменять в конфиге (а не в коде) — почти всегда это правка одного значения в похожей структуре, например в settings.json Telegram-бота или profile.json канала.
Пример посложнее, с вложенностью и списком (структура ближе к твоему profile.json канала):
{
"channel_id": "lightning-news-01",
"voice": "moss-v3",
"topics": ["breaking-news", "tech", "politics"],
"limits": {
"videos_per_day": 12,
"min_gap_minutes": 180
}
}
Читается по вложенности сверху вниз: у канала lightning-news-01 голос moss-v3, три темы в СПИСКЕ (квадратные скобки — значит порядок и повтор допустимы, это не «карточка», а «список карточек» или значений), а limits — это отдельная ВЛОЖЕННАЯ карточка внутри карточки, со своими двумя полями. Если нужно добавить четвёртую тему — допиши "health" внутрь списка через запятую, ничего больше трогать не надо.
Как читать/править JSON, не сломав:
1. Открой файл в любом текстовом редакторе (VS Code подсвечивает скобки и парные пары — сильно помогает не заблудиться во вложенности).
2. Найди нужный "ключ", поменяй только значение СПРАВА от двоеточия, не трогай кавычки/скобки/запятые вокруг.
3. Перед сохранением проверь: у каждой открывающей скобки {/[ есть закрывающая, у предпоследнего поля в списке НЕТ запятой в конце (частая причина «файл не грузится» — этот один лишний символ разваливает весь файл целиком, JSON не прощает опечаток так, как прощает их обычный текст).
4. Валидатор (сайт типа jsonlint.com или команда python -m json.tool файл.json) — если сомневаешься, пусть машина скажет, не гадай; она либо молча подтвердит структуру, либо укажет точную строку ошибки.
Разница GET и POST (два самых частых типа запроса): GET — «спроси и покажи» (получить статус, список, файл — ничего не меняет на той стороне), POST — «сделай действие» (создать задание на генерацию картинки, опубликовать видео). Практически: curl https://адрес без флагов — это GET; если нужно что-то ЗАПУСТИТЬ, а не просто посмотреть, обычно требуется POST с телом-JSON (curl -X POST -d '{"key":"value"}' https://адрес).
Для любопытных: HTTP-статусы ответа — тоже часть контракта API. 200 = «всё хорошо», 404 = «такого пациента/адреса не существует», 429 = «слишком много запросов, притормози» (твой FastGen-лимит), 500/502 = «сломалось на стороне сервера» (502 у тебя буквально означает «Caddy достучался, а контейнер за ним не отвечает» — см. §8). 🟢 [деление кодов на классы 2xx/4xx/5xx — стандарт HTTP, не внутренняя договорённость завода]
Живой якорь: открой в браузере https://bank.rbrstack.com/api/health (или curl его с сервера) — увидишь живой JSON-ответ от memory-bank прямо сейчас.
6. .env и переменные окружения — тайный ящик с ключами от палаты
По-человечески: у каждой палаты есть свой набор ключей и кодов доступа — их не пишут мелом на двери (в коде, который все видят и коммитят в git), а держат в отдельном опечатанном конверте рядом с постом медсестры. .env — это и есть такой конверт: файл с секретами и настройками, который лежит РЯДОМ с кодом, но не является частью кода и не отправляется в git.
Как называется по-настоящему (English): environment variables (переменные окружения) — значения, которые программа читает из своего «окружения» при старте, а не из кода. .env — просто самый частый способ ИХ хранить и подгружать (файл вида КЛЮЧ=значение, строка на строку).
Твой реальный пример (упрощённо, по мотивам карты секретов из 08-contracts.md):
FASTGEN_API_KEY=sk-xxxxxxxxxxxx
BANK_URL=http://memory-bank:4600
GOVERNOR_URL=http://resource-governor:4700
IMAGES_VIA_BROKER=1
Каждая строка — один секрет или настройка. Контейнер при запуске читает этот файл и получает эти значения как будто они «в воздухе» вокруг него.
Главная грабля, которую ты уже словил живьём (твоя же запись в 06-errors-and-gotchas.md, пункт 5): «код умеет X, но фича выключена» — .env-файл существует и содержит правильное значение, но docker-compose.yml его не пробрасывает внутрь контейнера. Цепочка проверки всегда одна и та же:
.env (файл на диске) → docker-compose.yml (пробрасывает ли переменную?) → docker exec <c> printenv (что реально видит программа)
Три звена — если сломалось где-то посередине, снаружи это выглядит как «фича не работает», хотя код правильный.
Для любопытных: правило «секреты — только в
.env, никогда в код/репозиторий/чат с ИИ» (у тебя это прямо прописано в карте секретов) — не паранойя, а стандартная гигиена: если ключ попадёт в git-историю, его придётся считать скомпрометированным даже после удаления файла, потому что старые коммиты помнят всё. Это же правило — прямая причина, почему ночной строитель приложений (модуль 6, 11.2) не должен иметь доступа к боевым.env-файлам завода: агент, которому дали ключи «на всякий случай», рано или поздно случайно засветит их в логе, коммите или ответе.
Небольшая тонкость, которая экономит часы недоумения: переменные окружения можно задать и БЕЗ файла .env — прямо в терминале (export FASTGEN_API_KEY=xxx на Mac/Linux, $env:FASTGEN_API_KEY="xxx" в PowerShell). Такая переменная живёт только в текущем окне терминала и исчезает при закрытии — удобно для разового теста, но именно поэтому воспроизводимость и надёжность даёт только файл .env, который перечитывается при каждом запуске контейнера.
Живой якорь: на сервере cat /root/channel-hq/.env | grep -v KEY (без вывода настоящих ключей на экран) — увидишь список настроек одного из твоих сервисов вживую.
7. Порты и сеть: кто с кем может говорить
По-человечески: представь больницу с сотнями кабинетов на одном этаже (сервере). Порт — номер конкретного кабинета: «зайти к терапевту» и «зайти к рентгенологу» — это один и тот же этаж, разные двери. localhost / 127.0.0.1 означает «этот же корпус, для внешних пациентов вход закрыт» — только свои сотрудники могут туда попасть. 0.0.0.0 означает «дверь открыта на улицу для кого угодно» — это то, что стоит проверять на предмет случайной уязвимости.
Как называется по-настоящему (English): port — номер (0–65535) конкретного «канала связи» на одном IP-адресе; localhost / 127.0.0.1 — «этот же компьютер, снаружи недоступно»; 0.0.0.0 — «слушай на всех сетевых интерфейсах, доступно снаружи, если файрвол не против».
На твоём заводе это выглядит так (снимок 04-server.md): большинство контейнеров биндятся на 127.0.0.1 — наружу их видно ТОЛЬКО через Caddy (единственный публичный вход, порты 80/443, дальше он сам решает, в какой «кабинет» перенаправить запрос по имени поддомена — channel.rbrstack.com → localhost:8030). Несколько сервисов (photo-генератор, omni-генератор, статик-карты) исторически открыты на 0.0.0.0 — это прямо отмечено как то, что стоит регулярно аудировать (твой roadmap, этап 1 первой недели).
Между «недоступно снаружи вообще» и «доступно всему интернету» есть и третий, средний вариант — частные IP-адреса (диапазоны 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 🟢 [зарезервированы стандартом RFC 1918 для приватных сетей — проверено по первоисточнику]): видны внутри локальной/облачной сети, но не маршрутизируются в открытый интернет напрямую — это тот же принцип, что «внутренняя больничная связь между этажами», не выходящая на городскую телефонную линию. Твоя docker-сеть factory работает именно в такой частной адресации — контейнеры получают внутренние IP из этого диапазона, а наружу их выпускает уже только Caddy на настоящем публичном адресе VPS.
Внутри docker-сети factory — отдельное правило: контейнеры видят друг друга по ИМЕНИ, не по localhost и не по IP — например, http://memory-bank:4600, а не http://localhost:4600 (это сработает только если ты уже ВНУТРИ того же контейнера, что и адресат). 🟢 [это встроенное поведение Docker: контейнеры в одной пользовательской bridge-сети получают друг для друга DNS-имена по имени контейнера — задокументированное поведение Docker, не особенность твоего завода]
Для любопытных: диапазон 0–1023 называется «известные порты» (well-known ports, закреплены за стандартными службами — 22 = SSH, 80/443 = веб), 1024–65535 — свободные для приложений. 🟢 [деление на диапазоны портов — стандарт IANA]. Порты твоих сервисов (8000, 8030, 8600...) выбраны произвольно из свободного диапазона — это НЕ протокольное требование, просто соглашение внутри завода, чтобы не пересекались.
Ещё один слой, который встретится в roadmap (модуль 8.1, 11.2): Tailscale. Это не порт и не Docker-сеть, а частная «телефонная линия» между твоими устройствами (VPS, ПК с 3090, ноутбук) — они видят друг друга по своим внутренним адресам, как будто сидят в одной локальной сети, даже физически находясь в разных городах, и это НЕ видно и НЕ доступно снаружи вообще. Это тот механизм, которым домашний ПК с 3090 сможет по требованию давать VPS доступ к GPU, оставаясь при этом закрытым для всего остального интернета — прямая противоположность случайно открытому 0.0.0.0.
Живой якорь: на сервере ss -tlnp | grep LISTEN — список всех открытых портов и того, кто их слушает; сверь с таблицей контейнеров в 04-server.md — если появился новый порт, которого там нет, стоит разобраться, откуда он.
8. Как читать ошибку и лог, когда сломалось
По-человечески: ошибка — это симптом, а не диагноз. «Болит живот» само по себе не говорит, что делать, — важно ГДЕ болит, КОГДА началось и ЧТО было перед этим. Лог (журнал) — это анамнез: хронологическая запись событий, по которой ищется первопричина, а не просто последняя жалоба.
Как называется по-настоящему (English): log — журнал событий; stack trace / traceback — «путь», по которому ошибка всплыла наружу через код: это не хаотичный текст, а строгий маршрут вызовов одной функции за другой. Направление чтения зависит от языка (в Python традиционно самая ВАЖНАЯ строка — последняя, внизу, «где реально сломалось»; в JavaScript часто наоборот, важное — сверху) — если не уверен, ищи глазами не позицию, а саму фразу ошибки (Error:, Exception:) и текст вокруг неё, а не пытайся угадать по одному правилу для всех языков.
Порядок действий, когда «что-то не работает» (твой закон №4 в действии):
1. docker ps — контейнер вообще жив? Если его нет в списке или он в статусе Restarting — вот и причина.
2. docker logs --tail 100 <имя> — последние 100 строк журнала. Ищи глазами не первую попавшуюся строку с «error», а самую раннюю ошибку в цепочке — часто дальше идёт лавина повторов одной и той же причины.
3. HTTP-код, если проблема при обращении к сервису через браузер/Caddy: 502 почти всегда значит «Caddy достучался, а сам сервис за ним не ответил» (контейнер упал/перезапускается); 429 — «слишком много запросов, ты уперся в лимит» (твой FastGen-кейс); 404 — «неверный адрес/сабдомен».
4. Твоя собственная база граблей (06-errors-and-gotchas.md) — прежде чем гадать заново, проверь, не наступал ли ты УЖЕ на эту грабли месяц назад. Симптом «висит на running» у тебя уже расшифрован (залипший семафор), «429 при свободном usage-окне» — тоже (зомби-задачи).
Пример из твоей же практики (реальный кейс, не гипотетический): Learningo после передеплоя без override терял порт 8110, и Caddy отвечал 502 — снаружи это выглядело как «бот не отвечает», хотя сам Telegram-поллинг был жив. Диагноз нашёлся не гаданием, а строго по цепочке порт → docker-compose override → Caddy — то есть теми же тремя инструментами (docker ps, конфиг, лог), что и в §6.
Небольшой словарь фраз, которые будут попадаться постоянно (переводя их сразу на «диагноз», а не пугаясь текста):
- Connection refused — «дверь заперта»: адрес верный, но по этому порту никто не слушает (контейнер не запущен или слушает другой порт).
- Permission denied — «нет допуска в палату»: не тот пользователь, не тот ключ, не те права на файл.
- Timeout / Connection timed out — «дверь не отвечает вообще»: не отказ, а тишина — часто файрвол, не тот адрес в сети, или сервис реально завис (см. п.1 твоей же базы граблей про залипший семафор).
- No such file or directory — «в карте нет такой страницы»: опечатка в пути или файл ещё не создан/уже удалён.
- Address already in use — «кабинет уже занят»: кто-то другой уже слушает этот порт — обычно означает, что старый контейнер не до конца остановился перед новым запуском.
Для любопытных: «тихий отказ» (silent failure) — самый опасный вид ошибки для ночной автономии: программа не падает с текстом ошибки, а просто молча делает не то (или ничего). Именно поэтому курс (модуль 6.3, 8.3) требует явных чекпоинтов и структурного лога решений — умение читать
docker logsруками сегодня прямо готовит тебя понимать, ПОЧЕМУ это архитектурное требование, а не бюрократия.
9. Собираем всё вместе: разбор одного вымышленного, но типичного вечера
Представь: ты открываешь channel.rbrstack.com — портал не грузится, браузер показывает 502. Вот как расследование выглядит, используя ровно те восемь навыков выше, по порядку, а не хаотично:
- Терминал + ssh (§1, §4):
ssh root@— заходишь на сервер. - Docker (§3):
docker ps— видишь, чтоchannel-hqв списке ЕСТЬ, но колонка STATUS показываетRestarting (1) 12 seconds ago— контейнер падает и перезапускается по кругу, а не просто «висит». - Логи (§8):
docker logs --tail 50 channel-hq— в самом верху пачки повторяющихся строк видишь однократную первопричину:Error: connect ECONNREFUSED memory-bank:4600— «дверь заперта», channel-hq пытался достучаться до memory-bank и не смог. - Проверяешь соседа (§3 снова):
docker ps | grep memory-bank— а его вообще нет в списке. Значит, упал он, а channel-hq просто честно сообщает о последствиях. - .env / порт (§6, §7): проверяешь, не менялся ли недавно
.envmemory-bank или его порт вdocker-compose.yml— например, кто-то (ты сам вечером, или ИИ во время сессии) поправилBANK_URLи забыл, что имя контейнера в сетиfactoryдолжно совпадать буква в букву. - Git (§2):
git log --oneline -5в папке memory-bank — смотришь, было ли сегодня изменение конфигурации, которое могло всё это спровоцировать;git diffпокажет ТОЧНО что поменялось. - Чинишь и проверяешь JSON/API (§5): после исправления и
docker compose up -d --build—curl https://bank.rbrstack.com/api/health, ждёшь{"ok":true}в ответ, а не гадаешь по внешнему виду сайта.
Ни один отдельный навык здесь не решил бы задачу — только их последовательность, применённая методично (твой закон №4 «проверяй живое», а не первое предположение). Заметь и то, чего в этом расследовании НЕ произошло: ты ни разу не написал ни строчки нового кода — только читал состояние системы и поправил один настроечный файл. Это ровно тот уровень, который требуется от директора завода, а не от разработчика.
Где здесь деньги: каждая минута, которую ты сейчас тратишь на docker ps вместо переписки с ассистентом «а что там с сервером», — это минута, отвоёванная у роли «человек-клей» (твоя боль №1 из профиля). Больше того: без этой азбуки ты физически не сможешь САМ проверить ночной прогон строителя приложений или консультант-фабрики утром — а значит, не сможешь доверить системе полную автономию, потому что «доверяй, но проверяй» требует уметь проверить. Азбука — это цена входного билета в ночную автономию, а не бюрократия ради бюрократии.
Чек-лист внедрения
Отметь каждый пункт, сделав его руками, не через ИИ-ассистента (цель — мышечная память, а не разовый просмотр):
- [ ] Открыл терминал, набрал
pwd,ls,cd— вошёл и вышел из двух-трёх папок. - [ ]
ssh root@— зашёл на сервер без пароля, набралhostname && uptime && df -h. - [ ] На сервере:
docker ps— нашёл в спискеchannel-hq,memory-bank,resource-governorглазами. - [ ]
docker logs --tail 50 memory-bank— прочитал реальный журнал живого сервиса. - [ ]
docker exec -it <любой контейнер> bash→printenv | grep URL— заглянул внутрь палаты. - [ ] В любой папке репозитория на ПК:
git status,git log --oneline -5,git diff(если есть незакоммиченные правки). - [ ] Открыл любой
.env-файл сервиса текстовым редактором, нашёл 3 знакомых ключа, НЕ менял ничего. - [ ] Открыл любой
settings.json/profile.json, нашёл значение, мысленно поправил (без сохранения) — проверил, что скобки и запятые не сбились. - [ ]
ss -tlnp | grep LISTENна сервере — свёл список портов со списком в04-server.md. - [ ] Специально вызвал
curl https://bank.rbrstack.com/api/health(или через браузер) — увидел живой JSON-ответ и его HTTP-код. - [ ] Нашёл в
06-errors-and-gotchas.mdодну грабли, которую сможешь узнать по симптому в следующий раз без подсказки. - [ ] Открыл файл через
nanoпрямо по ssh на сервере (не на ПК), сохранил без изменений (Ctrl+O,Enter,Ctrl+X) — почувствовал разницу с редактором на своём ПК. - [ ] Проговорил вслух своими словами разницу
127.0.0.1и0.0.0.0— если объяснение получается короче двух предложений, значит действительно понял, а не запомнил формулировку.
Когда все пункты отмечены — «живые якоря» остальных модулей курса перестают быть страшными: это те же самые 8-9 движений, просто применённые к новой теме. Не обязательно проходить чек-лист за один вечер — это ежедневная разминка на первую неделю, пока руки не привыкнут искать docker ps раньше, чем вопрос к ассистенту.
Мини-проверка себя (3 вопроса, ответь вслух, не подглядывая, потом сверься с подсказкой в скобках):
1. Портал channel.rbrstack.com не открывается, браузер пишет 502 — с какой ОДНОЙ команды ты начнёшь расследование и почему именно с неё? (подсказка: та, что покажет, жив ли контейнер вообще, прежде чем читать журнал).
2. В .env сервиса есть правильное значение ключа, но фича всё равно не работает — какое из трёх звеньев цепочки ты проверишь ПЕРВЫМ и почему не сразу «код»? (подсказка: цепочка идёт от файла на диске к тому, что реально видит запущенная программа, не наоборот).
3. Чем 127.0.0.1 отличается от 0.0.0.0 в одном предложении, без терминов из учебника? (подсказка: вопрос «кто снаружи может постучаться в эту дверь», а не «где физически находится сервер»).
Если на все три вопроса ответ пришёл без подглядывания в текст выше — азбука усвоена, дело за практикой, а не за повторным чтением.
Куда это в курсе
Этот модуль — несущие леса (Приложение COURSE-GAPS, пункт №3), место в структуре: между Модулем 0 (Введение) и Модулем 1 (Основы агента), как «Модуль 0.5» — ставится рано специально, потому что от него зависит интерактивность (живые якоря) всего, что идёт дальше. Прямая зависимость: КАЖДЫЙ «живой якорь» с М1 по М11 предполагает, что студент уже умеет открыть терминал, зайти по ssh, прочитать docker ps/docker logs и не бояться JSON — это ровно то, что здесь закрыто.
Отдельные пункты дальше углубляются предметно, азбука — это их фундамент, не замена:
- git-паттерн памяти агента (main/commit/log) — Урок 3.3 «Git-память для строителя».
- матрица портов и биндингов 0.0.0.0 — Урок 4.4 «Sandbox и безопасность: изолируй ночного строителя от секретов завода».
- .env-цепочка секретов и карта секретов завода — Урок 4.1 (матрица риска) и Урок 11.2 (изоляция ночного билдера от боевых .env).
- чтение логов при тихих отказах (silent failure) — Урок 6.3 «Ночь без человека» и Урок 8.3 «Единая веб-панель».
- docker/контейнеры как единица изоляции — Урок 4.4 и Урок 11.2 (сравнение Docker/gVisor/microVM).
- JSON и API-контракты — Урок 1.3 «Инструменты (tool use)», где тот же формат станет языком, которым агент описывает свои функции.
Если проверка себя (три вопроса выше) прошла легко — двигайся в Модуль 0 «Введение: организм, а не программа». Если нет — вернись к чек-листу и повтори живые якоря ещё раз, не читая теорию заново: это навык рук, а не знание.