Экранное время, которое ребёнок зарабатывает сам — а выдаёт его сервер, не родитель.
Ребёнок открывает на планшете свой учебный сайт, смотрит разбор задания ВПР или проходит короткую миссию, отвечает на вопросы. Сервер проверяет ответы, записывает награду в журнал и сам поднимает дневной лимит в Google Family Link — через минуту на планшете появляются минуты, и Roblox открывается. Никаких «папа, добавь время»: сделка работает без родителя в цикле.
Тёмная тема — потому что ребёнок занимается вечером; язык интерфейса — игровой HUD, а не школьный сайт. Дизайн-система — в docs/design-system.md.
| Экран дня | Миссия по карточкам |
|---|---|
![]() |
![]() |
| Снимок тетради | Страница родителя (телефон) |
|---|---|
![]() |
![]() |
На табло — единственная валюта: минуты. Заработанное, но запертое домашкой, окрашено янтарным; выдача на планшет показывается честно, в трёх состояниях. Ошибка ребёнка не бывает красной — красный оставлен сбоям системы.
- Домашка — сначала. Отметка «сделал» принимается только со снимком тетради с камеры планшета и стоит 45 минут сразу. Пока домашка не отмечена, заработанное на сайте лежит в журнале и ждёт. В будни «не задавали» — только со снимком дневника; в воскресенье делается домашка на понедельник.
- Доказательство — ответы, а не просмотр. Видео нельзя «досмотреть» ради минут: минуты дают вопросы по разбору, и правильные ответы никогда не уходят в браузер.
- Честная арифметика. Квота = min(максимум, база + домашка + заработанное), ставится абсолютным значением. Одно задание — одна награда в день, две вкладки и перезапуск сервера не удваивают время. Баланс всегда пересчитывается из журнала.
- Сервер не доверяет Google на слово. После каждой записи квота перечитывается; три состояния выдачи — подтверждено / не применено / неизвестно. Ручную правку родителя в Family Link сервер замечает, запоминает как надбавку дня и прибавляет к заработанному — обе руки уважаются, и надбавка видна родителю в сводке.
- Ребёнок не наказан за поломку. Базовые 15 минут живут в Family Link независимо от сервера: выключенный ноутбук — это не ноль экрана.
- Родителю — только контроль. Страница
/parentсо снимками тетради по дням и кнопкой «отозвать»: минуты запираются обратно, награды в журнале целы.
| Контент | 31 разбор заданий ВПР по математике (4 класс) с видео и вопросами, сверенные с ключами, в том числе 13 полных вариантов 2025 года; 23 миссии по 9 навыкам, ротация по навыку — повтор навыка приходит с новым условием |
| Выдача времени | Home Assistant + интеграция HAFamilyLink, вызовы через REST; фоновый исполнитель с перечиткой и защитой от ручной правки |
| Приложение | FastAPI + SQLite без ORM, Jinja2, своя дизайн-система в одном CSS без внешних запросов; 420+ тестов, включая пересчёт каждой цепочки ответов |
| Железо | Старый ноутбук под Debian и Docker Compose; сайт виден только в домашней сети, наружу через туннель — по желанию |
| Стоимость | Электричество и домен. Никаких платных API в ежедневном цикле |
Проверено на настоящем планшете: команда доезжает до Family Link за 9 секунд, разовая надбавка истекает в полночь сама, ручная правка родителя не перезаписывается.
Честно о риске. У Family Link нет официального API. HAFamilyLink работает через неофициальные запросы Google, и его авторы предупреждают о возможной блокировке аккаунта. Решение принимается осознанно.
- Требования и механика — spec.md
- Текущее состояние и что дальше — docs/status.md
- Подготовка сервера — docs/etap0-checklist.md, проверки стенда — docs/etap1-checklist.md
- Домен и доступ с планшета — docs/domain-setup.md
- Педагогика миссий — docs/pedagogy-and-motivation.md
- Исследование, на котором всё основано — familylink_learning_research.md
Дальше — инструкция по запуску: две машины, установка, этап 1.
Интеграция HAFamilyLink работает через неофициальные, восстановленные обратной разработкой запросы Google. Авторы проекта пишут прямо:
использование интеграции может нарушать условия использования Google и привести к блокировке аккаунта
Для одной семьи с редкими запросами риск невелик, но нулевым он не является.
Второе. Ни один сервис из этого набора нельзя открывать в интернет: через порт
8099 отдаётся полная сессия Google. В docker-compose.yml поэтому везде стоит
127.0.0.1 — трогать это не нужно.
Это главное, что нужно держать в голове. Путаница между ними — самая частая причина непонятных ошибок.
| Рабочий ноутбук | Сервер | |
|---|---|---|
| Что это | Windows, ваше рабочее место | Samsung R428, Debian, без графики |
| Роль | здесь пишется код, здесь браузер | работает круглосуточно, держит контейнеры |
| Приглашение в терминале | PS C:\...> |
aidar@agent:~$ |
| Что здесь запускают | ssh, scp, правки кода |
docker, python3, sudo |
Признак, что вы не на той машине: команда ssh agent внутри уже открытой
SSH-сессии заставит сервер подключиться сам к себе. Выглядит это как запрос
пароля и жалобы «Address already in use».
Подключение к серверу:
ssh agentАлиас настроен в ~/.ssh/config на рабочем ноутбуке. Если соединения нет —
проверьте, что выключен AmneziaWG: с поднятым туннелем сервер недоступен
полностью, включая ping.
Уже проделано, подробности и результаты — в docs/etap0-checklist.md. Кратко, если придётся повторять на другой машине:
1. Сеть. У ноутбука мёртвый Ethernet, работает Wi-Fi:
sudo bash ~/child-mind/server/setup-wifi.shСкрипт спрашивает домашнюю сеть и запасную (хотспот телефона), настраивает
wpa_supplicant и systemd-networkd. Запускать с консоли самого сервера.
2. Доступ. На сервере:
sudo apt install -y openssh-server avahi-daemonЗатем с рабочего ноутбука создаётся ключ и кладётся в ~/.ssh/authorized_keys
на сервере. avahi-daemon даёт имя agent.local, чтобы не искать IP-адрес.
3. Проект на сервер. С рабочего ноутбука:
cd "D:/AI/Child Mind" && tar --exclude=.git --exclude=data --exclude=snapshots --exclude=.env --exclude=__pycache__ -czf - . | ssh agent "mkdir -p ~/child-mind && tar xzf - -C ~/child-mind"Этой же командой обновляется проект после правок.
4. Docker. На сервере:
sudo bash ~/child-mind/server/install-docker.shСтавит из официального репозитория Docker: в Debian trixie нет пакета с
compose v2, а старый docker-compose не понимает наш docker-compose.yml.
После установки переподключитесь (exit, затем ssh agent) — иначе
членство в группе docker не подействует и команды будут требовать sudo.
На сервере создайте .env — либо скопируйте .env.example и заполните, либо
сгенерируйте секреты на месте, чтобы они не проходили через переписку:
cd ~/child-mind && cp .env.example .env && chmod 600 .envОбязательно заполнить: TZ (должен совпадать с часовым поясом Google-аккаунта
ребёнка), VNC_PASSWORD, FL_AUTH_API_KEY. Остальные поля появятся на шаге 6.
cd ~/child-mind && docker compose up -dПервый запуск Home Assistant занимает около минуты и грузит процессор на 100% — на машине 2010 года это нормально. Проверить:
docker compose psПорты сервера закрыты для сети, поэтому их надо перекинуть к себе. Запускать на рабочем ноутбуке, не на сервере:
ssh -N -L 8099:127.0.0.1:8099 -L 6080:127.0.0.1:6080 -L 8123:127.0.0.1:8123 agentОкно не закрывать, пока не закончите. Пока туннель поднят, три адреса
127.0.0.1 на вашем компьютере ведут на сервер.
Адрес страницы должен содержать ключ API. Без него кнопка «Start
Authentication» отвечает Failed to start: защита распространяется и на запуск
авторизации, а интерфейс читает ключ из адресной строки.
Получите готовый адрес — на сервере:
echo "http://127.0.0.1:8099/?api_key=$(grep FL_AUTH_API_KEY ~/child-mind/.env | cut -d= -f2)"Дальше:
- Откройте напечатанный адрес в браузере на рабочем ноутбуке.
- Нажмите «Start Authentication».
- Во второй вкладке откройте
http://127.0.0.1:6080/vnc.html— это окно браузера, работающего на сервере. - Войдите в родительский Google-аккаунт, пройдите двухфакторное подтверждение.
- Дождитесь подтверждения на первой вкладке.
Пароль вводите вы, в окне на своём экране.
Проверить, что сессия сохранилась:
KEY=$(grep FL_AUTH_API_KEY ~/child-mind/.env | cut -d= -f2); wget -qO- --header="X-API-Key: $KEY" http://127.0.0.1:8099/api/cookies/checkОтвет {"exists": true} означает успех.
На сервере:
cd ~/child-mind && python3 tools/install_familylink.py && docker compose restart homeassistantСкрипт кладёт файлы через контейнер: каталог конфигурации создан Docker от имени root, и записать туда с диска обычный пользователь не может.
- Откройте
http://127.0.0.1:8123(через тот же туннель), создайте локальную учётную запись — она к Google отношения не имеет. - Настройки → Устройства и службы → Добавить интеграцию → Family Link.
- Адрес сервиса авторизации:
http://familylink-auth:8099— внутреннее имя контейнера, снаружи недоступно. - Ключ API — значение
FL_AUTH_API_KEYиз.env. - Strict mode оставьте выключенным. С ним Home Assistant начнёт откатывать ваши ручные изменения в приложении Family Link, а на пилоте вы должны иметь возможность вмешаться.
В Home Assistant: профиль слева внизу → Безопасность → Токены
долгосрочного доступа → Создать токен. Скопируйте сразу, второй раз он не
покажется. Впишите в .env как HA_TOKEN.
Затем на сервере:
cd ~/child-mind && python3 tools/fl.py check && python3 tools/fl.py discoverdiscover покажет идентификаторы ребёнка и устройств — впишите их в .env
(FL_CHILD_ID, FL_TABLET_DEVICE_ID, FL_PHONE_DEVICE_ID).
cd ~/child-mind && python3 tools/fl.py snapshotТочка отката: файл в snapshots/ покажет, как всё было настроено до нашего
вмешательства.
Откройте docs/etap1-checklist.md и идите сверху вниз, записывая результаты прямо в файл. Семь проверок, каждая с готовой командой. Ответ «не сработало» — такой же полезный результат: он определяет, каким путём мы пойдём дальше.
На сервере:
docker compose ps # что работает
docker compose logs -f # что происходит (выйти: Ctrl+C)
docker compose restart homeassistant
docker compose down # остановить всё
python3 tools/fl.py check # связь с Home Assistant
python3 tools/fl.py discover # id ребёнка и устройств
python3 tools/fl.py snapshot # сохранить настройки
python3 tools/fl.py show # текущее состояние
python3 tools/fl.py set-limit --minutes 35
python3 tools/fl.py set-limit --minutes 0 --phone
python3 tools/fl.py app --package com.android.chrome --mode unlimited
python3 tools/fl.py watch --minutes 10На рабочем ноутбуке:
ssh agent # подключиться к серверу
ssh -N -L 8099:127.0.0.1:8099 -L 6080:127.0.0.1:6080 -L 8123:127.0.0.1:8123 agentИмена приложений для --package:
| Приложение | Имя пакета |
|---|---|
| Chrome | com.android.chrome |
| Roblox | com.roblox.client |
| YouTube | com.google.android.youtube |
Точное имя любого другого приложения видно в адресе его страницы в Google Play,
в части ?id=.
Этап 2 — сайт с уроками, журнал наград и очередь выдачи — начинается после того, как этап 1 пройден. Если какая-то из семи проверок провалится, мы меняем исполнитель ограничений, а не переписываем всё: сайт и журнал от Family Link не зависят.



