Поток нарисован, путь «код» выбран. Сервер в Амстердаме у тебя уже есть, бот на нём работает. Не хватает двери: адреса в интернете, на который Яндекс Форма сможет прислать заявку. В прогоне на неё ушло пять заданий агенту, пройдём их по порядку.
Чему научишься в этом уроке:
• завести поддомен с записями A и AAAA и проверить их;
• поручить агенту nginx, сертификат и файрвол так, чтобы не потерять доступ к серверу;
• собрать сервис приёма заявок с секретом в адресе и отдельной службой;
• подключить Яндекс Форму и починить приём по журналу.
Что добавится на сервере
- nginx (произносится «энджин-икс»): администратор на входе. Принимает всех посетителей по адресу и провожает к нужному сервису внутри.
- Сертификат Let’s Encrypt через программу certbot: бесплатная печать подлинности для HTTPS.
- ufw: файрвол, то есть вахтёр со списком открытых дверей. Всё, чего нет в списке, закрыто.
- Сервис приёма kodium-forma: маленькая программа, которая слушает только внутренний адрес 127.0.0.1:8080. Снаружи к ней не достучаться, только через nginx.

Шаг 1. Поддомен и IPv6
Нужен домен, то есть имя сайта. Если у тебя уже есть домен, заведи в нём поддомен, например kodium.твой-домен.ru: это отдельное имя внутри домена, покупать ничего не нужно. Если домена нет, как купить и привязать его, показано в уроке трека 2 про установку на сервер.
Справка Яндекс Форм говорит, что запросы форма шлёт только по IPv6 (новый вид адресов, длинный, с двоеточиями). Поэтому поддомену нужны две записи в DNS (адресной книге интернета): A ведёт на IPv4-адрес сервера, AAAA на IPv6-адрес.
IPv6 на сервере. В прогоне IPv6 включили в панели Timeweb, и сервер получил адрес вида 2a03:…. Где именно кнопка, платно ли это и нужна ли перезагрузка, мы не записали и не проверяли. Сначала посмотри, есть ли адрес уже сейчас: на сервере выполни команду ниже. Нужна строка с пометкой scope global. Если её нет, ищи IPv6 в настройках сети сервера в панели Timeweb или спроси их поддержку.
ip -6 addr show
- Узнай оба адреса: IPv4 тот, что ты набираешь в ssh, IPv6 из строки
scope global. - Открой панель того, кто обслуживает DNS твоего домена. Обычно это регистратор, где куплен домен; в прогоне это был reg.ru.
- Добавь запись типа A: имя
kodium, значение IPv4-адрес сервера. - Добавь запись типа AAAA: имя
kodium, значение IPv6-адрес сервера. - Подожди, пока записи разойдутся по интернету: обычно до часа.
- Проверь в браузере:
https://dns.google/resolve?name=<твой поддомен>&type=AAAAи то же сtype=A. В ответе в разделе Answer должен стоять твой адрес.
Проверяй так, а не командой nslookup на своём компьютере: в прогоне nslookup на Windows не видел AAAA-запись, хотя DNS Google отдавал её верно.
Честно о прогоне: заявки от Яндекс Форм в итоге пришли по IPv4, а не по IPv6. Справка и практика расходятся. IPv6 и AAAA-запись мы оставили: так приём работает при любом поведении формы.
Шаг 2. Почему не всё сразу
В прогоне первое задание было «всё сразу»: nginx, сертификат, файрвол, сервис, служба, тесты и проверка одним текстом. Через 3 минуты и 8 ходов агент не сделал ничего и спросил, работать по шагам или скриптом. Поэтому дальше задания идут по одному. Если ты всё же отправил большое задание и агент спросил то же самое, ответь ему заданием шага 3.
Шаг 3. nginx, сертификат и файрвол
Выполняй по шагам сам, не спрашивай. Сейчас только пункт 1: на сервере <адрес сервера> поставь nginx и certbot, получи сертификат Let's Encrypt для <твой поддомен> (почта для certbot — не нужна, используй --register-unsafely-without-email), включи ufw с правилами: OpenSSH, 80, 443 (IPv4 и IPv6). ВНИМАНИЕ: сначала разреши OpenSSH, потом включай ufw, иначе потеряем доступ. Пока nginx пусть отдаёт на / простую страницу «Кодиум: приём заявок». В конце покажи: ufw status, curl -I https://<твой поддомен> с сервера и дату окончания сертификата. Бота не трогай.
Главная строка здесь про OpenSSH. Включить файрвол, не разрешив SSH, значит запереть дверь изнутри, оставив ключи снаружи. Флаг --register-unsafely-without-email значит «сертификат без почты для напоминаний». В прогоне шаг занял 3,5 минуты и 10 ходов.
Как читать ответ: HTTP/1.1 200 или HTTP/2 200 в первой строке curl значит «страница открылась». В ufw status напротив OpenSSH, 80 и 443 должно стоять ALLOW дважды: просто и с пометкой (v6).
Шаг 4. nginx и IPv6
После certbot в прогоне nginx слушал только IPv4: запрос curl -6 возвращал 000, то есть «соединиться не удалось». Записи DNS были ни при чём. Задание на починку (текст курса):
curl -6 на https://<твой поддомен> отвечает 000, а по IPv4 всё работает. Похоже, nginx после certbot слушает только IPv4. Добавь в настройку сайта listen [::]:443 ssl и listen [::]:80, проверь настройку nginx, перезагрузи его и покажи ответ curl -6 -I https://<твой поддомен>. Выполни сам на <адрес сервера>.
В прогоне это заняло 1,1 минуты, после правки curl -6 ответил 200.
Шаг 5. Сервис приёма заявок
Сначала заведи секрет. Это длинная случайная строка в адресе: как код домофона, только вписанный в ссылку. Кто не знает ссылку, тот получает 404 («нет такой страницы»). Секрет живёт в .env на сервере под именем WEBHOOK_SECRET, как остальные ключи из урока 1.6. Задание агенту (текст курса, в прогоне секрет заводили вместе с сервисом):
Сгенерируй случайный WEBHOOK_SECRET из 32 латинских букв и цифр и допиши его в .env проекта на сервере <адрес сервера>. Значение в ответе не печатай, покажи только, что строка WEBHOOK_SECRET в .env есть.
Пункт 1 готов (https работает). Теперь пункты 2–4 из задачи про форму, сам, без вопросов:
- веб-сервис приёма заявок на 127.0.0.1:8080 (aiohttp — он уже есть в зависимостях через aiogram), nginx проксирует https://<твой поддомен>/forma/ на него; адрес /forma/<WEBHOOK_SECRET из .env>, без верного секрета — 404;
- принимаем и JSON, и обычную форму; в лог — только названия пришедших полей, без значений;
- без согласия не сохраняем; иначе уведомление владельцу в Telegram «Источник: сайт» + строка в zayavki.xlsx на Диске с колонкой «Источник» (старым строкам — «бот»); бот при своих заявках тоже пишет «бот»; ответ форме быстро, отправку на Диск — после ответа;
- служба systemd kodium-forma (пользователь bot, автозапуск);
- тесты с подменой Диска и Telegram, запусти;
- выложи, перезапусти бота и форму, проверь curl с сервера: POST JSON {"name":"Тест Форма","phone":"+79000000009","course":"Python","consent":"да"} на https://<твой поддомен>/forma/<секрет> — покажи ответ и статусы служб. Секрет в ответе не печатай.
В прогоне шаг занял 11 минут и 31 ход. Итог: обе службы active и enabled; без секрета 404; без согласия 400, заявка не сохраняется; тестовая заявка пришла владельцу с пометкой «Источник: сайт» и легла строкой в xlsx. Форме сервис отвечает сразу, а на Диск пишет уже после ответа.

Шаг 6. Подключаем Яндекс Форму
Этот шаг делаешь ты сам, в браузере. Сначала посмотри свой секрет. Проще всего командой в терминале своего компьютера:
ssh root@<адрес сервера> "grep WEBHOOK_SECRET /opt/bot-kodium/.env"
Можно и заданием агенту: «покажи мне строку WEBHOOK_SECRET из .env на сервере». Тогда секрет останется в переписке с агентом, поэтому команда лучше. Скопируй значение после знака «=». Адрес с секретом в форму вставляешь ты, агента об этом не проси.
- Создай форму с вопросами: имя, телефон, курс (выбор из списка: Python, Веб-разработка, Нейросети, Пока не знаю), согласие (галочка с текстом согласия).
- Открой вкладку «Интеграции», найди карточку «API» и в её меню выбери «Запрос JSON-RPC POST».
- В поле «Адрес в интернете» вставь https://<твой поддомен>/forma/<секрет>. Метод POST.
- В «Параметры» нажми «Добавить параметр» четыре раза. Слева имя: name, phone, course, consent. Справа переменная «Ответ на вопрос» и нужный вопрос формы. Поставь галочку «Передавать, если значение задано».
- Нажми «Сохранить» и отправь форму сам как посетитель.

Кейс: честный отказ
В прогоне форму сначала подключили без параметров. Сервис ответил 400 «без согласия», в журнале стояло «Получен JSON с полями: [jsonrpc, method, params, id]». JSON-RPC это формат «конверта»: поля формы лежат внутри params, а без параметров конверт пришёл пустым. Пустую заявку без согласия сервис правильно не сохранил.
После добавления параметров данные пришли внутри params, и сервис нужно было научить их доставать. Задание прогона:
Яндекс Форма прислала заявку, сервис ответил 400 «без согласия». В логе: «Получен JSON с полями: [jsonrpc, method, params, id]» — это формат JSON-RPC, поля формы лежат внутри params (имена параметров заказчик задаёт сам: name, phone, course, consent). Значение согласия от формы может быть не «да», а текстом галочки или true — считай согласием любое непустое значение, кроме false/нет/0. Почини разбор: если пришёл JSON с ключом params — бери поля оттуда; логируй названия полей внутри params без значений. Ответ форме в формате JSON-RPC: {"jsonrpc":"2.0","result":"ok","id":<тот же id>}. Тест на такой запрос. Запусти тесты, обнови службу kodium-forma на <адрес сервера>, проверь curl-ом JSON-RPC-запросом.
Агент справился за 4 минуты. Следующая живая заявка получила ответ 200 «Заявка #5 с сайта сохранена» и дошла до Telegram и файла.
Где смотреть, что ответил сервис: в форме вкладка «Интеграции → Выполненные интеграции», запрос оттуда можно перезапустить. Как форма повторяет запросы при сбое, разберём в уроке 3.6. На сервере журнал службы:
journalctl -u kodium-forma -n 30
Если не получилось
- certbot пишет, что не может проверить домен → DNS-запись ещё не разошлась или A-запись указывает не на тот адрес → проверь через dns.google, подожди и повтори шаг 3.
curl -6отвечает 000, по IPv4 всё работает → nginx слушает только IPv4 → задание шага 4.- Форма получает 404 → в адресе неверный секрет или опечатка в пути /forma/ → сверь адрес в настройках формы с выводом команды grep WEBHOOK_SECRET из шага 6.
- Форма получает 400, в журнале поля [jsonrpc, method, params, id] → в форме нет параметров или сервис не берёт поля из params → добавь параметры и дай агенту задание из кейса.
- В уведомлении курс «Backend» или другой текст не из списка → вопрос о курсе сделан свободным полем → поменяй на выбор из списка.
Контрольный вопрос. Ты включил ufw и разрешил только 80 и 443. Через минуту ssh к серверу перестал подключаться совсем. Что пошло не так?
Подсказка: Посмотри, какие правила попали в список открытых дверей.
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. Ты отправил форму, в «Выполненных интеграциях» ответ 400, а в журнале kodium-forma строка «Получен JSON с полями: [jsonrpc, method, params, id]». Что делать первым?
Подсказка: Где в конверте JSON-RPC лежат ответы формы?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. С сервера curl по IPv4 к поддомену отвечает 200, а curl -6 отвечает 000. AAAA-запись верная. Где искать причину?
Подсказка: Записи DNS в порядке. Кто на сервере принимает соединения?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Задание. МИССИЯ «Дверь для формы». Обязательна одна из двух миссий, 3.3 или 3.4: та, путь которой ты выбрал в уроке 3.2. Вторая по желанию. 1. Заведи поддомен с записями A и AAAA, проверь их через dns.google. 2. Пройди с агентом шаги 3–5: nginx, сертификат, ufw, сервис приёма, служба kodium-forma. 3. Подключи свою Яндекс Форму с четырьмя параметрами и отправь заявку как посетитель. 4. Сломай и почини: отправь запрос с неверным секретом (поменяй в адресе одну букву) и заявку без галочки согласия. Убедись, что первая получила 404, а вторая 400 и не попала в файл. Верни правильный адрес. Принято, если: приложены снимок уведомления владельцу с «Источник: сайт»; снимок строки в zayavki.xlsx; снимок «Выполненных интеграций» с успешным запросом и с отказами 404 и 400; вывод ufw status и curl -6 -I с ответом 200. Секрет, адрес сервера, токены и телефоны на снимках закрыты.
Подсказка: Начинай с проверки curl с сервера, потом подключай форму. Так ты отделишь поломки сервера от поломок формы.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Что дальше
Путь «код» работает: форма стучится в дверь, сервис проверяет и раскладывает заявку. Дальше тот же поток соберём в n8n, без сервера и без своего кода в службах.
Итог:
• поддомен с A и AAAA, nginx слушает и IPv4, и IPv6;
• ufw включается только после правила OpenSSH;
• сервис приёма слушает 127.0.0.1:8080 за nginx, адрес с секретом, без согласия ответ 400;
• Яндекс Форма шлёт JSON-RPC, поля лежат в params, адрес с секретом вводишь сам.
