Головна/Завдання/Частина 4/Стек і збірка
Стек і збірка: що ми збираємо з вашого коміту
1Розкладка репозиторію
<ваш репозиторій>
.zoria логін у мережі (частина 1, P1-05)
README.md команди встановлення, збирання і запуску
compose.yaml стек здачі
.npmrc реєстр конкурсу і посилання на ZORIA_NPM_AUTH, без секрету
bot/ каталог бота з частини 2
package.json поле zaria.bot: команда запуску
nakaz.yaml ваша грамота
Dockerfile образ голови
src/ ...
control-plane/ службові ручки (04-service.md)
hq/ каталог штабу з частини 3
package.json поле zaria.hq: каталог збірки
Dockerfile образ штабу
DECISIONS.md розбір рішення, до фінальної здачі (scoring.md, 10.1)Імена bot і hq - із шаблонів zaria-kit; свої імена припустимі, поки каталог бота знаходиться за полем zaria.bot, а каталог штабу - за zaria.hq, і таких каталогів по одному. Клієнт мережі з частини 1 живе там само, де жив.
npx zaria-kit init stack дописує compose.yaml і сервіси навколо того, що у вас уже є, і нічого не перезаписує: каталог бота він знаходить полем zaria.bot, каталог штабу - полем zaria.hq. Каталогу бота немає - кладе і шаблон частини 2. Свій README.md він не чіпає: наступні кроки тоді лягають поруч файлом STACK.md. Переписати СВОЇ файли стека поверх колишніх - --force; ваш код не чіпається і ним.
2Стек: ролі й мітки
Стек - один файл compose.yaml у корені репозиторію. Сервісів може бути скільки завгодно; конвеєру треба знати три ролі і шлях щоденника віри, і він читає їх мітками:
| Мітка | На чому стоїть | Що означає |
|---|---|---|
zaria.role: head | рівно один сервіс | голова на проводі: їй вузол кладе ZARIA_URL і ZARIA_TOKEN |
zaria.control: <порт> | не більше одного | службові ручки (04-service.md); без мітки - голова, порт 7801 |
zaria.hq: <порт> | не більше одного | штаб: віддає зібрану сторінку; без мітки - сервіс з іменем hq, порт 8080 |
zaria.belief: <шлях> | на голові | абсолютний шлях щоденника віри всередині контейнера |
Сервіс без мітки zaria.role, названий head, теж вважається головою: шаблон працює і без міток.
Приклад мінімального стека:
services:
head:
build: { context: ., dockerfile: bot/Dockerfile }
labels:
zaria.role: head
zaria.control: '7801'
zaria.belief: /data/belief.ndjson
environment:
ZARIA_URL: ${ZARIA_URL:-}
ZARIA_TOKEN: ${ZARIA_TOKEN:-}
ZARIA_BELIEF: /data/belief.ndjson
volumes: [ diaries:/data ]
hq:
build: { context: ., dockerfile: hq/Dockerfile }
labels: { zaria.hq: '8080' }
volumes:
diaries: {}Щоденник віри обов'язковий (P2-05): за ним працює Ш1 вашого штабу, і його ми знімаємо з машини перед відкатом. Шлях оголошується міткою zaria.belief, а голові передається своєю змінною.
3Правила машини
Машина учасника - не ваш ноутбук: на ній поруч живуть чужі контейнери і наш шлюз. Тому стек перевіряється правилами; порушення - провал ступеня stack з переліком усіх знайдених місць одразу.
Заборонено в сервісі: privileged, cap_add, devices, device_cgroup_rules, volumes_from, external_links, gpus, cgroup_parent, runtime, credential_spec, isolation.
Заборонені чужі простори імен і мережі: network_mode крім none і service:<ім'я>, pid, ipc, uts, userns_mode, cgroup зі значенням host або container:<ім'я>.
security_opt - тільки no-new-privileges.
Томи - тільки іменовані (type: volume) і tmpfs. Bind-том з хоста заборонено: хост не ваш.
Мережі і томи верхнього рівня - без external, без свого name, без driver_opts і свого ipam; драйвери - bridge для мереж і local для томів. Розділи secrets і configs верхнього рівня заборонено: файли запікаються в образ.
Збірка: контекст і Dockerfile - усередині репозиторію (симлінк назовні вважається виходом назовні), без ssh, secrets, additional_contexts, entitlements, без privileged і без network: host.
env_file - тільки з репозиторію.
include і extends заборонено: стек описується одним файлом. Вони тягнуть чужі файли під час читання, і після нормалізації їх уже не видно, а замок публікується поруч із вердиктом.
Імена образів ставить конвеєр. image: поруч із build: ми перевизначаємо, щоб ваш тег не перетер чужий образ на спільному хості.
Мережа машини під час прогону і партії закрита: бачити можна лише шлюз і сервіси власного стека (доповнення 15).
4Збірка
Збірка йде в пісочниці на нашому боці, з дерева зафіксованого коміту: docker compose config нормалізує файл, далі збираються всі сервіси, у яких є build.
Мережа збірки - тільки реєстр конкурсу https://npm.zoria.net/. Він же проксує публічний npm, тому звичайні залежності ставляться як завжди; прямих виходів в інтернет у збірки немає. Секрет реєстру кладемо ми: у .npmrc вашого репозиторію лежить посилання на змінну ZORIA_NPM_AUTH, а не сам секрет (частина 2, P2-07).
Ліміти: 20 хвилин на всю збірку стека, 200 МіБ на дзеркало репозиторію, образи - у межах диска машини учасника. Збірка, що не вклалася в ліміт, - провал ступеня build; дзеркало більше за 200 МіБ - провал ступеня repo.
Lockfile обов'язковий: npm ci без package-lock.json не працює, а збірка без lockfile невідтворювана. Те саме для штабу.
Базовий образ беріть за digest (node:24.15.0-bookworm-slim@sha256:...), як у шаблоні: тег переїжджає, digest - ні.
Збираємо ми, і тільки ми: образ, зібраний у вас, у залік не їде - ні за тегом, ні за digest. Так збірка лишається відтворюваною з коміту.
5Замок стека
Після збірки конвеєр пише замок: нормалізований compose.yaml з викинутими полями, які ставить машина (build, image, ports, container_name, env_file, deploy, develop, profiles), і з ID зібраних образів. Ключі відсортовано на будь-якій глибині.
digest замка - sha256 його байтів. Він їде у вердикт (01-submit.md, 4.1) і в запис пакета: за ним видно, що в партії грав саме цей стек.
Замок і SHA коміту - частина походження вашого коду: вони лягають у запис пакета. Вам їх видно у вердикті, сусідам по трійці - ні (part3/03-record.md, 6.2).
6Мова бота і секрети
Бот - TypeScript або JavaScript під Node.js 24 або новіший (scoring.md, 5.1а). Ступінь lang перевіряє машинно: поле zaria.bot у package.json каталогу бота запускає node з файлом із цього каталогу, а вихідний код у каталозі - .ts, .js, .mjs, .cjs. Інша мова - провал прогону з причиною "мова бота", коміт не прийнято.
Обхід - дочірній процес іншою мовою, WebAssembly з кодом бота, бінарник у каталозі - порушення за драбиною санкцій (scoring.md, 13.7). Звичайні залежності з нативними частинами порушенням не вважаються: ідеться про код вашого бота, а не про те, якою мовою написано zlib.
Штаб цим правилом не зв'язаний: його збирає та команда, яку ви назвали в README.
Секрети: на прийманні дзеркала працює наш сканер витоків. Він читає дерево коміту і ВСЮ його історію - усіх предків, разом із влитими гілками; інших гілок репозиторію не читає (01-submit.md, 6.1). Шукає за закритим списком родів: закриті ключі, токени випускачів (GitHub, npm, AWS, Slack та інші - кожен упізнається своєю формою), вписаний у .npmrc секрет реєстру і пароль, присвоєний рядком у коді. Посилання на змінну (${ZORIA_NPM_AUTH}), явні заглушки, мітки в квадратних дужках ([REDACTED]), значення з пробільними символами і значення у формі імені змінної середовища (ZORIA_NPM_AUTH) витоком не вважаються. Секрет у .npmrc - лише символи токена після _auth= чи _authToken=, щонайменше 16, а не код навколо такого рядка (доповнення 14). Пароль у коді не шукається на тестових шляхах - у каталогах test/, tests/, __tests__/, __mocks__/, e2e/ на будь-якій глибині і у файлах *.test.*, *.spec.*; блоб історії тестовий, лише якщо тестові всі його шляхи. Роди, що впізнаються формою, шукаються і там (доповнення 13). Знайдений секрет - провал ступеня secrets із родом, шляхом і номером рядка в detail, у дереві й в історії; коміт не прийнято (scoring.md, 8.2). Список родів закритий і запінений комітом вузла: оновлення стороннього сканера не повинне міняти вердикт посеред сезону. Високоентропійний рядок без розпізнавальних знаків він не ловить. Ширше ловить захист пушу на вашому боці (00-part4.md, P4-06): push protection GitHub або локальний хук pre-push зі сканером на загальних правилах - і ловить до того, як секрет поїде в історію.
Токен реєстру, печатки вузла і паролі в репозиторії не потрібні взагалі: усе, що потрібне збірці й партії, кладемо ми змінними середовища.
7Що ми кладемо вашій машині
| Змінна | Кому | Коли |
|---|---|---|
ZARIA_URL | голові | адреса шлюзу доріжки перед партією |
ZARIA_TOKEN | голові | печатка місця на цю партію |
ZARIA_FREEZE | голові | 1 на заліковій партії: промоут відповідає відмовою |
ZORIA_NPM_AUTH | збірці | секрет реєстру конкурсу |
Більше нічого ззовні не приходить. Машина між прогонами пакета відкочується до знімка, зробленого одразу після розгортання: усе, що бот устиг записати на диск, зникає, крім щоденника віри, який ми знімаємо до відкату.