Переиспользуем существующий SSH-ключ мейнтейнера (git ssh-signature format, не GPG). allowed_signers
пришпилен в репо; update.sh/release.sh делают verify-tag по нему перед сборкой/публикацией. Теги
подписываются нейтральной личностью Telemax Release <release@telemax> — личный email в историю не течёт.
На хосте нужен только ssh-keygen (уже есть через openssh).
Вырезана веб-панель целиком (express/ws/react/vite/tailwind, TLS-серт, /api/*, API_KEY, порт 3000) — мост теперь headless: MAX по исходящему сокету, Telegram long-polling, управление и авторизация только в боте (/login, /panel). Убрана поверхность атаки, bundle меньше.
Анимированные стикеры (.tgs) стали опциональными: build-arg STICKERS=full|slim. full (деф.) ставит Chromium+ffmpeg (образ ~1.7 ГБ); slim пропускает их (~0.3 ГБ, быстрая сборка), анимированные уходят статической картинкой (thumbnail). setup.sh спрашивает при установке (30с таймаут, не тормозит).
Подпись обновлений (Phase C): update.sh/release.sh проверяют GPG-подпись релизного тега при наличии pinned-ключа release-signing-key.asc (транспорт GitHub/зеркало недоверенный, важна только подпись мейнтейнера). Активируется после добавления публичного ключа.
Убираем хардкод IP прод-сервера из репозитория — ходим на зеркало по тому же
DDNS-имени, что уже используется в клиентском коде (version.ts/update.sh/
install.sh). IP в публичном коде больше нет.
Живые входящие (handleMaxPush) теперь двигают historyBackfillCursor после
доставки — раньше только бэкфилл и исходящие двигали его, поэтому catch-up на
реконнекте MAX-сессии перечитывал уже доставленные входящие (time > устаревший
курсор) и слал их в Telegram повторно. Симметрично уже существующему
rememberOutgoingSend для исходящих.
Зеркальный latest.json теперь несёт карту changelogs (версия -> заметки);
version.ts собирает все версии новее текущей, так что прыжок через несколько
версий показывает изменения каждой, а не только последней (как compare-API GitHub).
git clone/pull теперь при неответе GitHub (флаг/фильтрация) переключаются на
резервное зеркало; GIT_TERMINAL_PROMPT=0, чтобы скрытый репозиторий падал сразу,
а не висел на запросе логина. Бутстрап отдаётся с зеркала (release.sh кладёт
install.sh в веб-корень).
update.sh и проверка версии при неответе GitHub переключаются на резервное
зеркало (bare-репо + latest.json за DuckDNS-именем): обновления и уведомления
о версии работают, даже если GitHub недоступен или аккаунт скрыт. Зеркало
наполняется на релизе через scripts/release.sh (пуш в оба remote + latest.json).
MIRROR_BASE_URL / MIRROR_GIT_URL — переопределяемы через env, чтобы зеркало
можно было перенести без правки кода у установок.
Full flow reverse-engineered and confirmed live: CHECK_CODE responds
with a passwordChallenge (trackId + optional hint) instead of a login
token when the account has a password set. A new opcode, CHECK_PASSWORD
(0x0073), exchanges {trackId, password} for the same 663-char login
token CHECK_CODE would have returned directly — from there it's an
ordinary LOGIN.
verifyCode() now returns a discriminated union (ok / password_required)
instead of throwing either way, so callers can actually ask for a
password instead of just failing. Wired through everywhere MAX auth
happens: the web panel gets a new password step (with hint), setup.sh's
console flow prompts for it too, and the dev auth-cli script handles it
via the same file-polling pattern it already used for the SMS code.
A wrong password doesn't burn the trackId (confirmed live) — both the
web UI and setup.sh let you retry it freely without a fresh SMS. It's
only consumed on success, matching MAX's own behavior.
Two-way message/media bridge between MAX and Telegram forum topics, with
group management, polls, calls, forwarding, and admin commands (/help,
/reboot, /kill, /donate) from Telegram. Includes one-command install
(install.sh) and interactive first-run setup (setup.sh) for a bare Ubuntu
server.