Перейти к содержимому
Pre-MVP. Кодовая база 1.0 не является выпущенным продуктом — см. границы продукта 1.0.

Граница продукта 1.0

Статус: Pre-MVP. Не выпущен. Последнее обновление: 2026-08-27.

Основание: текущий код репозитория, целевые локальные тесты и канонические спецификации. Полного журнала приёмки на реальном Cloudflare или авторизованной приёмки портфеля из четырёх сайтов не существует. Релизные гейты определяет SPEC.md §11.

Продукт: headless CMS для определения структурированного контента и работы с ним, которая может сгенерировать автономный бэкенд, принадлежащий владельцу. Graduation выдаёт нормализованную схему Prisma, миграции, пригодные для ревью, сгенерированные маршруты со знакомой семантикой моделей/запросов Prisma и опубликованный контент в репозиторий, базу данных и среду выполнения, которыми владеет владелец. Сгенерированный бэкенд читает нормализованные таблицы и не имеет зависимости от Dee Wan во время выполнения.

Cloudflare — текущая платформа. Strapi — первый адаптер миграции. Ни то, ни другое не определяет аудиторию. Импорт помогает внедрению и переносимости; это не основной тезис продукта.

Каждая возможность находится ровно в одном списке: контракт 1.0, пилот под наблюдением или недоступно. «Недоступно» — это не обещание это сделать.

Контракт 1.0 — реализовано и проверено локально

Заголовок раздела «Контракт 1.0 — реализовано и проверено локально»
  • Модели контента и поля, определяемые через UI: текст, форматированный текст, число, логическое значение, выбор, дата, медиа, JSON, связь и компонент.
  • Проверки ломающих изменений перед применением определения модели.
  • Многоязычный контент с настраиваемыми языками и политикой отката локали (fallback).
  • Неизменяемые опубликованные снимки, история версий, восстановление и публикация/снятие с публикации.
  • Прямой переход черновик/публикация и настраиваемый механизм процесса согласования (approval workflow).
  • Необязательный режим ревью, выключенный по умолчанию, для каждого сайта отдельно. Переключатель находится на вкладке настроек General; PATCH /api/sites/:id отказывает в его выключении, пока контент находится в состоянии ревью, и называет эти элементы. Когда режим включён, дашборд является очередью ревью — что ожидает, что этот вызывающий может сделать дальше, чего ожидает строка, которую он не может сдвинуть, и отдельно — что было возвращено. Когда режим выключен, дашборд показывает, что недавно изменилось. Ни в одном из случаев определения workflow не переписываются, поэтому сайт, который настроил свой, получает его обратно без изменений.
  • Восстановление несохранённой работы для обычного редактирования контента И для компоновщика страниц (page composer). Правка записывается в локальное хранилище браузера относительно серверной базовой версии, от которой она была взята, и предлагается обратно только относительно той же базовой версии, поэтому восстановление никогда не может молча перезаписать чужое сохранение. Покрывает упавшую вкладку, перезагрузку и освобождённый процесс; встроенная в приложение защита от ухода покрывает намеренную навигацию. В компоновщике базовой версией является id текущей версии страницы, а сохраняемая работа включает НЕПРИМЕНЁННОЕ ПРЕДЛОЖЕНИЕ — единственное на этом экране, что стоило вызова провайдера и для чего продукт намеренно не записывает версию. Локально в браузере и для каждого устройства отдельно: это не серверная непрерывность черновиков между устройствами.
  • Компоненты, принадлежащие сайту, с необязательной общей библиотекой. Заданный component.site_id означает, что компонентом владеет один сайт и на этом сайте достаточно model:apply; NULL означает общую библиотеку, которую читает каждый сайт, а пишет только суперадминистратор. Уникальность действует в пределах владельца, поэтому два сайта могут каждый владеть своим hero. Сайт может сделать FORK общего компонента в себя. Форк — это ПЕРЕОПРЕДЕЛЕНИЕ НА ВЕСЬ САЙТ: он не переписывает ни одной строки, и каждое поле на этом сайте, уже ссылающееся на этот slug, после этого указывает на форк. Маршрут возвращает количество ссылок, которые он перенаправил, а UI сообщает о последствии до нажатия. Обычное создание, которое попало бы на общий slug, ПОЛУЧАЕТ ОТКАЗ, пока вызывающий не подтвердит переопределение. SLUG компонента НЕИЗМЕНЯЕМ после создания — через него на компонент ссылаются все поля, вложенные компоненты и пресеты, и нигде не хранится id, поэтому переименование молча перенаправило бы или осиротило бы каждую ссылку; имя остаётся редактируемым. Предупреждения о форке и защита от удаления учитывают ссылки ИЗ ОБОИХ источников: полей моделей и определений других компонентов. На компоненты везде ссылаются ПО SLUG, поэтому собственный компонент ЗАТЕНЯЕТ общий компонент с тем же slug — видимый набор сайта имеет уникальные slug, а затенённая ссылка не учитывается для общего компонента при его удалении. Применение импортированных определений компонентов разблокировано на уровне схемы и авторизации; часть импортёра, которая их записывает, НЕ реализована, и точность переноса компонентов не продемонстрирована.
  • Роли на уровне сайта и права доступа, ограниченные владельцем.
  • Компоновка страниц в фреймворк-нейтральный структурированный документ. Ревизии используют обычное версионирование, правила публикации и откат. Это контракт контента, а не рендерер сайта.

Основная поставка отсоединяемого бэкенда

Заголовок раздела «Основная поставка отсоединяемого бэкенда»
  • Аутентифицированный GET /api/models/public-client.ts выдаёт TypeScript-клиент без зависимостей для одного сайта: точные поля моделей, union локалей, типы запросов, оболочку ответа и методы моделей.
  • Сгенерированный клиент генерируется повторно вручную после изменения модели. У него нет автоматического обнаружения расхождений или версионирования артефактов.
  • Graduation выдаёт типизированный бэкенд, принадлежащий владельцу, и снимок контента, опубликованного на момент генерации. Сгенерированная среда выполнения отсоединена от Dee Wan и использует собственный bearer-токен.
  • Процесс повторной генерации, управляемый CMS, координируется: он забирает определения и сообщает свой результат в Dee Wan. Полномочия во время выполнения и координация сборки разделены.
  • Несколько сайтов в одной инсталляции с явной областью сайта. Вызывающие не могут вывести арендатора или выбрать привязку базы данных.
  • Логическая изоляция в общей начальной (bootstrap) базе данных.
  • Сессии Better Auth в базе данных, начальное получение прав (bootstrap claim), владение паролем, восстановление, отзыв, блокировка и ограничения запросов.
  • Необязательный периметр Cloudflare Access, выключен по умолчанию.
  • Существующие учётные записи могут получать гранты на уровне сайта. Приглашения недоступны.
  • Отдельный Worker для публичного чтения. Сайт определяется по Host, никогда по ID сайта, переданному вызывающим.
  • Чтение опубликованных снимков, строгая или fallback-политика локалей, пагинация по курсору, фильтры по связям/тегам и очистка кеша по cache-тегам при публикации.
  • Публичным моделям не нужен токен. Приватные модели используют токены чтения в области сайта и не попадают в общий кеш.
  • Привязка D1 у этого Worker доступна только для чтения по правилу код-ревью. У D1 нет настройки привязки только для чтения.
  • Хранилище R2 или Cloudflare Images, с конвертацией в WebP/AVIF.
  • Генерация текста, перевод и генерация изображений через серверный прокси DeepInfra. Ключи провайдера не попадают в браузер.
  • Почасовая квота на сайт. Другие провайдеры инференса не поддерживаются.
  • Переносимый бандл сайта Dee Wan и передача контента через CSV/XLSX.
  • Первый внешний адаптер: инспекция архива Strapi v4/v5, явное сопоставление разделов (partition mapping), пробный прогон (dry run), возобновление, детерминированный повторный прогон, квитанции, проблемные строки, манифесты и защищённая отмена (undo).
  • Неоднозначное владение в источнике блокирует пробный прогон. Импортёр не угадывает по именам, slug или URL.
  • Реальные фикстуры портфеля, лимиты развёрнутого Worker и доказательство переключения/отката остаются работой приёмки.
  • CLI: планирование установки, apply/resume, диагностика, проверка, манифест и teardown в режиме dry run.

  • Локальный браузерный установщик для разработчика, у которого уже есть Node, checkout и терминал.

  • История развёртываний и отказ в откате после деструктивной миграции.

  • Настройка исходящих вебхуков, подписанная история доставок, ротация секрета и ручной повтор.

  • Отчёты об ошибках. Необработанные ошибки отправляются в проект Sentry Dee Wan, из них предварительно удаляются контент, значения форм, email-адреса и учётные данные. Суперадминистратор отключает их для всего развёртывания в Settings → Integrations → Error reports; переменная Worker DEEWAN_ERROR_REPORTING=off делает то же самое.

    Settings, Integrations: исходящие вебхуки, история доставок и переключатель Error reports

  • Ограниченный автоматический повтор для исходящих вебхуков. Доставка, которая не удалась по причине, которая могла бы пройти позже — нет ответа, 408, 429, любой 5xx, — повторяется по экспоненциальному расписанию с джиттером до пяти попыток с учётом Retry-After; отказ, который подписчик повторил бы, сразу является окончательным. Каждая попытка несёт один и тот же id события, поэтому дубликат, вызванный тайм-аутом, потребитель может распознать. Ожидающие попытки не учитываются в лимите истории доставок, а повтор отправляет в точности записанные байты, подписанные ТЕКУЩИМ секретом подписчика. Только локальное покрытие; доказательств на развёрнутой системе нет.

  • Форматированный текст — это HTML от доверенного автора, и РЕНДЕРЕР ОБЯЗАН ЕГО САНИТИЗИРОВАТЬ. Его сгенерированный тип — Html, никогда не SafeHtml и никогда не обобщённый Json, потому что ничто в этом продукте не санитизирует и не переписывает хранимую разметку. На единственной границе записи проверяются две вещи: значение должно быть СТРОКОЙ (не строка даёт структурированный 400 с указанием поля и полученного типа, никогда не строковое [object Object]), и оно должно занимать не более 256 KiB в UTF-8, считая в байтах. Всё, что создал допущенный автор или генерация ИИ, публикуется в авторском виде, поэтому потребитель, который вставляет это в страницу без санитизации, и берёт на себя риск.

  • Единый раздел Delivery (Settings → Delivery). Живой read API CMS и бэкенд, прошедший graduation, представлены как два РАЗНЫХ контракта, с различиями в маршрутах, аутентификации, оболочке ответа, развёртывании, откате и зависимости от CMS, указанными до переключения. Раздел сообщает состояния: никогда не генерировался, актуален, расходится, собирается, сбой сборки/развёртывания и отсоединён, а список ревизий сохраняет собственный отказ в откате для каждой ревизии. Только локальное покрытие; доказательств на развёрнутой системе нет.

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

  • Административный журнал аудита. Кто изменил модель, компонент, роль, грант, токен сайта, настройки, инфраструктуру, имя хоста, или кто выполнил развёртывание и откат — записывается в управляющем слое (control plane), с разбиением по сайтам, как намерение, записанное ДО действия, и результат, записанный после, связанные одним id операции. Действие не выполняется, если его намерение не удаётся записать; если результат не удаётся записать, намерение сохраняется, а исход читается как UNKNOWN, а не как успех. Одно чтение за собственным правом доступа audit:read, в Settings → History. Только добавление: маршрута записи не существует. Это не телеметрия — ни счётчиков, ни агрегатов, ни дашборда.

Пилот под наблюдением — реализовано локально, внешнее доказательство неполное

Заголовок раздела «Пилот под наблюдением — реализовано локально, внешнее доказательство неполное»
  • D1 контента на каждый сайт. Выбранная топология — одна управляющая D1 плюс одна D1 контента на каждый сайт. Разделение схемы, маршрутизация, провижининг, распространение миграций, резервное копирование/восстановление и teardown проверены локально. Доказательств на реальном Cloudflare, по ёмкости, резервному копированию/восстановлению и изоляции четырёх сайтов нет.
  • Развёртывание graduation под управлением CMS. Получение прав (claim), токен сборки, процесс сгенерированного репозитория, линия миграций, отчёт в обратную сторону и управление откатом проверены локально. Ни одно сохранённое развёртывание GitHub Actions плюс Cloudflare не доказывает весь цикл.
  • Полный путь установщика. Admin Worker и UI следуют обычному пути установки. setup также ЗАПИСЫВАЕТ backend/wrangler.public.jsonc в тот момент, когда учётная запись, зона и id базы данных уже определены; он никогда не перезаписывает существующий файл и ОТКАЗЫВАЕТ С УКАЗАНИЕМ ИМЕНИ вместо того, чтобы угадывать значение, которое не может вывести. Ручная подготовка по примеру — это запасной путь для этого отказа, а не обычный путь. Без этого файла deploy ОТКАЗЫВАЕТ, а не пропускает шаг: инсталляция, которая намеренно не отдаёт публичный контент, заявляет об этом через --admin-only, и тогда прогон отображается как admin-only, а не как завершённый. Нет ни молчаливого пропуска, ни отката на публичную конфигурацию другой инсталляции. Открытым остаётся доказательство на развёрнутой системе, а не файл.
  • Маршрутизация предпросмотра. API хранит HTTPS-origin рендерера и шаблоны маршрутов для каждой модели, а редактор открывает возвращённый отрендеренный URL. Settings → Preview читает и записывает этот контракт, сообщает, что делает кнопка Preview у ненастроенного сайта, и называет каждый отказ. Без настроенного рендерера Preview открывает JSON-ответ публичного API, и экран об этом сообщает.
  • Протокол удалённых плагинов v1 (сторона ядра). Установка из HTTPS-манифеста с выбранными вручную грантами на возможности и модели, хешированные и ротируемые токены инсталляции, disable/enable/uninstall, одноразовый делегированный запуск, чтение контента в пределах области и закреплённая команда publish/unpublish (SPEC-PLUGINS.md). Команда заново проверяет полномочия делегирующего человека в момент выполнения, применяется не более одного раза на ключ идемпотентности и ревизию workflow, публикует всю группу переводов и отвечает на каждый отказ именованным окончательным кодом. Только локальное покрытие; доказательств на развёрнутой системе нет.
  • Резервное копирование и восстановление. Реализовано и проверено на локальном SQLite. Не проверено на реальной D1.
  • Миграция Strapi для четырёх сайтов. Общий путь адаптера существует. Авторизованных архивов, контрактов потребителей, проверенных разделов, staging, доказательств переключения и отката нет.

Позже / недоступно — не реализовано или отключено

Заголовок раздела «Позже / недоступно — не реализовано или отключено»
  • Рендерер-эталон для продакшена, стартовая тема и полный путь поставки SEO/sitemap/редиректов.
  • Установщик без терминала или зарегистрированное OAuth-приложение Cloudflare.
  • GitHub App; текущее подключение репозитория использует device flow.
  • Приглашения, полностью. Неактивный механизм ссылок находится за ENABLE_INVITATIONS; отслеживаемые конфигурации оставляют его неустановленным, и все маршруты приглашений отвечают 404. Проверенной доставки почты или шага подтверждения адреса не существует.
  • Массовые действия, сохранённые представления и согласованные релизы контента.
  • Кадрирование медиа, фокальная точка и обработка дубликатов. ПОИСК медиа и фильтрация по alt-тексту реализованы.
  • Назначения на ревью, комментарии и уведомления.
  • Очередь вебхуков сверх описанного выше ограниченного повтора и любая гарантия доставки сильнее, чем at-least-once.
  • Непрерывная синхронизация CMS с базой данных, прошедшей graduation.
  • Редактирование точных копий или заблокированных секций в компоновщике страниц.
  • Шардирование базы данных на уровне моделей.
  • Корпоративный SSO, MFA и passkeys.
  • Запланированные публикация и снятие с публикации в ядре. Планирование — это отдельный плагин Scheduling (plugins/scheduling/), не развёрнутый.

У Dee Wan две поверхности поставки для разработчиков:

  1. Публичный API CMS работает вживую. Публикация сразу обновляет чтение его снимков и очищает кеш по cache-тегам.
  2. Бэкенд, прошедший graduation, — это релиз схемы и контента. Он содержит то, что было опубликовано на момент запуска генерации. Более поздние публикации в CMS попадают в него только после явного повторного graduation и повторного развёртывания.

Не описывайте graduation как живой поток. Не описывайте локальное тестовое покрытие как релиз. Первое заявление о 1.0 требует, чтобы каждый открытый внешний и финальный гейт из SPEC.md §11 прошёл на одном конкретном коммите.