DigitableCourses

Проба слоёв

Состояние портала

По каждому слою страница печатает три вещи: чем он проверен, какой отказ проверка ловит и какой не ловит. Третье здесь не оговорка, а главное. 4 августа продажи стояли сутки, и всё это время здоровье сервиса отвечало paymentsConfigured: true, mode: live, status: ok — потому что признак «реквизиты заполнены» и признак «до банка есть дорога» это разные признаки, а различал их только человек, который в тот день никуда не смотрел. Зелёный флаг, который горит всегда, вреднее отсутствия флага, поэтому колонка «не ловит» тут заполнена везде и всерьёз.

Опрос выполняет ваш браузер, а не сервер. Так проверяется тот же путь, по которому идёт читатель: маршруты /api/, статика, файлы, доставленные в обход сборки. Сводка, собранная на сервере, показала бы «200» и там, где до браузера ничего не доехало — а именно так и терялись кнопка «Слушать», числа счётчика и подпись манифеста. Единственное, чего браузеру не видно, — TLS-дорога до банка; её проверяет сам сервис каждые пять минут и отдаёт готовый ответ полем acquiringTrusted, поэтому второй такой пробы здесь нет.

Все опрашиваемые адреса и без этой страницы открыты любому — она их только собирает в одно место и печатает из ответа лишь заранее перечисленные поля. Ни одного адреса, который создаёт заказ, обращение или платёж, здесь нет и быть не может: опрос ходит только за чтением.

Итог

Опрос не выполнялся: карточки напечатал сервер, слои опрашивает скрипт в браузере.

Слои

  1. API портала отвечает

    не опрошено

    Проба
    GET /api/health — код 200, тело разбирается как JSON, поле status равно ok.
    Ловит
    Сервис не запущен, упал или перестал получать запросы по /api/. Заодно — момент почасового провала: когда задание аналитики съедало память и роняло сервер, опрос в это окно не получил бы ответа.
    Не ловит
    Провал, который был пять минут назад: проба мгновенная, истории у неё нет. Состояние базы, очереди писем и диска — здоровье их не проверяет, поэтому «API отвечает» не значит «заказ создастся».
  2. До банка есть дорога

    не опрошено

    Проба
    Поле acquiringTrusted из /api/health равно true, а acquiringCheckedAt не старше 15 минут (сервис проверяет дорогу каждые пять). Поля нет — «неизвестно», а не «живо»: сервис нарочно не печатает его, пока проверять нечего.
    Ловит
    Отказ 4 августа: реквизиты заполнены, режим боевой, а Init не доходит до банка — рвётся на проверке цепочки сертификатов. И смерть самой пробы: если серверная проверка перестала повторяться, отметка времени застынет и вердикт станет «неизвестно», а не «живо».
    Не ловит
    Банк принимает Init, но платёж не проходит дальше; отказ фискализации; неверный терминал, тариф или сумма. Это одно рукопожатие TLS, а не пробный платёж.
  3. Оплата в боевом режиме

    не опрошено

    Проба
    В /api/health поле mode равно live и paymentsConfigured равно true; рядом печатается receiptMode.
    Ловит
    Прод, оставшийся в тестовом режиме: кнопки нажимаются, заказы «оплачиваются», денег нет. И обратное — реквизиты стёрлись, а страницы оплаты продолжают предлагать оплатить.
    Не ловит
    Ровно то, чего не поймал этот признак 4 августа: заполнено — не значит работает. Живой считается эта пара только вместе с карточкой выше, и по отдельности зелёный флаг здесь не означает продаж.
  4. Сайт и API про одну редакцию документов

    не опрошено

    Проба
    GET /api/checkout/configofferVersion равно 2026-08-04, privacyVersion равно 2026-08-01. Обе даты вшиты в этот сайт при сборке, поэтому сравнивается не API сам с собой, а API с тем, что читатель видел в форме.
    Ловит
    Расхождение редакций: читатель принимает оферту одной версии, а продавец записывает согласие на другую. Гейт выкатки сверяет это один раз при деплое — и больше никогда; после ручной правки настроек сервиса не сверяет никто.
    Не ловит
    Правку текста оферты или политики без смены номера редакции: номера совпадут, а документы разойдутся. Это ловит манифест целостности, а не эта проба.
  5. Счётчик посещаемости считает

    не опрошено

    Проба
    GET /api/analytics/content-stats.jsonsource.type равно page-beacon, totals.views и totals.pages больше нуля, последний просмотр не старше суток. Заодно сверяются обещания приватности: cookies, externalTrackers и rawIpAddressesStored — все false.
    Ловит
    Нули при живом журнале — отказ, при котором файл статистики показывал ноль на 27 тысячах обращений, потому что каждая выкладка затирала числа заготовкой. Молчание маячка: числа есть, но последний просмотр вчерашний. Возврат к файловому источнику. И регресс приватности — если счётчик начнёт хранить сырые адреса, это станет красным, а не незамеченным.
    Не ловит
    Недоучёт по устройству счётчика: читателей без JavaScript он не видит вовсе и видеть не может. И усадку числа: сутки — грубая мерка, провал на несколько часов внутри неё не различим. Само число печатается рядом — два опроса подряд покажут, движется ли оно.
  6. Манифест целостности и подписи отдаются

    не опрошено

    Проба
    HEAD /integrity.json — код 200; GET /integrity.json.minisig и /en/integrity.json.minisig — код 200 и подпись начинается с untrusted comment:. У манифеста спрашивается только наличие: он весит сотни килобайт, и тащить его в браузер ради галки — плата не по делу.
    Ловит
    Выкладку, потерявшую подпись или манифест, — в том числе английскую редакцию, которая подписывается отдельно. Без манифеста инструкция на странице проверки целостности перестаёт работать, и сейчас об этом не узнаёт никто.
    Не ловит
    Верность самой подписи: minisign — это Ed25519 поверх blake2b, ни того ни другого в браузерном WebCrypto нет, поэтому проверяются присутствие и форма, а не криптография. Настоящая проверка — одна команда в терминале: minisign -Vm integrity.json -P <ключ>. Совпадение манифеста с реальными файлами страниц эта проба тоже не считает.
  7. Озвучка доехала до сервера

    не опрошено

    Проба
    GET /audio/post/3d-pipeline/00-overview/manifest.js — код 200 и объявление window.DIGITABLE_READER_AUDIO; затем HEAD на первую запись по адресу из этого же манифеста. Материал выбран сборкой из data/audio/index.json — тот же список, по которому печатается кнопка «Слушать».
    Ловит
    Отсутствие озвучки на сервере: записи доставляются в обход сборки и не проходят через git, поэтому ни сборка, ни выкладка их не видят. Манифест есть, а записи нет — отдельный вердикт: так выглядит незавершённая доставка.
    Не ловит
    Содержимое записи: тот файл, но не тот голос или не тот текст. Покрытие за пределами одного проверяемого материала. Рассинхрон нарезки с текстом статьи после правки — это ловит пересборка сегментов, а не проба.
  8. Кнопка «Слушать» напечатана в разметке

    не опрошено

    Проба
    GET первых 128 КБ страницы /post/3d-pipeline/00-overview/ — в разметке встречается data-reader-audio-trigger. Проверяется отданный сервером HTML, а не то, что дорисовал скрипт: кнопка обязана приезжать разметкой.
    Ловит
    Ровно тот отказ, при котором кнопка не печаталась в проде ни на одной странице: признак наличия звука проверялся по файлу на диске сборщика, а на сборочной машине каталога с записями нет вовсе. Записи отдавались, кнопки не было, и заметили это случайно.
    Не ловит
    Кнопка есть, но нажатие ничего не играет — это соседняя карточка про доставку записей. Кнопку на материале, озвученном частично: полноту решает сборка по индексу, проба лишь смотрит на результат.
  9. Индекс поиска на месте

    не опрошено

    Проба
    HEAD /search-index.json — код 200 и вес не меньше 225 КБ — по 200 байт на каждый из 1156 материалов и видео. Порог занижен нарочно: замер даёт 1336 байт на запись, а записей в индексе больше, чем материалов (туда же едет каталог кейсов языка). Тело не запрашивается: страница состояния не тащит индекс поиска ни при загрузке, ни при опросе.
    Ловит
    Индекс пропал или уехал заготовкой: поиск при этом не ломается заметно — он молча ничего не находит. Порог считается от числа материалов, поэтому растёт вместе с порталом сам.
    Не ловит
    Свежесть индекса: вес не отличает сегодняшний индекс от прошлогоднего. Качество выдачи и работу самого диалога поиска — это браузерный набор сценариев, а не проба.
  10. Библиотека диаграмм доставлена

    не опрошено

    Проба
    HEAD /js/mermaid.min.js — код 200. Тело не запрашивается: библиотека весит около трёх мегабайт.
    Ловит
    Библиотека не доехала до сервера или уехала под другим адресом. Тогда у читателя вместо каждой схемы остаётся пустой блок или сообщение об ошибке — на всём портале сразу.
    Не ловит
    Главное и худшее: диаграммы с битым синтаксисом. Их разбирает только сам mermaid, и часть схем портала так и падает — читатель видит текст ошибки вместо чертежа. Числа здесь нарочно нет: его никто не пересчитывает, а вписанное число устаревает молча. Эта проба такие схемы не видит вовсе и остаётся зелёной при них; настоящая проверка — прогон парсера по собранным страницам в сборочном конвейере, и её ещё нет.

Справка, а не проверка

Ниже — опознавательные знаки сборки и сервиса. Красными они не бывают: сайт и сервис выкатываются по отдельности, и расхождение между ними — обычное дело, а не отказ. Смотреть на них стоит, когда проба покраснела и надо понять, то ли это, что вы только что выкатили.

Сайт собран
2026-08-07 01:03 UTC
Материалов и видео
1156
Релиз API
Часы сервиса