История изменений
Все значимые релизы trip2g. Новые записи идут первыми. Для каждого изменения указано
Что / Зачем / Как использовать, чтобы оператор или администратор мог решить,
стоит ли обновляться и как начать пользоваться функцией.
Теги старее v0.2.0 доступны только в истории git.
Unreleased
Поиск по сайту может не показывать недоступные заметки
- Что. Новая настройка
SEARCH_HIDE_UNREADABLE(--search-hide-unreadable), по умолчанию выключена. Когда она включена, поиск по сайту возвращает только заметки, которые читатель может открыть. Когда выключена, всё как раньше: каждая недоступная читателю заметка идёт после результатов как заглушка «Закрытый материал.» с настоящим заголовком и ссылкой. - Зачем. Заглушки показывают, что закрытый материал есть, и это удобно платному сайту. Сайт, чьи закрытые заметки не должны быть видны, не мог убрать их заголовки и ссылки из поиска.
- Как использовать. Установите
SEARCH_HIDE_UNREADABLE=trueи перезапустите сервер. На администраторов, читателей с доступом и поиск через MCP настройка не влияет. См. Как работает поиск.
MCP expand снова описывает toc_path как список строк
- Что. Схема входа инструмента
expandговорит, чтоtoc_pathэто массив строк, аlastобычное число. С v0.11.0 в схемеtoc_pathбыл массивом без типа элементов, а тип элементов стоял уlast. - Зачем. MCP-клиент, который строго проверяет схемы инструментов, мог из-за этого отказаться от
expandили от всего списка инструментов. - Как использовать. Ничего делать не нужно. Клиент, который закэшировал список инструментов, получит исправленную схему при следующем
tools/list.
Вошедший читатель открывает заметку с free: true
- Что. Заметка с
free: trueоткрыта любому читателю: гостю, вошедшему пользователю без доступа к её подграфу и админу. Раньше вошедший пользователь без доступа видел пейволл на бесплатной заметке, которую гость открывал, а форма на этой заметке отвечала емуform_not_found. Страница, GraphQL-запросnote, поиск, похожие заметки, MCP, ассеты, живые обновления заметок и отправка формы читают одну и ту же проверку, поэтому меняются вместе. Остальные заметки платного подграфа и подграфа со входом закрыты, как и раньше. - Зачем.
free: trueпубликует заметку для всех. Вход на сайт не должен закрывать то, что может прочитать гость. - Как использовать. Ничего делать не нужно.
Гость открывает заметку с free: true в подграфе со входом
- Что. Заметка с
free: trueв подграфе с флагом Require sign-in теперь открыта и гостю. Раньше гость видел на её странице и в GraphQL-запросеnoteформу входа, её ассеты отвечали 401, в кеш страниц она не попадала, RSS-фиды и другие списки через.Public()её пропускали, а поиск, похожие заметки и MCP тому же гостю её уже показывали. Теперь все пути отвечают одинаково. Заметка такого подграфа безfree: trueпо-прежнему показывает гостю форму входа. Карта сайта тоже включает такую заметку, см. ниже. - Зачем.
free: trueпубликует заметку для всех, что бы ни говорили её подграфы (ru/user/subgraphs). Форма входа противоречила остальному сайту. - Как использовать. Ничего делать не нужно. Проверьте подграфы со входом: если там есть заметки с
free: true, которые вы хотели оставить только для вошедших, уберите у нихfree. Бесплатная заметка показывает тем, кто её читает, и заметки, встроенные в неё через![[...]], поэтому закрытая заметка, встроенная в бесплатную заметку подграфа со входом, теперь видна и гостю.
Карта сайта включает заметку с free: true из подграфа со входом
- Что.
sitemap.xml, основной и каждого кастомного домена, включает заметку сfree: trueиз подграфа с флагом Require sign-in. Раньше в нём не было ни одной заметки такого подграфа, бесплатной или нет. Теперь карта сайта задаёт тот же вопрос, что страница, RSS-фиды и кеш страниц, и больше не может с ними разойтись. Как и раньше, в неё не попадают заметки безfree: true, заметки сnoindex: true, скрытые заметки, файлы.htmlи всё, что лежит по пути с/_. - Зачем. Такая заметка открыта гостю (см. выше), поэтому поисковик должен находить её, как любую бесплатную.
- Как использовать. Ничего делать не нужно. Чтобы убрать бесплатную заметку из поисковиков, добавьте ей
noindex: true.
v0.11.1 (2026-10-07)
Вебхуки оплат переехали в /_system/
- Что. Уведомления NOWPayments теперь приходят на
/_system/nowpayments/ipn, вебхуки Patreon — на/_system/patreon/webhook. По адресам/api/больше ничего не отвечает. Уведомление NOWPayments отклоняется, если не задан--nowpayments-ipn-key. - Зачем. Все системные адреса живут в
/_system/, эти два были последними вне его. - Как использовать. NOWPayments: ничего делать не нужно, trip2g передаёт адрес в каждом счёте. Счёт, созданный до обновления и оплаченный после, отправит уведомление на старый адрес, а он больше не отвечает: после обновления проверьте ожидающие покупки и выдайте доступ вручную тем, что в NOWPayments отмечены оплаченными. Patreon: ничего делать не нужно, trip2g регистрирует вебхук сам. При обычном перезапуске старая версия удаляет вебхук со старым адресом при остановке, а новая регистрирует новый адрес при старте. Если старая версия остановилась не штатно, новая всё равно зарегистрирует новый адрес; оставшийся вебхук со старым адресом ни на что не влияет, его можно удалить в кабинете автора Patreon. Участники Patreon в любом случае обновляются раз в час.
Отзыв доступа к подграфу из админки
- Что.
/api/admin/revokeusersubgraphaccessудалён. Вместо него — мутация администратораrevokeUserSubgraphAccessи кнопка Revoke на странице доступа в админке. Для отзыва нужна причина; после него страница доступа показывает, когда, кем и почему он отозван, а в списке доступов появилась колонка «Revoked At». - Зачем. Старый адрес не проверял роль администратора, и ничто в trip2g его не вызывало. Теперь отзыв идёт через пространство
admin, которое роль проверяет. - Как использовать. Админка → Users → Subgraph Accesses → открыть доступ → указать причину → Revoke. Или вызовите
revokeUserSubgraphAccessс номером доступа и причиной (ru/user/user_management).
Документация: приложение поверх trip2g
- Что. Приложение поверх trip2g теперь описывает весь путь JavaScript-приложения на своём шаблоне: как приложение получает доступ по cookie сессии посетителя, настройки во frontmatter, стандартные шапка и подвал сайта вокруг приложения, сохранение с
expectedHash, живые обновления через server-sent events, вызовы API и админские вызовы, MCP из браузера над всей базой, своё API, которое спрашивает у trip2g, кому можно его вызывать, формы и небольшой магазин как пример. Своё API размещается под/_system/extra/: trip2g там ничего не обслуживает, и это проверяет тест в его роутере. Шаблоны показывают стандартные шапку и подвал в любом своём шаблоне. - Зачем. Страница описывала только шаблон, бандл и сохранение. Чтобы сделать настоящее приложение, авторизацию, живые обновления и место для своего бэкенда приходилось искать в исходниках.
- Как использовать. Ничего делать не нужно. Перед тем как делать приложение, прочитайте Приложение поверх trip2g.
Документация: стандартный шаблон не рисует форму
- Что. Формы теперь прямо говорят, что стандартный шаблон только встраивает форму заметки как JSON: полей и капчи он не рисует. Чтобы показать форму, нужен шаблон, который её строит: готовый form_template, который показывает форму как опрос, или свой. Ещё на странице описан результат
TurnstileRequiredPayloadуsubmitForm. - Зачем. Страница утверждала, что стандартный шаблон рисует форму в конце страницы и обрабатывает Turnstile. Он не делает ни того, ни другого, и заметка с формой показывалась без формы.
- Как использовать. Положите
form.htmlиз form_template в_layouts/и задайте заметкеlayout: form. См. Формы.
v0.11.0 (2026-10-07)
Ссылки «Назад»/«Далее» и хлебные крошки
- Что. Стандартный шаблон показывает хлебные крошки над заголовком и ссылки «Назад»/«Далее» в конце страницы. Они берутся из первой заметки боковой панели (сначала левой, потом правой), где есть ссылка на страницу: соседние ссылки и заголовок над ней. Считаются и вики-ссылки, и markdown-ссылки. Свойства
prev,nextиbreadcrumbsво frontmatter их переопределяют,falseотключает. - Зачем. Документацию читают по порядку, а перейти к следующей странице можно было только через сайдбар. И не было видно, к какому разделу относится страница.
- Как использовать. Если у страниц уже есть заметка-сайдбар, ничего делать не нужно. Чтобы отключить для папки, задайте
prev: false,next: false,breadcrumbs: falsefrontmatter-патчем. См. Стандартный шаблон.
Ссылки во frontmatter ищутся как в Obsidian
- Что. Вики-ссылки в
header,footer,left_sidebarиright_sidebarищутся по тем же правилам, что и ссылки в тексте заметки: по имени файла, от заметки, в которой стоит свойство. Часть|псевдоними#заголовокне учитывается. - Зачем. Эти ссылки сравнивались только с адресом страницы. Поэтому
[[Папка/_sidebar]]с кириллицей в имени папки,[[Site Footer]]или[[Заметка|псевдоним]]ничего не находили, и сайдбар или шапка молча пропадали, хотя Obsidian показывал ссылку рабочей. - Как использовать. Ничего делать не нужно. В режиме по умолчанию (global) ссылки, которые работали, ведут в тот же файл, а те, что ничего не находили, заработают. В режиме scoped голая
[[Имя]]теперь выбирает ближайший файл, как ссылки в тексте заметки. См. Куда ведёт вики-ссылка.
Админ может скрывать заметки
- Что.
hideNotesи изменениеhideвupdateNotesработают для вошедшего админа: сессия в браузере, личный токенt2g_…в заголовке или в?token=, токен сессии. Изменениеhideработает и с токеном вебхука, в пределах его write patterns. Скрытие записывается на вошедшего админа, на админа, создавшего API-ключ, или на админа, создавшего вебхук. - Зачем. У всех, кроме API-ключа, оба вызова падали с записью в
errors, у токена вебхука так же падало изменениеhide: скрытие не на кого было записать. - Как использовать. Ничего делать не нужно. Токен вебхука, выданный до обновления, получает
ErrorPayloadот измененияhide, пока не истечёт. См. updateNotes.
Базовый шаблон может отметить место голым yield
- Что. Базовый шаблон может отметить место через
{{ yield main() }}вместо{{ block main() }}…{{ end }}; страницы, которые его наследуют, заполняют место своим{{ block main() }}. Автоимпорт больше не подключает шаблон, который начинается с{{ extends }}: такой файл является страницей, а не компонентом. - Зачем. Автоимпорт находил
mainв странице-наследнике и вставлял всю эту страницу в базу. База не загружалась, а все страницы, которые её наследуют, падали сtemplate /base could not be found. - Как использовать. Голый
yieldподходит для места, которое заполняет каждая страница,blockдля места с содержимым по умолчанию. См. Шаблоны.
Колонка текста шире на больших экранах
- Что. В стандартном шаблоне колонка текста растёт на широких экранах: 80 знаков от 1440px. От 1536px колонка шириной 56rem, раскладка растёт до 1680px с более широкими боковыми колонками, а базовый размер шрифта остаётся 18px и больше не растёт вместе с экраном. Ниже 1440px всё как раньше.
- Зачем. На большом мониторе текст сжимался в узкую полосу, а таблицы и примеры кода приходилось листать вбок.
wide: trueрешал это, но прятал оба сайдбара. - Как. Ничего делать не нужно.
wide: trueостаётся для страниц, которым нужна вся ширина (доски, большие диаграммы).
Wikilink на заголовок прокручивает к нему
- Что.
[[Заметка#Текст заголовка]]и[[#Текст заголовка]]теперь ведут на id заголовка, тот же, что у страницы и её оглавления. Текст сравнивается сначала точно, потом без учёта регистра и лишних пробелов; id вродеfaq-2или заданный через{#id}тоже подходит. Если фрагменту не соответствует ни один заголовок, ссылка теперь помечается как битая, аtrip2g lintсообщаетbroken link: Заметка#Заголовок. - Зачем. Фрагмент попадал в адрес как написан (
#Текст%20заголовка) и не совпадал ни с одним id: ссылка открывала страницу с начала, а ссылка на заголовок в той же заметке помечалась как битая. - Как использовать. Ничего делать не нужно: ссылки, написанные как в Obsidian, теперь работают. См. Markdown.
«Ссылки сюда» идут по алфавиту
- Что. Блок обратных ссылок в конце страницы и
nvs.BackLinks(note)в шаблонах отдают заметки по алфавиту заголовка (без учёта регистра), при совпадении по адресу. - Зачем. Список собирался из словаря, и его порядок менялся от рендера к рендеру: нужную ссылку было трудно найти, а одна и та же страница выглядела по-разному.
- Как. Ничего делать не нужно.
Шаблоны экранируют вывод по умолчанию
- Что. Свой шаблон (layout) теперь экранирует то, что выводит.
{{ note.Title() }}, значение из frontmatter,{{ note.ContentString() }}и любая другая обычная строка попадают на страницу текстом:<,>,&,"и'превращаются в HTML-сущности. HTML, который собирает сам сервер, по-прежнему выводится как есть:note.HTMLString(),FirstListHTML(),TitleHTMLиContentHTMLсекции,HTMLблока кода,FormSpecJSON(),SubgraphNamesJSON(),asset()иdefaultTemplate.*. - Зачем. Шаблоны выводили всё без экранирования. Заметка, в markdown которой было
</textarea><script>, вырывалась из<textarea>{{ note.ContentString() }}</textarea>и запускала свой скрипт, а заголовок с&или<ломал разметку вокруг. В каждом шаблоне приходилось помнить про| htmlна каждом значении, и хватало одного пропуска. - Как. Существующие шаблоны продолжают работать.
| unsafeи| rawпо-прежнему выводят строку как есть.| htmlиhtml(x)по-прежнему экранируют, ровно один раз, поэтому RSS-шаблон сhtml(n.HTMLString())выводит то же, что раньше; для текста фильтр больше не нужен.| jsonпо-прежнему экранирует<,>и&внутри JSON, так что{{ x | json }}безопасен внутри<script>.| urlэкранирует один раз. - Миграция. Проверьте шаблоны, которые выводят разметку из обычной строки: параметр блока вроде
title="Строка<br>вторая", HTML, склеенный через+или полученный изreplace(...). Теперь теги в нём покажутся текстом; выводите такое значение через{{ title | unsafe }}. ЗначенияhtmlInjectionsHead/htmlInjectionsBodyEndостаются обычными строками, поэтому{{ injection.Content | unsafe }}оставьте.asset()теперь кодирует в URL",',<,>, обратный слэш, обратную кавычку и пробельные символы; обычные адреса, включая подписанные ссылки хранилища с&, не меняются. См. Шаблоны.
Свои якоря заголовков и больше данных заметки для шаблонов
- Что. Заголовок может задать свой якорь:
## Цены {#plans}получаетid="plans",{.class}добавляет CSS-класс. В шаблонах у секции появилисьIDиLevel, аSection(...)находит заголовок и без учёта регистра и лишних пробелов, и по якорю ("plans"или"#plans"). Новое:Images()(markdown-картинки и встраивания![[...]]),CodeBlocks(lang), поляTaskиTaskMarkу пунктов списка (включая пользовательские статусы Obsidian вроде[/]) и функцииparseJSON,parseYAML,parseCSV. - Зачем. Сгенерированный якорь меняется вместе с текстом заголовка, и ссылки на него ломаются. Шаблон умел разбить заметку на секции, но не мог сослаться на них, найти секцию по якорю или прочитать картинки, блоки кода и статусы задач.
- Как. Допишите
{#id}в конец заголовка, см. Markdown. Функции шаблонов описаны в Шаблонах. Если заголовок уже заканчивался скобками такого вида, например## Установка {#install}, они пропадут из текста, а заголовок получит этот id.
Пустое поле во frontmatter больше не ломает синхронизацию
- Что. Заметка с полем
description:илиredirect:без значения (или со значениемnull,~) теперь загружается: поле считается незаданным. - Зачем. Раньше такая заметка роняла загрузку, и вся пачка синхронизации из ста заметок не проходила с ошибкой «invalid description type: ». В справке Obsidian так оформлены несколько страниц.
- Как. Ничего делать не нужно. Если синхронизация раньше падала с этой ошибкой, запустите её ещё раз.
Поиск находит заметку по её алиасу
- Что. Поиск на сайте и MCP-поиск теперь читают поле
aliases(иalias) из frontmatter. Если запрос целиком совпадает с названием заметки или одним из её алиасов, заметка получает прибавку вдвое весомее совпадения по тексту или по смыслу и обычно выходит на первое место. Регистр, лишние пробелы иё/ене важны. - Зачем. Раньше алиасы не участвовали в поиске вообще: заметку, которую все зовут «Starred», а называется она «Bookmarks», по слову «Starred» было не найти. На справке Obsidian запросы, совпадающие с алиасом, стали находить нужную страницу почти всегда (nDCG@5 0,81 → 0,98), а на остальных запросах выдача не изменилась.
- Как. Перечислите альтернативные названия в frontmatter, как в Obsidian:
aliases: [Starred, Избранное]. Новые URL алиасы не создают.
Образ MinIO заменён на Silo
- Что. Compose-файлы и инструкции по самостоятельному размещению теперь используют
pgsty/siloвместоminio/minio. - Зачем. MinIO прекратил открытую дистрибуцию и 11 сентября 2026 года удалил образы
minio/minioс Docker Hub.docker compose upпадает с ошибкойpull access denied for minio/minio. Silo: последний открытый сервер MinIO, который пересобирает и обновляет проект Pigsty. Тот же S3 API, те же переменныеMINIO_*, тот же формат данных на диске. - Как. В своём
docker-compose.ymlзаменитеimage: minio/minio:latestнаimage: pgsty/silo:latestи выполнитеdocker compose up -d. Том с данными не трогайте: Silo читает существующие данные как есть. Подробнее: Самостоятельное размещение.
Синхронизация больше не принимает заметку за шрифт или картинку
- Что. Сервер больше не отклоняет обычные заметки с ошибкой вида «Unsupported content type: application/vnd.ms-fontobject» (или
audio/mpeg,image/gif,text/xml). Теперь он проверяет только одно: содержимое должно быть текстом в UTF-8 без нулевых байтов. Бинарный файл под видом заметки по-прежнему не пройдёт, ошибка теперь звучит так: «File content must be UTF-8 text». - Зачем. Тип файла угадывался по первым байтам, как это делает браузер. Для текста такая проверка ошибается: заметка, у которой заглавные буквы
LPоказались на 35-м и 36-м байте (например, в заголовке «T HELPER» или «NLP»), считалась шрифтом EOT. Заметка, которая начинается с «ID3», считалась музыкой, а с «GIF89a» картинкой. Такая заметка не синхронизировалась, а по ошибке нельзя было понять, что с ней не так. - Как. Ничего делать не нужно. Если какая-то заметка раньше не синхронизировалась с такой ошибкой, запустите синхронизацию ещё раз.
Встраивания, превью и ссылки отображаются надёжно
- Что. Заметка, которая встраивает другую (
![[note]]), больше не выходит пустой, если у встроенной заметки нет текста, если встраивания замыкаются в кольцо или идут цепочкой на много уровней. О кольце теперь сообщает предупреждение «embed cycle» на заметке. Бесплатное превью (free_paragraphs,free_cut) сохраняет таблицы и callout'ы, каждый считается одним блоком. Вики-ссылки на адресаjavascript:,vbscript:,file:иdata:выводятся без адреса, как и обычные Markdown-ссылки. Встроенный твит теперь выводится стандартным способом X и собирается в браузере читателя, сервер его больше не скачивает. Виджеты и реклама Quail (quaily.com,quaily://) и чат-боты Dify (udify.app,dify://) больше не встраиваются: такой адрес в синтаксисе картинки выводится как обычная картинка. - Зачем. Страница могла потерять всё содержимое без единого предупреждения, таблица в начале заметки пропадала из превью, а встроенный твит заставлял каждую синхронизацию ждать ответа X. Кроме того, страницы, которые отрисовывались одновременно для разных посетителей, могли подменить друг другу ссылки на картинки в футере и карточках.
- Как. Ничего делать не нужно. Если на заметке появилось предупреждение «embed cycle», уберите одно из встраиваний, которые образуют кольцо.
noindex: true убирает страницу из поиска и из sitemap.xml
- Что. Новое свойство заметки
noindex. Заметка сnoindex: trueне попадает вsitemap.xml(ни в основной, ни в sitemap отдельного домена), в RSS-фиды и в списки.Public()в layout, и не содержит JSON-LD. Её страница получает<meta name="robots" content="noindex">в шаблоне по умолчанию и заголовокX-Robots-Tag: noindexв каждом ответе, в том числе со своим layout. - Зачем. Страница только для прямой ссылки (предложение, черновик, демо) могла вывести robots-тег из своего layout, но всё равно оставалась в
sitemap.xml, и её адрес видел любой, кто откроет sitemap. - Как. Добавьте
noindex: trueво frontmatter заметки или задайте его для папки frontmatter-патчем. Это не защита доступа: по ссылке страницу по-прежнему откроет кто угодно. Подробнее: SEO.
Закрытая страница так и говорит, что она закрыта, и предлагает вход вместо листа ожидания
- Что. Страница, к которой у посетителя нет доступа, теперь пишет «Эта страница закрыта», предлагает обратиться за доступом к владельцу базы знаний и сразу показывает форму входа. Блок листа ожидания (Telegram-бот и поле e-mail) по умолчанию выключен и появляется, только если включить новую настройку show_waitlists; как и раньше, он показывается только когда продавать нечего.
- Зачем. Поле e-mail от листа ожидания было единственным полем ввода на странице, а текст над ним просил авторизоваться, и посетители вводили туда свою почту для входа и ждали письмо, которого не будет. Большинство баз закрывают, чтобы они оставались приватными, а не чтобы потом продавать доступ.
- Как. Делать ничего не нужно: закрытые страницы получают новый текст и форму входа автоматически. Если вы всё-таки собираете контакты, пока предложений нет, включите show_waitlists в админке: System → Config.
Obsidian на телефоне: открыть заметку на сайте и закрыть вкладку с предупреждениями (плагин v0.11.0)
- Что. В меню «...» заметки появились пункты Открыть на сайте и Скопировать ссылку на сайт (только для опубликованных заметок) и команда Open published URL in browser. У вкладки с предупреждениями синхронизации появилась кнопка Close, а по кнопкам её тулбара теперь можно попасть пальцем.
- Зачем. Ни то ни другое на телефоне было недоступно. Индикатор с адресом живёт в статус-баре, которого в мобильном Obsidian нет, и до страницы заметки вела одна команда «Copy published URL»; а вкладку с предупреждениями приходилось закрывать через переключатель вкладок.
- Как. Обновите плагин до 0.11.0 или новее и нажмите
...на заметке → Открыть на сайте. Чтобы открывать в один тап, закрепите команду Open published URL in browser в Obsidian: Настройки → Панель инструментов (мобильная).
Цепочки доставки: трейс каждого шага вебхука через граф агентов
- Что. Каждая доставка вебхука теперь записывает, какая доставка её вызвала и к какой цепочке она принадлежит. В админке появился экран System → Цепочки доставок: список цепочек, а внутри шаги, у каждого видно, что он записал, какая запись его вызвала, его статус, стоимость и глубину в цепочке. Цепочка собирается через разные хосты агентов (на одном работают code-роли, на другом LLM-роли), и ни один из них про другой не знает.
- Зачем. Когда изменение запускало вебхук, который записывал заметку, которая запускала следующий вебхук, вся последовательность была невидима: отдельные записи доставки были, а связи между ними не было. Теперь весь путь распространения читается с одного экрана.
- Как. Откройте Админка → System → Цепочки доставок и выберите цепочку, чтобы посмотреть её шаги. Цепочки, которые ничего не записали, скрыты: расписание, не нашедшее работы, всё равно отрабатывает, и такие прогоны заваливают остальные. Включите Показывать пустые, чтобы их увидеть. Настраивать нечего: все доставки после обновления связываются сами.
Агенты сообщают стоимость прогона в своих единицах
- Что. Ответ вебхук-агента теперь несёт объект
costsвместо фиксированных полейtokens_usedиsteps:{"costs": {"tokens": 5186, "steps": 2}}. Единица живёт в ключе, поэтому агент, который платит деньгами, отдаёт{"usd": 0.004}, а считающий что-то своё отдаёт своё. Админка показывает то, что пришло: по каждому шагу и суммарно по единицам за всю цепочку. - Зачем. Токены и шаги: словарь одного исполнителя. Всему остальному (платному API, балансу кредитов, квоте очереди) сообщить о себе было нечем, а trip2g хранил понятие «токенов», знать которое ему незачем.
- Как. Возвращайте из агента
costsобъектом чисел. Каждое значение должно быть числом: строка или объект внутриcostsделают весь ответ нечитаемым для trip2g, и егоchangesтоже не применяются. Полейtokens_usedиstepsбольше нет: агент, который всё ещё их шлёт, просто не сообщает стоимость.
Идемпотентные записи больше не поднимают события изменений
- Что. Вызов
updateNotes, пуш синхронизации или изменение в ответе вебхук-агента, содержимое которых совпадает с уже сохранённым, не создают новую версию и не поднимают событие изменения. Доставка вебхука не ставится в очередь, SSE-событие не отправляется, повторное встраивание не запускается, обновление Telegram-вью не происходит. Изменение хотя бы одного байта по-прежнему срабатывает как обычно. Скрытие заметки работает как раньше и событий не поднимает. Запись в скрытый путь возвращает заметку (другого способа снять скрытие нет); черезupdateNotesили пуш синхронизации это считается изменением, и вернувшаяся заметка поднимает обычные события. - Зачем. Агент, повторно записывающий то же содержимое, ре-триггерил вебхук, который его вызвал, на каждом цикле, с полной стоимостью LLM, ограниченной только
max_depth. Исправление останавливает цикл на уровне записи: если ничего не изменилось, никому из downstream не нужно об этом знать. - Как. Автоматически. Агенты, возвращающие неизменённое содержимое, больше не порождают неконтролируемые цепочки. Защита
max_depthостаётся полезной для агентов, которые действительно вносят изменения.
Фильтр путей заметок по frontmatter
- Что.
notePathsпринимаетfilter: { frontmatter: [...] }. В каждом условии указанkeyи одно изexists: true,exists: falseилиequals: "<строка>". Условия должны выполняться все сразу и работают вместе сpaths,likeиsearch. Проверяется frontmatter последней версии заметки, уже после frontmatter-патчей.equalsсравнивает только строковые значения:equals: "5"не найдётcount: 5, аequals: "true"не найдётdraft: true. - Зачем. Чтобы найти все заметки с нужным свойством (например, все роли с
fleet_id: codellm), скрипту приходилось загружать заметки по одной и разбирать их frontmatter. - Как.
notePaths(filter: { frontmatter: [{ key: "fleet_id", equals: "codellm" }] }), с API-ключом вX-Api-Key, как и раньше. Сервер сохраняет frontmatter каждой версии заметки при загрузке. При старте он заполняет текущие последние и опубликованные (live) версии. Старые версии не обрабатываются, поэтому ключ, который есть только в старых версиях, в индекс не попадает.
Поиск заметки без результата в шаблоне даёт nil
- Что. В своём шаблоне
nvs.ByPath,nvs.ByPermalink,nvs.ByWikilink,.First()и.Last()у запроса,note.LangAlternative(...),PartialRenderer().Section(...),Section(...)у секции иFirstList()возвращаютnil, если ничего не нашлось. Результат теперь проверяют и{{ if x }}, и{{ if x == nil }}. - Зачем. При промахе возвращался пустой указатель конкретного типа.
{{ if x }}считал его пустым, аx == nilдавал false, поэтому шаблон с проверкойx == nilшёл дальше и работал с заметкой, которой нет. - Как. Ничего делать не нужно: шаблоны с
{{ if x }}работают как раньше. Чтобы найти и проверить в одном теге, пишите{{ if about := nvs.ByPermalink("/about"); about }}. См. Шаблоны.
Шаблоны с try/catch, exec, range _ и дефисом в папке загружаются
- Что. Шаблон с
{{ try }}…{{ catch err }}…{{ end }}, с{{ return }}в файле, который вызывают черезexec("путь", data), или с{{ range _, x := list }}теперь загружается и рендерится. Страница, которая начинается с{{ extends "..." }}, получает автоимпорт компонентов.@lidтеперь заменяет на_каждый символ, недопустимый в имени блока Jet (-,., пробел), и ставит_перед цифрой в начале:my-theme/card.htmlдаётmy_theme_card,2col/card.htmlдаёт_2col_card. - Зачем. Обход шаблона в загрузчике останавливался на этих конструкциях, и шаблон не загружался с ошибкой разбора. В странице с
extendsавтоимпорт вставлял компоненты над строкойextends, где Jet их не принимает. Компонент в папке вродеmy-theme/получал@lidс дефисом, а это недопустимое имя блока. - Как. Ничего делать не нужно. Префиксы
yield_blocksследуют форме@lid:yield_blocks("_style_my_theme_"). См. Синтаксис Jet и yield_blocks.
Стандартный шаблон: блоки кода, широкие таблицы и боковое меню
- Что. Блок кода с пометкой
jetподсвечивается как шаблон Jet. Кнопка копирования больше не уезжает вместе с длинной строкой; её подпись зависит отlangстраницы (русская дляru, иначе английская), а «Скопировано» озвучивается для экранных дикторов. Блок на языке, который подсветка не знает, показывается простым текстом. Широкая таблица прокручивается в своей рамке и не растягивает страницу. От 1024px левое меню прокручивается отдельно, открывается сразу на текущей странице и выделяет её цветным фоном. - Зачем. Кнопка копирования сидела внутри прокручиваемого кода и уезжала вместе с ним. Широкая таблица сдвигала вбок всю страницу. В длинном меню текущая страница могла оказаться за краем, а её отметку было плохо видно.
- Как. Ничего делать не нужно.
Больше интерфейса на языке читателя
- Что. Страницы 404 и 500 стандартного шаблона, подпись «Read in:» у переключателя языка, подписи кнопок меню, alt у логотипа и текст «пока не поддерживается» для файлов Canvas, Excalidraw и Bases теперь следуют языку интерфейса. На русском переведены и кнопка «Редактировать страницу», поиск файлов, список сохранения и сравнение версий в редакторе, переключатели автообновления и слежения за редактором, кнопка смены темы.
- Зачем. Эти строки были зашиты по-английски, или их русский перевод лежал там, где сборка фронтенда его не находила.
- Как. Ничего делать не нужно. Язык интерфейса берётся из языковой cookie или из
Accept-Languageбраузера.
Читатели больше не скачивают редактор страниц
- Что. Редактор страниц теперь отдельный бандл,
/assets/ui/editor/pane/-/web.js. Его загружает только фрейм редактора, когда админ открывает редактор; в бандле, который скачивает каждый читатель, редактора больше нет. - Зачем. Редактор входил в публичный бандл, и каждый читатель скачивал код, который запускает только админ.
- Как. С бинарниками из релиза и Docker-образом ничего делать не нужно. Свой шаблон, который выводит
{{ defaultTemplate.UserSpaceScripts() }}, передаёт адрес редактора сам. Если вы собираете фронтенд сами, соберите иtrip2g/editor/pane(npm start trip2g/editor/pane), как теперь делает Dockerfile.
Своя надпись на кнопке SSO и выключение входа по email
- Что. У кнопки входа через OIDC может быть своя надпись вместо «Войти через SSO»:
OIDC_DISPLAY_NAMEдля провайдера из переменных окружения илиdisplayNameвcreateOIDCCredentialsдля провайдера, созданного через API. Вход по коду на email можно выключить:DISABLE_EMAIL_SIGNIN=trueв окружении или переключательemail_signin_enabledв Админке → System → Config. Когда он выключен, экран входа прячет поле email, его кнопку и капчу, аrequestEmailSignInCodeиsignInByEmailотвечают ошибкойemail_sign_in_disabled. Новое в GraphQL:Query.emailSignInEnabled,OAuthUrlPayload.label,AdminOIDCCredentials.displayName. - Зачем. «SSO» называет протокол, а не место, куда попадёт читатель. В инстансе, где все аккаунты приходят от провайдера, форма email предлагала запросить код для аккаунта, которого нет.
- Как. Переменная окружения сильнее: пока задан
DISABLE_EMAIL_SIGNIN, переключатель в админке не включит вход по email обратно. Прежде чем выключать email, убедитесь, что провайдер работает, иначе войти не сможет никто, включая вас. Пустая надпись оставляет текст по умолчанию. Подробнее: OIDC и OAuth.
После входа читатель возвращается на свою страницу, а редиректы не уводят с сайта
- Что. После входа через Google, GitHub или OIDC читатель возвращается на страницу, где нажал кнопку, а не на главную. Редиректы входа теперь отдают относительный
Location, поэтому за прокси, который снимает TLS, они больше не переводят браузер с https на http. Любой адрес редиректа, собранный из запроса (адрес возврата после OAuth и чистый URL после входа по ссылке?hat=из оплаты), разбирается и собирается заново как путь на этом сайте; всё, что называет другой хост, протокольно-относительный//host,/\hostили URL с управляющими символами, превращается в/. - Зачем. Кнопка отправляла полный URL страницы, а сервер отбрасывал любой абсолютный URL, поэтому каждый вход заканчивался на
/. За TLS-прокси редирект вёл наhttp://, браузер не отправлял только что полученнуюSecure-куку, и удачный вход выглядел как неудачный. Редирект после?hat=копировал строку запроса как есть, и запрос с абсолютным URL мог увести браузер на чужой сайт. - Как. Ничего делать не нужно.
Обратные ссылки в GraphQL учитывают права чтения
- Что.
NoteView.inLinksпоказывает только те ссылающиеся заметки, которые вызывающий может читать. - Зачем. Поле отдавало все заметки, которые ссылаются на текущую, вместе с их
contentиhtml, в том числе заметки из подграфов, к которым у вызывающего нет доступа. - Как. Ничего делать не нужно.
Векторный поиск видит новые заметки без перезапуска
- Что. Эмбеддинги, посчитанные после синхронизации, теперь попадают в векторный поиск и в похожие заметки, пока сервер работает. Сервер перечитывает эмбеддинги и чанки из базы после того, как задачи эмбеддинга завершились: не чаще раза в
EMBEDDING_RELOAD_INTERVAL(по умолчанию5s), пока задачи продолжают завершаться, и ещё раз после последней. Заметки при этом не перерисовываются. - Зачем. Эмбеддинги загружались в память вместе с заметками, раньше, чем фоновые задачи успевали их записать, поэтому заметка, синхронизированная после старта, не находилась векторным поиском до следующего перезапуска.
- Как. Ничего делать не нужно. Во время долгой заливки поиск улучшается по мере работы задач, а не после её окончания.
MCP: expand читает лист, показывает только края длинной заметки, а ошибки федерации называют базы
- Что.
expandна разделе без подразделов возвращает сам раздел: тот же текст, чтоnote_htmlдля этогоtoc_path, иsection_htmlв структурированном ответе. Новые аргументыfirstиlastуexpandиfederated_expandпоказывают только N самых старых и N самых новых подразделов; сводка говорит, что список неполный («newest 30 of 365 subsection(s)», между краями строка «… 330 subsection(s) not listed …»), а в ответе естьtotal_childrenиomitted. Ответfederation_not_configuredтеперь перечисляет базы, к которым агент может обратиться (connected_kb_ids), а если промах сообщил пир, то и базы, подключённые к этому хабу напрямую. Результаты поиска и заметки, которые указывают на другую базу, показывают еёkb_idв тексте. - Зачем. Чтобы прочитать лист, агенту приходилось вызывать
note_htmlс теми же аргументами. Заметка, в которую каждый день добавляется раздел с датой, отдавала список в десятки тысяч символов тому, кому нужна последняя запись. Ошибка «not configured» не называла ни одного верногоkb_id, и агент не мог исправиться сам. - Как.
{ "name": "expand", "arguments": { "path": "log.md", "first": 5, "last": 30 } }. Если края сходятся или перекрываются, возвращается весь список. Подробнее: expand.
База знаний в одном контейнере
- Что.
Dockerfile.docsсобирает образ, в который зашито хранилище.docker runзапускает trip2g, один раз заливает в него хранилище клиентом синхронизации из onboarding-архива самого инстанса и продолжает работать. Ни тома, ни входа, ни настройки: база и секреты создаются внутри контейнера при старте. Внутренний адрес слушает только127.0.0.1. По умолчанию в образе эта документация. - Зачем. Чтобы раздать базу знаний только для чтения (команде или MCP-клиенту), нужен был работающий инстанс и отдельный шаг синхронизации.
- Как.
docker build -f Dockerfile.docs -t trip2g-docs ., затемdocker run --rm -p 8080:8080 trip2g-docs. Замените строкиCOPYсвоей папкой, а публичный адрес передайте через-e PUBLIC_URL=.... Подробнее: База знаний в одном контейнере.
Fleet: пауза для роли и более надёжный запуск кода
- Что.
- Заметка роли с
enabled: falseуходит из реестра при следующем опросе, её вебхуки удаляются, и cron-роль перестаёт срабатывать; сама заметка остаётся.--dry-runпоказываетSTATUS: DISABLED (enabled: false). Значение, отличное от true/false (принимаются такжеyes/no,on/off,1/0), считается ошибкой разбора. - Повторный вызов codellm больше не запускает код второй раз: fleet отправляет один
Idempotency-Keyна вызов и повторяет его при каждой попытке, а codellm 10 минут отвечает на повтор сохранённым ответом. Повторы считаетcodellm_exec_replays_total. - Итоговый stdout сверх лимита теперь роняет запуск с ошибкой «stdout limit exceeded» (HTTP 422 от
/v1/chat/completions), а не отдаёт обрезанное начало. Лимит по умолчанию 10 MiB (было 1 MiB), задаётся черезCODELLM_MAX_STDOUT_BYTESили--max-stdout-bytes. - Записи, которые вернул инструмент
exec, проверяются одной пачкой до того, как применится хоть одна: путь вне области записи отбрасывается и называется в результате инструмента, а патч, чейfindне найден или найден больше одного раза, отклоняет всю пачку. - codellm отказывается распечатывать роль, чей
unseal_env_keyпопадает подCODELLM_EXPOSE_ENV_PREFIX, как уже отказывался для точных имён изCODELLM_EXPOSE_ENV. - Системный промпт агента начинается с неизменной части, а инструкция конкретной доставки идёт в конце, чтобы кеш промптов у провайдера мог его переиспользовать; в
fleet_llm_tokens_totalпоявилсяkind="cached"(частьprompt, а не дополнительные траты).
- Заметка роли с
- Зачем. Cron-выражение, которое никогда не наступит, не надёжная пауза. Сетевой повтор мог второй раз выполнить код с побочными эффектами. Обрезанный stdout выглядел как результат. Пачка могла закончиться наполовину применёнными записями и сообщением об успехе для модели. Список префиксов мог отдать ключ печати тому самому коду, от которого он защищает.
- Как. Чтобы поставить роль на паузу, добавьте
enabled: falseв её frontmatter; чтобы снять, уберите строку. Подробнее: Fleet и секреты codellm.
Админ может выдать пользователю ссылку для входа
- Что. На странице пользователя в админке появилась кнопка Sign-in link, а в admin GraphQL мутация
createHatLink(input: { email, redirectUrl, expiresInMinutes }). Она возвращает ссылку/_system/hat?token=..., по которой человек входит в один клик. Ссылка живёт 5 минут по умолчанию и не больше 60,redirectUrlдолжен быть путём на том же сайте. Ссылка впускает только существующего пользователя: аккаунт она не создаёт и прав админа не даёт. Нерабочая ссылка теперь открывает короткую страницу с тем, что делать («Ссылка устарела», «Ссылка не работает», «Аккаунт не найден»), а не ошибку. - Зачем. Впустить человека без письма можно было только через CLI сервера с его секретом подписи. А при сбое посетитель видел сырую ошибку, с которой нечего делать.
- Как. Админка → Пользователи → пользователь → Sign-in link, скопируйте ссылку и отправьте. Запрос попадает в журнал аудита, сама ссылка нет.
Личные токены для других пользователей
- Что. Админ может выпустить личный токен (
t2g_...) любому пользователю черезadminCreateUserToken(input: { userId, name, expiresInDays })(срок от 1 до 365 дней, необязательно) и отозвать любой токен по id черезadminRevokeUserToken. Раздел Админка → Пользователи → Личные токены показывает все токены инстанса; ссылка Personal tokens на странице пользователя открывает его с фильтром по этому пользователю и заполненной формой нового токена. УCreateUserTokenPayloadновое полеinstructions: готовый текст с адресом MCP и самим токеном, который можно передать тому, кто будет им пользоваться. В окне аккаунта у токенов появились свои экраны, а главная аккаунта показывает email, под которым вы вошли, и избранные заметки. - Зачем. Токен мог создать только сам пользователь для себя, и чтобы дать доступ агенту, приходилось сначала входить от имени этого человека.
- Как. Админка → Пользователи → пользователь → Personal tokens → новый токен. Скопируйте блок инструкций один раз: токен уже внутри. См. MCP.
Закрытые страницы отвечают 401 или 403 и не выдают описание
- Что. Страница за стеной входа или пейволлом теперь отвечает
401посетителю без входа и403вошедшему, у которого нет доступа, в обоих случаях сCache-Control: no-store. Тело страницы прежнее. Закрытая заметка также больше не получает автоматический<meta name="description">из первого абзаца; используется только явныйdescriptionво frontmatter. - Зачем. Стены отдавались с
200, и поисковики, превью ссылок и API-клиенты принимали их за настоящую страницу. Запасное описание строилось из текста заметки и печаталось в<head>самой стены, так что первые строки закрытой заметки можно было прочитать без доступа. - Как. Ничего делать не нужно. Чтобы у закрытой страницы было описание в поиске, задайте
descriptionво frontmatter.
Логотип сайта грузится у анонимных посетителей
- Что. Картинки из
_header.mdи_footer.md(в любой папке) отдаются посетителям без входа, есть у этих заметокfreeили нет. Ассеты других системных заметок подчиняются тому же правилу, что у обычной заметки: они анонимные, если заметку можно прочитать без сессии. - Зачем. Логотип сайта отвечал
401каждому анонимному посетителю: ассет считался публичным, только если заметку-владельца можно показать в публичных списках, а системные заметки вроде_header.mdтуда не попадают никогда. Админ всегда залогинен и проблемы не видел. - Как. Ничего делать не нужно. Шапка, подключённая под другим именем через поле
header:во frontmatter, по-прежнему требуетfree: true, чтобы её картинки были публичными.
Ссылки оставляют читателя на том хосте, куда он пришёл
- Что. На своём домене шапка, подвал, сайдбар, обратные ссылки и запросы в шаблонах теперь строят ссылки так же, как тело страницы: ссылка на заметку того же хоста относительная, а не абсолютный URL. Заметка, открытая по своему
routeиз frontmatter, отдаётся по этому пути, без редиректа на постоянный адрес. - Зачем. Общая обвязка рендерилась один раз, для основного домена. На своём домене её ссылки вели на основной домен или на абсолютный адрес, который не открывается на внутреннем или preview-хосте. Редирект с
routeзависел от того, есть ли в имени файла символы, которые транслитерируются, а на своём домене вёл на 404. - Как. Ничего делать не нужно.
rel=canonical,og:urlи JSON-LD остаются абсолютными.
MCP: стандартный транспорт, аргументы федерации и исправления поиска
- Что. Эндпоинт MCP
/_system/mcpтеперь работает на официальном Go MCP SDK (Streamable HTTP, без состояния, ответы в JSON). Имена, описания и схемы инструментов не менялись. Вместе с этим:- Федеративные вызовы передают пиру все аргументы.
federated_searchраньше отправлял только запрос, иlimit,detail_limitи подобные терялись;federated_note_htmlтеперь принимаетtoc_path. Вызов к одномуkb_idполучил тот же таймаут, что и рассылка по всем. - API-ключ видит KB-заметки (
mcp_federation_kb_url) безfree, как и все остальные заметки. Раньше вызовыfederated_*к такой базе отвечали «не настроено». - Фрагменты поиска больше не начинаются с битого символа в кириллице и других нелатинских текстах.
expandне показывает H1 с заголовком заметки и выводит короткое превью после слишком коротких заголовков.toc_pathизsearchобрезается так же.- Заметки, зарегистрированные как инструменты (
mcp_method), больше не попадают в результаты поиска. - У каждого совпадения в поиске есть
section_url, ссылка на найденный заголовок на сайте.
- Федеративные вызовы передают пиру все аргументы.
- Зачем. Самописный слой протокола расходился со спецификацией, а федеративный агент получал не тот результат, что тот же вызов локально.
- Миграция. Запрос должен содержать
Accept: application/json, text/event-stream, без него эндпоинт отвечает400. Настоящие MCP-клиенты его шлют. Обновите curl-скрипты и свои интеграции (stdio-адаптер в документации обновлён). Инстанс старой версии, который обращается к этому через федерацию, заголовок не шлёт, поэтому обновите обе стороны. Другие заметные изменения:notifications/initializedотвечает202с пустым телом,tools/listотсортирован по имени. См. MCP.
Реранкинг включается для каждого поиска отдельно
- Что. Если реранкер настроен, каждый поиск может его запросить: аргумент
rerankу MCP-инструментовsearchиfederated_searchи полеrerankв GraphQLSearchInput. Новый ключvector_search.reranker.defaultвFEATURESрешает, что делать, когда запрос ничего не указал; по умолчаниюfalse. Аргумент MCP показывается, только когда реранкер настроен. - Зачем. Кросс-энкодер на CPU тратит около секунды на кандидата, и поиск с
top_n20 шёл около 20 секунд. Агенту, который что-то исследует, это терпимо, человеку у строки поиска нет. - Миграция.
reranker.enabled: trueбольше не реранжирует каждый поиск. Чтобы вернуть прежнее поведение, добавьте"default": trueв объектreranker. Это касается иmemcli up --reranker.
Полнотекстовый индекс может жить на диске
- Что. Новая настройка
SEARCH_INDEX_PATH(--search-index-path) хранит полнотекстовый индекс в этой папке, а не в памяти. После перезапуска сервер открывает готовый индекс, а не строит его заново, а заметки, удалённые, пока сервер стоял, убираются из индекса при следующей загрузке. Пустое значение оставляет индекс в памяти, как раньше. - Зачем. Индекс в памяти занимает примерно в 35 раз больше самого текста. На базе с 10 МБ markdown он держал 350 МБ кучи, и большие базы падали по памяти при перезапуске. На диске та же база занимает 4 МБ кучи и около 45 МБ диска, а после перезапуска индекс открывается за миллисекунды.
- Как. Укажите в
SEARCH_INDEX_PATHпапку на постоянном томе. Не направляйте два инстанса в одну папку.
Федерация: один ключ передачи, который меняется при установке
- Что. При добавлении входящего секрета теперь выдаётся один ключ передачи, в котором уже есть адрес и ID ключа; другая сторона вставляет его в Add Outbound. Прежде чем сохранить запись, принимающий хаб просит выдавший пир принять свежий случайный ключ, и значение, прошедшее через чат, перестаёт работать. На странице исходящего ключа есть кнопка Ротировать ключ (то же через
rotateFederationSecret), видно, когда ключ меняли последний раз, и что пир выдал, по ответу его нового эндпоинта/_system/mcp/federation. У подграфа появилось описание в одну строку, которое пир видит рядом с именем. Список в админке показывает направление каждого ключа. - Зачем. Общий ключ, переданный сообщением, остаётся в истории переписки, а если его передавал агент, то и в его логе. И оператор не мог увидеть, что пара ключей на самом деле открывает.
- Как. Add Inbound → скопируйте ключ передачи → отправьте → другая сторона вставляет его в Add Outbound. Переключатель Заменить ключ перед сохранением включён по умолчанию; выключите его для пира, который не умеет ротацию (публичная база, адаптер, инстанс старой версии), иначе пир откажет и ничего не сохранится. Ротация с пиром по
http://запрещена, если не разрешены приватные адреса федерации. См. Федерация.
Вебхук-агенты: лог прогона, долгая история, приватные адреса
- Что. Вебхук-агент может вернуть
logs, список записей{ts, level, msg, data}, и админка показывает их на шаге цепочки в разделе Log. На одну доставку хранится до 500 записей или 64 КБ; остальное заменяется одной записью о том, сколько отброшено. Записи доставок и тела запросов и ответов теперь хранятся 90 дней, срок задают--webhook-deliveries-retentionи--webhook-delivery-logs-retention. Новый флаг--webhook-allow-private(WEBHOOK_ALLOW_PRIVATE) пускает вебхуки на приватные и внутренние адреса без режима разработки. Заметки, которые агент вернул в теле ответа, теперь запускают вебхуки изменений, SSE и индексацию frontmatter, как любая другая запись, и считаются на уровень глубже в цепочке. - Зачем. Оператор видел, что прогон упал, но не видел, что агент делал. Записи исчезали через 30 дней, а тела через сутки, раньше, чем до них доходили руки. Агентов в приватной сети рядом с trip2g блокировала защита от SSRF. Заметки, записанные через тело ответа, не запускали следующую роль в цепочке.
- Как. Возвращайте
logsрядом сchangesиcosts. Тела теперь хранятся в 90 раз дольше, поэтому заложите место на диске или уменьшите срок.
Fleet входит по личному токену, а агенты не могут писать заметки ролей
- Что. Fleet аутентифицируется личным токеном админа (
--trip2g-admin-personal-token,TRIP2G_FLEET_TRIP2G_ADMIN_PERSONAL_TOKEN), а не JWT-секретом сервера. Задайте на сервереOWNER_PERSONAL_TOKEN_VALUEсо значениемt2g_..., и при запуске он заведёт этот токен владельцу (OWNER_EMAIL);memcli upгенерирует его сам. Fleet отклоняет запись или патч от агента, если в результате появится или изменится заметка роли (любая заметка сfleet_idво frontmatter), а патч применяется, только если заметка не изменилась после этой проверки.--allow-role-authoringотключает защиту. Fleet и codellm также отдают Prometheus/metrics, pprof и проверки здоровья на loopback-адресе:--metrics-addr(fleet127.0.0.1:18090, codellm127.0.0.1:18087,CODELLM_METRICS_ADDR); пустое значение отключает. - Зачем. С JWT-секретом fleet мог выпустить сессию кому угодно, и отключить его можно было только сменой секрета. Роль сама объявляет свои
write_patterns, поэтому агент, которого текст заметки уговорил записать заметку роли, мог выдать себе больше прав. - Миграция. У fleet больше нет
--jwt-secret,--admin-emailи--admin-api-key. ЗадайтеOWNER_PERSONAL_TOKEN_VALUEна сервере и передайте то же значение fleet. Если отозвать этот токен в админке, fleet остановится примерно через полминуты. См. Fleet.
Code-роли: набор инструментов, fleetkit и зашифрованные секреты
- Что. Образ codellm теперь на Debian, с Python 3, Node 24 и привычными утилитами (
jq,sqlite3,git,gh,ripgrep,curlи другие) и библиотеками вродеrequests,httpx,pydantic,jsonschema,axios,zodиajv. Помощникfleetkitдля Python и Node собирает вывод code-роли (note,write,patch,emit) и читает её вход (bag,frontmatter,secrets,note_frontmatter). Заметка роли может хранить зашифрованные значения: перечислите их вunseal, и codellm откроет их своимSEAL_KEY(или ключом изunseal_env_key) только на этот прогон. Зашифровать значение можно командойcodellm sealили формой/_system/codellm/seal(CODELLM_SEAL_PATH). Роль может сузить набор переменных окружения для своего кода полямиenv_passthroughиenv_prefix, в пределах списка оператора. - Зачем. У code-ролей был голый Python без библиотек, заметки и JSON собирались вручную, а каждый новый секрет означал правку деплоя codellm и перезапуск.
- Как. См. Fleet и Секреты для code-ролей.
Переэмбеддинг всех заметок из админки и ссылка на заголовок
- Что. На странице Просмотры заметок в админке есть кнопка Переэмбедить все заметки. Она вызывает
regenerateNoteEmbeddings(input: { force: true }), который ставит в очередь каждую заметку, даже если её сохранённый хеш говорит, что всё актуально; фрагменты, чей собственный хеш не изменился, модели повторно не отправляются. В стандартном шаблоне при наведении на заголовок рядом появляется ссылка#. - Зачем. После изменения разбиения на фрагменты все заметки по-прежнему выглядели актуальными, и новые фрагменты не попадали в векторный индекс. А чтобы сослаться на раздел, приходилось искать его id в оглавлении.
- Как. Админка → Просмотры заметок → Переэмбедить все заметки. Нужен включённый векторный поиск.
CLI trip2g-sync: команда graphql, а сбой загрузки останавливает синхронизацию
- Что.
trip2g-sync graphql '<query>' ['<variables>']выполняет GraphQL-запрос к инстансу, на который настроено хранилище, с ключом из.obsidian/plugins/trip2g/data.json;graphql --introspect '<pattern>'показывает подходящие типы схемы. Запросы идут через MCP, где API-ключ несёт свои админские права. Если CLI не смог загрузить список заметок с сервера, он теперь останавливается с ошибкой и кодом выхода 1. Голые ссылки на файлы вроде![[logo.png]]находят файл в любом месте хранилища (побеждает самый неглубокий), в CLI и в браузерной синхронизации. Скачанное онбординг-хранилище теперь содержитAGENTS.mdиCLAUDE.mdдля ИИ-агентов, которые в нём работают. CLItrip2g-sync(плагин 0.12.0) пишет, что произойдёт с каждой непустой строкой плана: например, что файлы из «Remote only» скачиваются как новые, а заметки из «Local deleted» скрываются на сервере. В справке теперь сказано, что удаление файла локально скрывает заметку на сервере без всяких флагов, а--two-wayскачивает заметки, которые есть только на сервере. - Зачем. Сбой загрузки читался как пустой сервер: прогон сообщал, что все локальные заметки удалены на сервере, и завершался с кодом 0. Ссылка на файл без папки ничего не находила, если файл не лежал рядом с заметкой.
- Как. Запускайте из папки хранилища, например
node .obsidian/plugins/trip2g/trip2g-sync.mjs graphql '{ viewer { id } }'. Эти изменения CLI выходят с плагином и CLI 0.12.0.
Файлы из заметок отдаёт сам сайт, с проверкой доступа
- Что. Картинки и другие файлы из заметок и шаблонов отдаются по адресу
/_system/assets/<sha256>/<имя файла>. Раньше это были подписанные ссылки на сервер хранилища (MINIO_PUBLIC_URL) или, при локальном хранилище,/_assets/.... Файл, который использует шаблон или заметка, открытая всем, публичный и кешируется на год как неизменяемый. Для остальных файлов нужен вошедший читатель, который может прочитать хотя бы одну заметку с этим файлом, действующий API-ключ вX-API-Keyили админ. Такие файлы кешируются только в браузере на 5 минут, потом доступ проверяется снова. Файл, который не использует ни одна текущая заметка или шаблон, доступен только админу. Работают запросы сRangeиETag. Адреса, которые уходят за пределы сайта (посты в Telegram,og:image, JSON-LD, RSS, адреса файлов в ответеpushNotes), дополняются публичным адресом сайта. - Зачем. Подписанная ссылка истекала через несколько дней и открывалась у любого, кому её передали, даже если файл из платной или закрытой заметки. Кроме того, сервер хранилища должен был быть доступен из браузера читателя.
- Как. С контентом ничего делать не нужно: страницы получают новые адреса, как только сервер запущен в новой версии.
MINIO_PUBLIC_URLиMINIO_URL_EXPIRES_INбольше не используются, публичный адрес хранилищу не нужен. Старые ссылки, скопированные со страниц до обновления, перестанут работать.
Формы: проверка доступа к заметке, капча без секрета не пропускает, forms: работает
- Что.
submitFormсначала проверяет, может ли посетитель прочитать заметку с формой. Если нет, ответform_not_found, как для несуществующей формы. Каждое текстовое поле и поле email ограничено 8192 символами, аmin_length/max_lengthтеперь считают символы, а не байты. Если заданturnstile-site-key, аturnstile-secret-keyпуст, проверка Turnstile теперь не проходит, а не пропускает всё подряд; это касается и капчи при входе по email. Несколько форм на одной заметке через ключforms:теперь работают. - Зачем. Номер версии заметки в отправке легко подобрать, и гость мог отправить форму с платной заметки, заметки только для вошедших или из подграфа. Наполовину настроенный Turnstile выглядел как защита, но ничего не проверял. Размер поля ограничивал только лимит тела запроса.
forms:не находился в настоящем frontmatter: YAML-парсер возвращает ключи в типе, которого код не ждал. - Как. Ничего делать не нужно. Если задан только ключ сайта Turnstile, добавьте секретный ключ или уберите оба, чтобы отключить капчу. У текста на кириллице, который был близок к
max_length, теперь больше места: лимит больше не считает байты. См. Формы.
Без SMTP коды входа пишутся в лог сервера
- Что. Если
SMTP_HOSTпуст, запрос кода для входа по email снова работает. Код пишется в лог сервера с уровнем warning вместе с адресом.LOG_SIGN_IN_CODES=trueпо-прежнему включает запись кодов и при настроенном SMTP. - Зачем. В прошлом выпуске запрос кода без SMTP возвращал ошибку, если не задан
LOG_SIGN_IN_CODES, и вход по email не работал на dev-стендах и свежих установках. - Как. На публичном сервере настройте SMTP: любой, кто читает лог сервера, может войти за любого пользователя, запросившего код. См. Отправка писем (SMTP).
Упавший шаблон отдаёт страницу ошибки
- Что. Свой шаблон сначала рендерится в буфер. Если рендер падает, читатель не получает из него ничего. Ответ: статус 500 и страница ошибки стандартного шаблона. Админ видит на ней имя шаблона и ошибку Jet со строкой. Остальные видят общую ошибку.
- Зачем. Jet успевает вывести всё до упавшего выражения. Посетитель мог получить половину страницы, а админ видел текст ошибки, приклеенный к этой половине.
- Как. Ничего делать не нужно. Чтобы увидеть, что сломалось, откройте страницу под админом.
Предупреждения шаблонов доходят до утилиты синхронизации, критические её останавливают
- Что.
pushNotesтеперь возвращает предупреждения каждого файла шаблона, раньше вместо них был пустой список. CLI-утилита синхронизации (trip2g-sync.mjs) выводит предупреждения по файлам,CRITICALкрасным, и завершается с кодом 1, если есть хоть одноCRITICAL(например, шаблон, который не разбирается). Новое предупреждениеWARNINGнаходит шаблон, где его собственное раскрытое имя@lidнаписано текстом (mesh-barвmesh/bar.html): при переименовании файла там останется старое имя, а плейсхолдер переименовался бы вместе с файлом. Совпадения в HTML-комментариях и URL не считаются. - Зачем. Сломанный шаблон отправлялся молча, и сайт узнавал об этом при рендере.
- Как. Обновите CLI и запускайте как раньше. В CI критическое предупреждение шаблона теперь роняет задачу. См. CLI для синхронизации.
coalesce() в шаблонах
- Что. Новая глобальная функция для шаблонов Jet:
coalesce(a, b, ...)возвращает первый аргумент, который задан и не пуст, иначе последний. Пустыми считаются отсутствующий ключ карты,nil,"", пустой список и пустая карта;0иfalseсчитаются значениями. - Зачем.
||в Jet возвращает булево значение, поэтомуvideos[lang] || videos["en"]не мог выбрать значение по умолчанию. - Как.
{{ coalesce(videos[note.Lang()], videos["en"]) }}. См. Функции Jet.
Диаграммы Mermaid: второй движок, масштаб, полный экран и экспорт
- Что. Блок-схемы, диаграммы состояний, последовательностей, классов, ER и xychart рисует beautiful-mermaid. Остальные типы (gantt, pie, gitGraph, mindmap и другие) и любая диаграмма, которую он не разобрал, идут через mermaid.js, как раньше. У каждой диаграммы есть кнопки увеличения, уменьшения, сброса, полного экрана и экспорта в PNG / SVG, а саму диаграмму можно перетаскивать. Диаграмма занимает не больше 80% высоты экрана и вписывается в свою рамку.
- Зачем. Перетаскивание диаграммы никогда не работало. Большие диаграммы сжимались до нечитаемого размера, а широкие и низкие превращались в узкую полоску.
- Как. Ничего делать не нужно. См. Mermaid.
Сетка карточек: сколько карточек крупных, средних и мелких
- Что. Три ключа frontmatter на странице с сеткой:
magazine_featured(крупные карточки, по умолчанию1),magazine_grid(средние карточки после них, по умолчанию4) иmagazine_grid_columns(колонки в сетке средних карточек, по умолчанию2). Остальные заметки идут списком.0убирает уровень, а число больше количества заметок просто урезается. Кроме того, в отрывок на карточке больше не попадают таблицы. - Зачем. Раскладка была жёсткой: одна крупная карточка, четыре средних и список. Таблица в начале заметки ломала её карточку.
- Как. Например,
magazine_featured: 0иmagazine_grid: 8дают сетку из восьми карточек без крупной. Значения по умолчанию сохраняют прежнюю раскладку. См. Стандартный шаблон.
Виджеты в списке content:
- Что. Список
content:стандартного шаблона теперь понимаетtoc,backlinks(илиinlinks),outlinksиsimilar, те же слова, что и боковые панели. Каждое выводит свой блок в основной колонке. - Зачем. Шаблон уже умел рисовать эти блоки в основной колонке, но
content:принимал эти слова за имена файлов и ничего не показывал. - Как. Например,
content: [self, backlinks, similar]выводит обратные ссылки и похожие заметки под статьёй.
Выноски в постах Telegram
- Что. Выноски Obsidian (
> [!note] Заголовок) в посте Telegram становятся цитатой с жирным заголовком в первой строке. Выноска без заголовка получает тип с заглавной буквы. Свёрнутая выноска ([!type]-) становится раскрывающейся цитатой, где до раскрытия виден только заголовок. - Зачем. Конвертер для Telegram не знал выносок: он выбрасывал весь блок и писал в лог
unexpected markdown node. - Как. Ничего делать не нужно: пишите выноски, как в Obsidian.
Токен вебхука не выходит за свои write_patterns
- Что. Временный токен, который вебхук передаёт агенту (Pass API key), теперь ограничен своими
write_patternsвезде, где он может писать.uploadNoteAssetпроверяет, что заметка лежит по пути, куда токену можно писать.pushNotes,hideNotesиcommitNotesотказывают такому токену сErrorPayload: у них нет ограничения по путям. ВupdateNotesтокен безwrite_patternsбольше никуда не может писать. - Зачем. Через эти вызовы токен мог писать за пределами своих
write_patterns: прикреплять файлы к любой заметке, отправлять или скрывать любую заметку, а в некоторых случаях писать куда угодно черезupdateNotes, еслиwrite_patternsпуст. - Как. Агентам, которые пишут через
updateNotesв своих путях, ничего делать не нужно. Агент, который вызывалpushNotes,hideNotesилиcommitNotesс токеном вебхука, должен перейти наupdateNotes(заметку скрывает изменениеhide). См. Вебхуки при изменении заметок.
Граф заметок: группы, сохранённые позиции и читатели в реальном времени
- Что. В шапке графа заметок в админке (Notes & Content → Note Graph) есть Auto layout, Save positions, Public outside и поле Group by:. Заметки с одинаковым значением этого ключа frontmatter (по умолчанию
subgraph) собираются в одно скопление. Перетащенная заметка сохраняет позицию. Пока граф открыт, переходы вошедших читателей между заметками показываются анимацией. Те же события доступны в GraphQL-подпискеreaderMovesтолько для админа (сессия админа или API-ключ инстанса). Читатель виден под анонимным ключом, который меняется каждый час, а не под id пользователя. Пока никто не подписан, сервер не делает для этого никакой работы. - Зачем. Заметки располагались случайно, раскладка терялась при каждой перезагрузке, и не было видно, как люди ходят по сайту.
- Как. Откройте граф, впишите ключ в Group by: и нажмите Auto layout. Чтобы сгруппировать целые папки, добавьте frontmatter-патч. См. Граф заметок.
Флит: роль выбирает флит по fleet_id, код выполняет codellm
- Что. Роль выбирает флит, который её выполняет, ключом frontmatter
fleet_id. Флаг флита--fleet-id(TRIP2G_FLEET_FLEET_ID) теперь обязателен и не имеет значения по умолчанию (раньше это была метка со значениемfleet1). Флит выполняет только роли со своимfleet_id. Роль безfleet_idне выполняется нигде, флит сообщает о ней как об ошибке при поиске ролей. Ключаexecutorбольше нет. Код-роли выполняет codellm, отдельный сервис с OpenAI-совместимым API (cmd/codellm, образDockerfile.codellm). Флит, у которого--llm-base-urlуказывает на codellm, выполняет тело роли как код. Сам флит код больше не запускает: флага--allowed-programsу него нет, список интерпретаторов, песочница и доступ в сеть настраиваются в codellm (CODELLM_ALLOWED_PROGRAMS,CODELLM_SANDBOX,CODELLM_SANDBOX_NETWORK). codellm может требовать ключ (CODELLM_API_KEY), флит передаёт его через--llm-api-keyили--exec-api-key. Ещё флит отдаёт GraphQL API только для чтения: роли и то, как они запускают друг друга (--graphql-addr, по умолчанию127.0.0.1:9093), доступ только для админов хаба. - Зачем. Выполнение кода и песочница жили внутри процесса агента. В отдельном сервисе код-роль и LLM-роль могут работать на разных хостах, а роль переносится между ними правкой одной строки.
- Как. До обновления флита: добавьте
fleet_idво все роли, запустите по флиту на каждыйfleet_idс таким же--fleet-id, для код-ролей запустите codellm и направьте на него--llm-base-urlэтого флита. Перенесите--allowed-programsвCODELLM_ALLOWED_PROGRAMSу codellm. См. Флит.
Мелкие исправления
- Отозванный личный токен перестаёт работать сразу.
revokeUserTokenиadminRevokeUserTokenтеперь убирают токен из кэша сервера. Раньше отозванный токен работал ещё до 30 секунд. - Кастомный домен, на котором нечего индексировать, получает пустой sitemap. Его
/sitemap.xmlтеперь пустой<urlset>. Раньше он отдавал sitemap основного домена со страницами другого сайта. См. Мульти домены. - После обновления браузер получает новые встроенные скрипты и стили. Файлы под
/assets/теперь отдаются сETagиCache-Control: адрес с текущим хэшем файла в?h=кэшируется на год как неизменный, любой другой на час, потом перепроверяется. Раньше сервер отвечал «304 Not Modified» на любойIf-Modified-Since, потому что у встроенных файлов нет времени изменения, и браузер мог держать старый скрипт после обновления. - Быстрее синхронизация файлов.
uploadNoteAssetсначала проверяет, сохранён ли файл, и только потом разбирает заметку, а версия layout загружает только файлы из_layouts/, а не все заметки. Повторная синхронизация хранилища с большим числом картинок больше не разбирает заметку заново ради каждой картинки. - Коммит в логе запуска. Сервер пишет в лог
trip2g startingс git-commit, из которого он собран. - Проверка email. Создание и правка пользователя в админке и
createHatLinkпроверяют только формат адреса. MX-запись домена больше не запрашивается, поэтому домен без почты не вызывает ошибку, а форма не ждёт DNS. - Длинный инлайн-код. В стандартном шаблоне длинный URL или путь в инлайн-коде переносится, а не расширяет страницу.
- Даты в админке. Журнал аудита и экраны листов ожидания показывают даты правильно; раньше они форматировались дважды.
- $mol в шапке. Компонент $mol в шапке больше не занимает всю строку шапки, пока грузятся его стили.
- Выход из админки. Кнопка Sign out в админке выполняет выход. Раньше она ничего не делала.
- Заголовки выносок. В светлой теме заголовок выноски окрашен в цвет выноски и читается. Тип выноски, который начинается с нелатинской буквы (
[!заметка]), получает правильную заглавную букву в заголовке по умолчанию, и на сайте, и в Telegram. - Лимиты Telegram. Отправка и правка постов Telegram (ботом и через аккаунт) делает не больше трёх попыток, если Telegram отвечает ошибкой лимита, а ожидание между ними прерывается при остановке сервера. Раньше число повторов не было ограничено.
- Хранилище при старте. Сервер ждёт S3-хранилище до 45 секунд, а не завершается на первой неудачной проверке бакета. Неверные ключи или имя бакета по-прежнему останавливают запуск сразу. Имя
MINIO_SECRET_ACCESS_KEYпринимается как секретный ключ. - Пропавшие файлы в хранилище. Если клиент загружает файл, запись о котором уже есть, а данных в хранилище нет (например, бакет очистили), сервер сохраняет файл заново. Раньше он только привязывал запись, и файла так и не было.
- Имя стартового хранилища.
/_system/onboarding-vault?name=<имя>задаёт и имя zip-файла, и папку внутри. Буквы, цифры, точка, дефис и подчёркивание, до 64 символов. Неверное имя даёт 400.
v0.10.0 (2026-07-13)
RSS теперь шаблон, а не Go-фича
- Что. Старый автоматический фид
<адрес-заметки>.rss.xml(по фиду на заметку, элементы = ссылки внутри неё) убран, вместе с настройкой сайтаenable_rss. RSS теперь обычный Jet-layout: заметка с frontmatterlayout: rss+content_type: application/rss+xml; charset=utf-8рендерится в RSS 2.0-фид, отдаётся там, куда вы его смаршрутизируете (по умолчанию —/feed.xml). Какие заметки попадают в фид, определяет frontmatterrss_glob/rss_limit, и в него всегда попадают только публично читаемые заметки (free, без sign-in-гейта, без системных). - Зачем. Фид «по ссылкам заметки» был жёстким и его было сложно найти. Шаблонный фид полностью кастомизируется — правьте
_layouts/rss.html, чтобы поменять состав элемента, или сделайте несколько фидов с разным охватом — тем же layout-механизмом, что и весь остальной сайт. - Как. См. ru/user/rss-feeds. Старые ссылки подписчиков на
.rss.xmlтеперь отдают 404, редиректа нет — настройте/feed.xmlвзамен.
Темы для дефолтного шаблона + живой редактор тем
- Что. Две новые страницы про смену вида дефолтного шаблона. ru/user/themes объясняет: весь вид задают переменные Pico CSS
--pico-*— переопределите их в блоке<style>через админку HTML Injection, и сайт перекрашивается целиком, без пересборки и форка. ru/user/theme-editor показывает живой редактор прямо на сайте: двигаете переменные, видите превью, а под админом жмёте Save — тема записывается в HTML injection вашего сайта. Редактор — это кастомный Jet-layout (layout: theme_editor), его можно поставить на любой сайт из trip2g/theme_editor_template. - Зачем. Раньше сменить вид означало читать стили и угадывать нужную переменную. Редактор превращает это в цикл «подвигал — сохранил» и заодно служит примером кастомного шаблона, работающего с admin API (тот же приём, что у канбан-доски).
- Как. Список переменных и куда вставлять — в ru/user/themes. Попробовать редактор — ru/user/theme-editor; поставить к себе —
curllayout из репозитория шаблона и заметка сlayout: theme_editor.
Встраивание заметок с кастомным CSS-классом
- Что. Новая страница ru/user/embedded-notes про
![[имя-заметки]]— содержимое целевой заметки рендерится прямо на странице, обёрнутое в<div class="embedded-note">. Добавьтеembed_class: my-classво фронтматтер встраиваемой заметки — обёртка получит ещё иembedded-note__my-class, зацепку для стилей из админки. Любая заметка становится переиспользуемым оформленным блоком: колаут, промо-карточка, общий футер. - Зачем. У переиспользуемых блоков (баннер подписки, общий заголовок) не было описанного паттерна. Вместе со скрытыми
_-заметками одно редактирование обновляет блок везде, где он встроен. - Как. См. ru/user/embedded-notes. Держите блок в
_скрытойзаметке, добавьтеembed_class:для стилей и встраивайте через![[_block]].
Доступ Telegram-группа ↔ подграф (в обе стороны)
- Что. Новая страница ru/user/telegram-access про связку Telegram-группы с подграфом: доступ течёт в обе стороны — участники группы читают заметки подграфа на сайте, а читатели с доступом к подграфу получают приглашение в группу. Каждое направление — отдельный переключатель, и оба живые: вышли из группы — заметки закрылись; истёк доступ — фоновая задача убирает вас из группы. В основе — членство теперь отслеживает события Telegram
chat_member, поэтому входы и выходы регистрируются сразу, а меню/contentбота ведёт на нужные подграфы. - Зачем. Гейтинг контента по членству в платной группе был возможен, но плохо описан, а изменения членства не всегда учитывались. Теперь Telegram-группа — полноценный источник доступа, наравне с платным оффером или ручной выдачей.
- Как. Добавьте бота админом группы, свяжите группу с подграфом в Админка → TG bots (по секции на каждое направление), пометьте заметки
subgraph:, а участники открывают закрытые заметки через/content. Полный разбор — в ru/user/telegram-access.
Доступ к сайту по Git
- Что. Новая страница ru/user/git: каждый сайт trip2g — это git-репозиторий.
git clone https://ваш-сайт.com/_system/git, правьте markdown любым инструментом или агентом, коммит, push — сервер применяет ваши коммиты к живому сайту. Авторизация HTTP Basic (user+ git-токен из Админка → Integrations → Git tokens), а области токена теперь разделяют pull и push — токен только на чтение клонирует, но не пишет. - Зачем. Массовым правкам, CI и кодинг-агентам нужен стандартный git, а не отдельный плагин. Ограниченные токены делают безопасной выдачу URL клонирования только на чтение.
- Как. Создайте git-токен в админке,
git cloneURL_system/gitи push в веткуmaster(единственная ветка, которую сервер принимает). Для ежедневного письма в Obsidian по-прежнему удобнее плагин синхронизации. См. ru/user/git.
Рецепты подграфов: приватность по умолчанию, всё бесплатно, скрытые _-заметки
- Что. В ru/user/subgraphs добавлены три практических рецепта. Патч фронтматтера приватность по умолчанию назначает каждую непомеченную заметку в зарезервированный подграф, к которому ни у кого нет доступа — ничего не утечёт, пока вы явно не опубликуете. Симметричный патч всё бесплатно ставит
free: trueна весь vault для полностью публичного сайта. И уточнённый раздел описывает: любой файл или папка с именем на_скрыты из списков, поиска и RSS (но остаются доступны по ссылке и встраиваемы). - Зачем. «Какие заметки кому видны» — самый частый вопрос про подграфы. Эти рецепты дают готовые точки старта для двух краёв спектра — приватность-в-первую-очередь и публичность-в-первую-очередь.
- Как. В Админка → Notes & Content → Frontmatter Patches создайте правило с jsonnet из ru/user/subgraphs (приватность по умолчанию или всё бесплатно) и заведите зарезервированный подграф
privateбез выдач.
v0.9.0 (2026-07-12)
Первый вход на свежем self-host сервере
- Что. Первый вход администратора на новом сервере больше не требует почтового сервера. Рекомендованный путь:
trip2g-server login-linkпечатает одноразовую ссылку для входа, действующую 5 минут — открываете её прямо из терминала (/_system/hatтеперь принимает токен по GET, поэтому ссылка кликабельна в браузере). Хотите вход по коду из письма, но без почтового сервера? ПоставьтеLOG_SIGN_IN_CODES=true, и сервер пишет коды входа в лог (читаете изjournalctlилиdocker logs). Два сопутствующих фикса: запрос кода без настроенного транспорта теперь возвращает понятную ошибку вместо молчаливого «письмо отправлено», аSMTP_STARTTLS=falseработает — код уходит на локальный plaintext-relay (Postfix, MailHog) без навязанного STARTTLS. - Зачем. Первый вход на чистом сервере — без домена, без почты — был тупиком: запрос принимался, но код нигде не появлялся, а локальный relay падал на ненужном STARTTLS. Теперь одна команда даёт рабочую ссылку входа, с fallback через лог и честными ошибками.
- Как. Запустите
trip2g-server login-linkна сервере (или черезdocker execв контейнере) и откройте ссылку в течение 5 минут; повторный запуск даёт свежую ссылку. Fallback:LOG_SIGN_IN_CODES=true, запросите код на странице входа, скопируйте из лога. Уберите эти bootstrap-опции, когда настроите OAuth или реальный почтовый транспорт — см. ru/user/smtp.
Поиск: один движок для сайта и MCP
- Что. Поиск на сайте (GraphQL) и MCP-инструмент
searchтеперь используют один движок выдачи (текст + векторы + слияние рангов) вместо двух разошедшихся копий. Два изменения поведения для MCP-клиентов: анонимные (и федеративные) клиенты теперь ищут по опубликованным (live) заметкам, как анонимные посетители сайта — раньше поиск шёл по последним версиям, включая черновики публичных заметок; векторный поиск теперь пропускает устаревшие эмбеддинги после смены модели, а не ранжирует их случайно. Неиспользуемая серверная страница/searchудалена (поисковый виджет сайта работает через GraphQL и не затронут). - Зачем. Исправления и настройка поиска попадали на одну поверхность и молча пропускали другую; агенты и посетители могли видеть разную выдачу по одному запросу. На сайтах с включённым показом черновиков анонимные MCP-клиенты могли читать черновики публичных заметок.
- Как. Автоматически. Если интеграция полагалась на то, что анонимный MCP-поиск видит черновики, авторизуйте её API-ключом — клиенты с API-ключом по-прежнему ищут по последним версиям.
Руководство по SMTP-провайдерам
- Что. Новая двуязычная страница документации ru/user/smtp — как выбрать провайдера транзакционной почты для self-hosted инстанса trip2g. Перечислены переменные окружения (
SMTP_HOST,SMTP_PORT,SMTP_FROMи другие), разобраны категории провайдеров (dedicated transactional, почта регистратора, бесплатные тиры) и дан однозначный вывод: не запускайте собственный исходящий MTA. - Зачем. Доставляемость почты для self-hosted инстансов — частый вопрос на старте. Доступные переменные конфигурации были описаны разрозненно; единого места для сравнения вариантов не было.
- Как. Прочитайте ru/user/smtp перед настройкой email на новом инстансе.
Instagram-фреймы (instaframes)
- Что. Для instaframes появились двуязычное руководство и галерея-лендинг. На страницах с каруселью теперь есть виджет редактирования прямо на странице: кнопка открывает колоду слайдов для правки, не уходя с опубликованной страницы. Устаревший reel/teleprompter-layout удалён — carousel остаётся единственным layout для instaframes.
- Зачем. Руководство и галерея делают функцию обнаруживаемой. Виджет редактирования закрывает петлю обратной связи: увидел опечатку на слайде — исправил здесь же. У reel-layout не было активных пользователей, и он дублировал кодовый путь карусели.
- Как. Полное руководство и галерея: ru/user/instaframes. Виджет редактирования появляется на страницах карусели автоматически, если вы вошли как владелец сайта.
Исправления в панели администратора
- Что. Два небольших исправления в admin-дашборде: ссылка на документацию по онбордингу теперь ведёт на нужную страницу (опечатка приводила к 404), а объём хранилища подписан MiB, что соответствует реальным расчётам в коде.
- Зачем. Ссылка на онбординг была первым, что нажимал новый оператор; попасть на 404 — плохое первое впечатление. Прежний ярлык «MB» подразумевал десятичные мегабайты, тогда как код отдавал двоичные мебибайты.
- Как. Автоматически после обновления.
Улучшения MCP и федерации
- Что. Четыре точечных улучшения MCP-эндпоинта и слоя федерации. (1) Новый инструмент
federated_instructionsпозволяет клиенту получить инструкционный текст для любого маршрута с кэшированием на маршрут. (2) Глубина федеративного обхода теперь работает по включающей семантике: запрос сmax_depth=2может пройти ровно 2 прыжка — раньше он останавливался на один прыжок раньше. Запросы, превышающие лимит, теперь возвращают чистый отказ. (3) Hop-rewrite распространяетkb_idвызывающего фрейма на федеративные результаты и ошибки — клиент всегда знает, от какого пира пришёл ответ. (4) Инструменты чтения заметок (note_html,note_text) теперь работают поpathилиmatch_id; документально зафиксировано, чтоnote_id— это внутренний целочисленный идентификатор, непригодный для долгосрочных ссылок. - Зачем. Ошибка «на единицу» означала, что
max_depth=1не достигал ни одного пира — конфигурация выглядела рабочей, но не работала. Hop-rewrite позволяет строить цепочки атрибуции через федерированные хранилища. Ясные указания поnote_idпредотвращают кэширование агентами внутренних идентификаторов между синхронизациями. - Как. Изменений конфигурации не требуется. Если вы опирались на старую семантику глубины, уменьшите
max_depthна единицу, чтобы сохранить прежнее поведение.
Визуализатор обхода графа MCP
- Что. Новая интерактивная страница документации ru/search_visualizer позволяет наблюдать, как AI-модель обходит федеративный граф в реальном времени. Каждый вызов инструмента отображается как узел; есть мини-карта топологии федерации, быстрый выбор модели, временна́я ось шагов, панель импорта трейсов для воспроизведения сохранённых сессий. Бюджет — 50 шагов, достаточно для глубоких многоуровневых спусков.
- Зачем. Обход федеративного графа непрозрачен: трудно понять, почему агент выбрал тот или иной путь и откуда взялась задержка. Визуализатор делает обход наблюдаемым.
- Как. Откройте ru/search_visualizer, выберите модель и начальную базу знаний, введите запрос. Обход разворачивается узел за узлом. Трейс можно скачать и воспроизвести позднее.
memcli: локальный векторный поиск
- Что.
memcliтеперь включает нативный arm64-ретривер, который выполняет векторный поиск локально — без обращений к внешнему сервису. Ретривер обрабатывает и эмбеддинги, и (опционально) ранжирование. - Зачем. Векторный поиск на локальном инстансе memcli раньше требовал сетевого вызова к серверу эмбеддингов. Встроенный ретривер делает локальный семантический поиск автономным на Apple Silicon.
- Как. Ретривер запускается автоматически с
memcli upна arm64 macOS. Дополнительная настройка не требуется. Гайд: ru/user/memcli.
Ретривер: поддержка Metal/MPS и безопасные сниппеты для UTF-8
- Что. Ретривер-сайдкар теперь запускает инференс на Metal (Apple GPU) и MPS с точностью fp16 и исправлениями стабильности для micro-batch. Сниппеты результатов поиска обрезаются по границам rune, поэтому многобайтовые символы (кириллица, CJK, эмодзи) больше не обрезаются посередине кодпоинта.
- Зачем. Инференс на CPU был в 3–10 раз медленнее, чем на GPU. Усечённый UTF-8 приводил к искажённому тексту сниппетов в многоязычных хранилищах.
- Как. Автоматически. Ретривер выбирает лучшее доступное устройство при старте. Изменений конфигурации не требуется.
Операционные метрики: конвейер эмбеддингов и очереди задач
- Что. На внутреннем эндпоинте метрик появились две новые группы Prometheus-метрик. (1) Метрики конвейера эмбеддингов — количество чанков, задержка и процент ошибок для каждого прохода эмбеддинга. (2) Метрики глубины очередей задач — сколько задач ждёт в каждой именованной очереди. Дополнительно: запросы на эмбеддинг чанков теперь разбиваются на под-батчи по максимальному размеру, который объявляет сервер ретривера, — это предотвращает шторм повторного эмбеддинга при лимите батча ниже числа чанков.
- Зачем. Без метрик глубины очередей у операторов не было раннего предупреждения о росте бэклога. Фикс под-батчинга устранял цикл падений: oversized-батч → отказ эмбеддера → полный повторный эмбеддинг → снова oversized-батч.
- Как. Новые метрики появляются на существующем эндпоинте без изменений конфигурации. Обновите дашборды, добавив панели глубины очередей и задержки эмбеддингов.
Telegram: навигационный браузер только для публичных live-заметок
- Что. Встроенный в Telegram навигационный браузер теперь показывает только публичные live-заметки. Раньше в нём могли появляться приватные заметки и черновики.
- Зачем. Пользователь, нажавший навигационную ссылку в Telegram, должен попадать на доступный ему контент. Черновики и приватные заметки открывали битые или закрытые страницы.
- Как. Автоматически. Черновики и приватные заметки больше не отображаются в навигационном браузере Telegram.
v0.8.0 (2026-07-01)
Флот агентов (fleet)
- Что. Заметки trip2g теперь могут запускать LLM-агентов. Вы пишете «роль-заметку» в папке (по умолчанию
roles/): frontmatter описывает конфигурацию (модель, инструменты, шаблоны путей чтения/записи, триггер, лимиты бюджета, политика конкурентности), тело содержит инструкцию в виде Jet-шаблона против контекста триггера. Демон fleet обнаруживает роль-заметки, согласует change/cron-вебхуки и при срабатывании триггера запускает ограниченный агентный цикл с инструментами: search, read_note, patch_note, write_note. Инструментexecutor: code(например, Python) отключён по умолчанию и контролируется через--allowed-programs/TRIP2G_FLEET_ALLOWED_PROGRAMS. - Зачем. Хранилище превращается в поверхность автоматизации (обработка транскриптов, построение баз знаний, сортировка, суммаризация) без внешней оркестрации. Trip2g остаётся простым источником событий; fleet является единственной движущейся частью.
- Как. Бинарный файл
fleetпоставляется в образе trip2g по адресу/fleetи как отдельная загрузка. Настройте через переменные окруженияTRIP2G_FLEET_*(или флагиcmd/fleet); аутентификация через admin HAT, сгенерированный из JWT-секрета приложения. Гайд: ru/agents_how_it_works.
Готовые бинарные файлы с контрольными суммами
- Что. К каждому релизу на GitHub теперь прикреплены готовые архивы: linux amd64/arm64, macOS arm64/amd64, Windows amd64. В каждом архиве находятся
trip2g-serverиfleetплюс файл.sha256. - Зачем. Trip2g можно запустить без Docker: единственный бинарный файл на любой из пяти целевых платформ.
- Как. Скачайте архив для своей ОС со страницы Release, проверьте через
sha256sum -c *.sha256, распакуйте и запустите. macOS-бинари не подписаны: Gatekeeper предупредит при первом запуске; откройте через правый клик → «Открыть», чтобы разрешить.
trip2g lint docs
- Что. Новая подкоманда
lint docsзапускает реальный загрузчик заметок над деревом документации и сообщает о проблемах: утечки межъязыковых вики-ссылок (русская заметка ссылается на английскую страницу), ошибки рендеринга layout-ов, битые ссылки (информационный режим). Заменяет прежний bash-скриптcheck-doc-lang-links.sh; встроена в CI. - Зачем. Обнаруживает проблемы публикации до того, как они попадут в эфир: русская заметка ссылается на английскую страницу, layout не рендерится.
- Как.
trip2g lint docs(илиgo run -tags dev ./cmd/server lint docsиз исходников). Код выхода ненулевой при предупреждениях; битые ссылки на ещё несозданные заметки считаются информационными и не блокируют проверку.
Метрики базы данных и более строгий SQL
- Что. На существующем эндпоинте метрик теперь доступны три новые Prometheus-метрики: состояние пула соединений, размер файла базы данных и размер WAL. Двойные кавычки в SQL-строках отклоняются прагмой
_dqsпри старте, чтобы SQL-опечатки выявлялись сразу. Последний прямой импортmattn/go-sqlite3удалён из тестового инструментария; приложение работает на pure-Go modernc с более ранней версии. - Зачем. Метрики дают операторам видимость нагрузки на базу до того, как она стала инцидентом. Прагма
_dqsпревращает целый класс тихих SQL-ошибок в явную ошибку запуска. - Как. Метрики появляются на существующем эндпоинте внутренних метрик без изменения конфигурации. Прагма
_dqsможет обнаружить имеющиеся SQL-опечатки при обновлении; проверьте логи запуска, если сервер отказывается стартовать.
Поиск: cross-encoder reranker удалён
- Что. Опциональный второй этап ранжирования на основе cross-encoder (добавлен по умолчанию отключённым в одном из предыдущих релизов) удалён вместе с Python-сайдкаром
reranker-server/. - Зачем. Два раунда бенчмаркинга показали снижение качества поиска: nDCG упал с 0.9221 до 0.8881 на отрывках в 512 символов и до ~0.39 на полных текстах заметок. Cross-encoder переоценивал поверхностное совпадение терминов и продвигал тематически близкие, но нерелевантные результаты, которые bi-encoder + BM25 + RRF корректно располагал ниже. Подробнее:
docs/dev/reranker.md. - Как. Ничего делать не нужно: функция была отключена по умолчанию. Любые ключи
vector_search.reranker.*теперь игнорируются.
v0.7.1 (2026-06-26)
Широкие страницы (wide: true)
- Что. Любую заметку теперь можно отрисовать на всю ширину, добавив
wide: trueво frontmatter: страница убирает обе боковые панели и узкую читаемую колонку, и контент занимает всё основное поле. - Зачем. Широкий контент (kanban-доски, большие Mermaid-диаграммы, широкие таблицы) был зажат в дефолтной колонке 65ch.
- Как. Добавьте
wide: trueво frontmatter заметки. Гайд: ru/user/default-template.
Инструмент превью layout-ов: trip2g-preview, замена renderlayout.py (19483421, d0fe4179)
- Что. Новый standalone node-CLI
scripts/trip2g-preview.mjsрендерит Jet-layout против заметки через/_system/renderlayoutи добавляет режим--watch: пере-POST-ит при каждом сохранении, а открытая по?liveстраница перезагружается сама. Заменяет старыйscripts/renderlayout.py(удалён), без зависимости от Python. - Зачем. Ускоряет цикл вёрстки: правишь
_layouts/*.htmlи сразу видишь результат, ловишь ошибки Jet в терминале. Работает против любого сервера: локального memcli (читаетdata.json) или удалённого/staging через--api-url/--api-key. - Как.
node scripts/trip2g-preview.mjs --watch --layout-file _layouts/article.html --note-path /about, затем открыть напечатанный?liveURL (с memcli сначалаmemcli open, чтобы браузер был залогинен). Гайд: ru/user/renderlayout; два локальных дизайн-цикла описаны вdocs/dev/local_design_iteration.md.
memcli: изолированные инстансы через --name (5f0542f8, 353841e0)
- Что.
memcli up --name <id>поднимает второй инстанс в своём контейнереtrip2g-memory-<id>. Тот же--nameпередавайте вdown/status/logsи задавайте отдельный--port. Без--nameповедение по умолчанию (trip2g-memory) не меняется. - Зачем. Позволяет держать несколько локальных memcli-инстансов рядом (например, по одному на vault), не конфликтуя за единственное захардкоженное имя контейнера.
- Как.
node cli/memcli/dist/memcli.js up --folder ./v2 --name v2 --port 24381. Состояние хранится отдельно для каждого--folder. Гайд: ru/user/memcli.
v0.7.0 (2026-06-23)
Режим read replica (a4423895)
- Что. Второй инстанс trip2g теперь может работать как реплика только для чтения. Задайте
TRIP2G_LEADER_ADDRна нём: он будет обслуживать все GET-запросы из локальной копии SQLite (реплицируемой через LiteFS) и пересылать каждую запись на первичный инстанс. - Зачем. Это снижает задержку на чтение при нагрузке на primary, позволяет перезапускать primary без потери ни одного читательского запроса и открывает возможность горизонтального масштабирования на отдельных машинах. В тестах во время перезапуска лидера не было зафиксировано ни одного сбоя чтения на реплике.
- Как. Запустите LiteFS на обоих серверах (реплика получает потоковую копию WAL SQLite). Запустите реплику с
--leader-addr=http://10.x.x.x:8082 --leader-shared-secret=.... Значение--leader-shared-secretдолжно совпадать сTRIP2G_LEADER_REPLICA_SECRETна primary. Полная инструкция по настройке: ru/user/read-replica.
Вход через OIDC / корпоративный SSO (1d2b12b5, b6446f76, e6d7afe9)
- Что. Пользователи теперь могут входить с корпоративной учётной записью через Authentik, Keycloak или любой стандартный OpenID Connect-провайдер. На странице входа появляется кнопка «Войти через SSO», когда OIDC-провайдер настроен. Автопровизиинг опционален: при включении валидная идентичность IdP создаёт пользователя trip2g автоматически; при отключении пускаются только предварительно заведённые аккаунты.
- Зачем. Команды с корпоративным IdP больше не управляют отдельными паролями trip2g, а MFA обеспечивает сам IdP.
- Как. Задайте
OIDC_CLIENT_ID,OIDC_CLIENT_SECRETиOIDC_DISCOVERY_URLна сервере. Строка в базе данных не нужна: провайдер читается из переменных окружения при каждом запросе. CRUD учётных данных администратора доступен через GraphQL-мутации для установок, которым это нужно. Полная инструкция: ru/user/oidc.
Хранилище на локальной файловой системе (0f5b84e2)
- Что. Trip2g теперь может хранить загруженные ассеты (изображения, вложения) на локальной файловой системе вместо S3 или MinIO. Задайте
STORAGE_BACKEND=local(или--storage-backend=local) и при необходимости укажите путь черезSTORAGE_LOCAL_PATH. - Зачем. Работа без объектного хранилища убирает главную внешнюю зависимость для одиночного сервера. Дешёвый VPS с подключённым томом становится полноценным автономным деплоем.
- Как. Добавьте
STORAGE_BACKEND=localв файл переменных окружения. Путь по умолчанию:./data/storage; переопределите черезSTORAGE_LOCAL_PATH. При переходе с S3 нужно предварительно скопировать содержимое бакета в локальный путь. Полная инструкция для одного сервера: ru/user/local-quickstart.
Деплой без даунтайма (08a6cfde, 22dd8cf3)
- Что. Trip2g теперь поддерживает socket activation systemd (
LISTEN_FDS) и публикует два health probe на отдельном внутреннем порту:/livez(всегда 200, даже в период прогрева) и/readyz(503 до тех пор, пока инстанс не прогрелся, и снова во время дрейна). Вместе они дают балансировщику или оркестратору сигналы, необходимые для переключения трафика со старой версии на новую без потери запросов. - Зачем. Раньше при rolling restart возникал разрыв: старый процесс уже останавливался, пока новый ещё прогревал кэш заметок. Теперь новый инстанс прогревается параллельно, и трафик переходит только после того, как
/readyzвернул 200. - Как. Добавьте socket unit в systemd, чтобы ОС удерживала порт в момент перезапуска. Задайте
--internal-listen-addr=:8081и настройте балансировщик на ожидание/readyzперед маршрутизацией.--shutdown-grace-periodуправляет тем, как долго старый инстанс продолжает обслуживать запросы после того, как/readyzпереключился на 503. Используйте--simple-backup-on-shutdown=false, чтобы пропустить финальный бэкап во время rolling restart. Готовые рецепты (Nomad/Traefik, Caddy, k3s): ru/user/zerodowntime.
memcli: CLI для агентной памяти (a4e6fd1e, 951d52ed)
- Что.
memcliзапускает инстанс trip2g как долгосрочную память для AI-агентов одной командой. Одна команда (node memcli.js up --folder ./memory) запускает Docker-контейнер с локальным хранилищем, создаёт admin-ключ через HAT, стартует sidecartrip2g-sync --watchи записывает заметкуhub.mdв memory-vault. Подкомандаhubи MCP-инструментmemory_bind_hubпривязывают инстанс к удалённому federation hub, чтобы поисковые запросы агента охватывали федеративные базы знаний через единственный MCP-эндпоинт. - Зачем. Раньше настройка отдельного trip2g для агента требовала четырёх ручных шагов (сервер, ключ, синхронизация, MCP-конфиг). memcli сворачивает их в одну команду. Скомпилированный
dist/memcli.jsпоставляется в репозитории, шаг сборки не требуется. - Как.
node cli/memcli/dist/memcli.js up --folder ./memory-vault. После завершения MCP-эндпоинт доступен по адресуhttp://localhost:24081/_system/mcp. Добавьте его в.mcp.jsonвашего агента. Полная документация: ru/user/agent-memory.
Стабильность gitapi-зеркала (2845bb82, 8082c24d)
- Что. Два исправления в зеркале DB→git (gitapi). Если вызов
materializeзавершается с ошибкой на полпути, осиротевшие loose-объекты теперь очищаются перед возвратом ошибки. После успешногоmaterializeсразу запускается gc для упаковки loose-объектов. Раньше неудачные запуски могли оставлять orphaned-файлы, накапливавшиеся с каждой ночной перестройкой и в итоге заполнявшие диск. - Зачем. На инстансах с большим числом заметок повторные сбои materialize приводили к бесконтрольному росту директории git-данных. У операторов с небольшим VPS возникало давление на диск, требовавшее ручной очистки.
- Как. Ничего делать не нужно. Оба поведения автоматические после обновления. Если наблюдался рост диска от осиротевших объектов, запустите
git gc --prune=nowв директории репозитория (или дождитесь следующего запуска materialize при перезапуске сервера).
v0.6.1 (2026-06-22)
Живые обновления в плагине Obsidian (плагин v0.5.0)
- Что. Плагин trip2g sync теперь использует подписку
noteChanges(появилась в v0.6.0) и подтягивает изменения с сервера в хранилище в реальном времени, без нажатия синхронизации и без ожидания периодической проверки. - Зачем. Для работы с нескольких устройств и правок агентами изменение на сервере (или на другом устройстве) появляется в Obsidian за секунду, а не после следующего опроса. Это изменение UX: мгновенная свежесть и меньше холостого трафика, а не прирост сырой скорости. Запрос списка заметок и так был чтением по индексу на ~10 мс.
- Как. Обновите плагин до 0.5.0 (через BRAT), включите Two-way sync, затем задайте шаблоны Live pull в настройках плагина (include/exclude по glob, например
**, илиblog/**с исключениемdrafts/**). Безопасность сохранена: локальные правки никогда не перезаписываются (показывается окно конфликта), а удаление на сервере сначала спрашивает. Опрос раз в 60 секунд заменён на более лёгкую сверку-страховку раз в 5 минут. Документация:docs/ru/user/Двухсторонняя синхронизация.md. История:docs/ru/thoughts/sync-benchmark.md.
Намного быстрее массовая и CLI-синхронизация заметок с ассетами
- Что. Синхронизация множества заметок с картинками стала радикально быстрее из CLI и browser-sync. Холодный пуш 2000 заметок с 2000 картинок упал с 231.8 с до 8.8 с (~26×).
- Зачем. Каждая загрузка ассета запускала полный reload всех заметок на сервере, потому что CLI и browser-sync не батчили загрузки так, как уже делал плагин Obsidian. Один забытый флаг (
skipCommit) превращал 2000 загрузок в 2000 полных reload-ов. - Как. Ничего делать не нужно, кроме обновления до плагина/CLI 0.5.0. Интерактивный плагин Obsidian был не подвержен.
Стабильность: подписка реального времени могла ронять сервер (c283963b)
- Что. Гонка в in-process шине событий за подпиской
noteChangesмогла уронить весь сервер, когда подписчик отключался во время сохранения (send on closed channel). - Зачем. Пока подписчиков не было, она не стреляла, но новый плагин live-pull подключается и отключается регулярно, что делало баг реальным риском для всех, кто использует подписку.
- Как. Ничего делать не нужно; шина теперь использует отдельный done-канал на подписчика и прогнана под нагрузкой с детектором гонок. Также в плагин добавлена команда Sync now (палитра команд).
v0.6.0 (2026-06-17)
Диаграммы Mermaid
- Что. Блок
```mermaidв заметке рендерится как диаграмма на опубликованной странице: блок-схемы, диаграммы последовательности, круговые, Ганта, классов, состояний, ER и все остальные типы, которые поддерживает Mermaid. - Зачем. Диаграммы пишутся так же, как в Obsidian. Блок работает сразу.
- Как. Добавьте блок кода с тегом
mermaidи вставьте синтаксис диаграммы внутрь. Библиотека Mermaid загружается лениво, только на страницах, где есть блокmermaid; страницы без диаграмм ничего не платят. Документация:docs/en/user/mermaid.md,docs/ru/user/mermaid.md.
Графики из блоков datachart (7cadad3e, cb1403f5)
- Что. Блок
```datachartпревращается в интерактивный график на опубликованной странице (движок Apache ECharts). Данные берутся из четырёх источников:inline(строки прямо в блоке);frontmatter(файл из vault-а по[[вики-ссылке]]в frontmatter);url(внешний HTTP-JSON эндпоинт, который trip2g запрашивает на сервере и кеширует);internal(данные вашего сайта через SQL, скоро). Ошибки при загрузке URL фиксируются и видны автору сразу при синхронизации, а не только в браузере (97997bb4). - Зачем. Публикуйте живые графики так же естественно, как пишете любую заметку в Obsidian. Виджет ECharts загружается лениво, только на страницах с блоком
datachart. - Как. Добавьте блок с тегом
datachart. Внутри разместите JSON-объект с ключамиdataиconfig. Объектconfigпередаётся прямо в ECharts: работают все типы графиков и опции, которые он поддерживает. Документация:docs/en/user/chartdata.md,docs/ru/user/chartdata.md.
MCP Federation: эндпоинт топологии для администраторов и настраиваемый kb_id (4485b6cd, 5c7492ae)
- Что. Два дополнения к федерации, появившейся в v0.4.1. Новый эндпоинт
GET /_system/federation/admin(только для администраторов) возвращает полную топологию федерации: все зарегистрированные KB-пиры, их скоупы и статус доступности. Идентификаторkb_idвашего инстанса теперь настраивается вручную; если не задан, берётся хост публичного URL. - Зачем. Операторы нескольких федеративных инстансов видят топологию без погружения в базу данных. Явный
kb_idдаёт стабильный, читаемый идентификатор, который остаётся корректным независимо от домена инстанса. - Как. Эндпоинт требует admin API-ключ или сессию. Задайте
kb_idв конфигурации инстанса, чтобы переопределить значение по умолчанию. Существующие федеративные настройки менять не нужно.
SSE-подписки на изменения заметок в реальном времени (854c56b0)
- Что. Новая GraphQL-подписка
noteChangesпередаёт клиентам события upsert и удаления заметок через SSE с опциональной фильтрацией по glob. Каждое событие содержит diff изменённых HTML-селекторов, чтобы клиент мог точечно обновить DOM без перезагрузки страницы. - Зачем. Позволяет строить интерфейсы, которые отражают изменения vault-а в момент, когда они попадают на сервер: редактор в браузере, панель предварительного просмотра, кастомный дашборд.
- Как. Подпишитесь на
noteChanges(glob: "posts/**")через GraphQL SSE-эндпоинт. ПолеchangedHtmlSelectorsв каждом событии перечисляет CSS-селекторы, чей рендер изменился; это позволяет обновлять только нужные части страницы.
Редактор в браузере: переход по wikilink через Ctrl+Click (a065fea2)
- Что. В редакторе файлов в браузере (появился в v0.5.0) Ctrl+Click (или Cmd+Click на macOS) по wikilink открывает связанную заметку.
- Зачем. Повторяет поведение Obsidian и убирает необходимость искать нужный файл вручную.
- Как. Откройте редактор, наведите курсор на любую
[[вики-ссылку]]; курсор меняется на указатель. Нажмите Ctrl+Click, и связанная заметка откроется.
v0.5.1 (2026-05-27)
Секреты вебхуков: зашифрованные данные в теле доставки (0b72acf2)
- Что. У каждого change-вебхука и cron-вебхука в админке появился раздел Секреты. Добавляйте именованные пары ключ-значение (например
auth_token,api_key); они хранятся зашифрованными и отправляются в каждом запросе доставки в полеpayload.secrets. Получатель вебхука читает их без дополнительных вызовов API. - Зачем. Cron-вебхуки служат основой системы плагинов. Плагин представляет собой веб-сервер (или serverless-функцию), который получает payload с временным API-токеном и обрабатывает его, как правило в фоне, а затем патчит хранилище, когда готов. Плагины чаще всего stateless: им некуда безопасно хранить собственные учётные данные. Секреты решают эту проблему: trip2g хранит их зашифрованными и доставляет при каждом вызове, так что плагин остаётся credential-free на своей стороне. Если плагину нужно больше времени, он использует API-токен для асинхронных обновлений; секреты дают ему всё остальное для работы с внешними сервисами.
- Как. Откройте Admin → Change Webhooks (или Cron Webhooks) → выберите вебхук → прокрутите до раздела Секреты. Введите название и значение, нажмите Add Secret. Чтобы обновить значение, введите его в поле строки и нажмите Save. Чтобы удалить, нажмите иконку корзины (подтверждение на второй клик). Секреты появляются в теле доставки в виде
{ "secrets": { "auth_token": "...", "api_key": "..." } }.
v0.5.0 (2026-05-26)
Редактор файлов в браузере (для администраторов)
- Что. Администратор теперь может отредактировать любую страницу прямо на сайте. Иконка редактора появилась в правом верхнем углу админки и рядом с поиском на каждой странице. Откройте редактор, чтобы просмотреть все загруженные файлы деревом папок, открыть и отредактировать любой и сохранить. Файл можно откатить к прошлой версии.
- Зачем. Поправить опечатку или обновить страницу за секунды. Без Obsidian и повторной синхронизации.
- Как. Нажмите иконку редактора (только админы). Заметка текущей страницы открывается сразу; любой другой файл выбирается в дереве слева. Правки хранятся в браузере, пока не нажмёте «Сохранить»; панель версий позволяет загрузить и восстановить старую версию.
Публичный хаб курируемых баз
- Что. Раздел
hub/с двуязычным индексом баз знаний, доступных через хаб (первая запись: «Журнал Ника Сенина», отфильтрованные кейсы Code with Claude 2026). - Зачем. Просматриваемая публичная точка входа к федеративным базам.
- Как. См.
docs/ru/hub/_index.md; добавьте свою черезdocs/ru/hub/_create.md.
v0.4.1 (2026-05-25)
MCP Federation: один хаб поверх многих баз знаний
- Что. Ваш инстанс может работать как федеративный хаб. KB-заметка (заметка с
mcp_federation_kb_urlво frontmatter) регистрирует другую MCP-совместимую базу, аfederated_search/federated_similar/federated_note_htmlобращаются ко всем сразу через ваш единственный MCP-эндпоинт. Публичным базам аутентификация не нужна; приватные пиры используют общий HMAC-секрет. - Зачем. Один эндпоинт, одна точка авторизации: агент ищет по вашим заметкам, инстансам партнёров и внешним адаптерам (GitHub, Telegram) одновременно, без правки
.mcp.json. - Как.
- Документация:
docs/en/user/federation.md,docs/ru/user/federation.md - Публичная база: создайте заметку с
mcp_federation_kb_url(+ опциональноmcp_federation_kb_id) иfree: true. - Приватный пир: обменяйтесь федеративным секретом в Админка → Federation, затем добавьте KB-заметку.
- Документация:
Файлы Canvas (Base и Excalidraw позже)
- Что. Файлы
.canvasсинхронизируются и рендерятся..baseи.excalidrawтоже принимаются синхронизацией, но их рендеринг появится позже; пока вместо сломанной страницы показывается понятная заглушка. - Зачем. Vault-ы с Canvas работают уже сейчас; Base и Excalidraw синхронизируются без ошибок, пока готовится полная поддержка.
- Как. Просто синхронизируйте: плагин и CLI принимают все три расширения; Canvas рендерится уже сейчас.
Telegram-боты навигации и canvas
- Что. Бот-браузер по wikilink-ам и навигация на основе canvas поверх Telegram business connection.
- Зачем. Читатели могут ходить по графу вашего vault прямо из Telegram.
- Как. См. документацию по Telegram; включите на business connection.
Админка и конфиг
- Что. GraphQL API для истории версий заметки; фильтр отправок форм в админке (статус / дата / обработано); переменные окружения принимают префикс
TRIP2G_(неизвестные переменные вызывают предупреждение). - Зачем. Смотреть и откатывать контекст, разбирать отправки и безопаснее настраивать self-hosted инстансы.
- Как. Панели админки; добавьте префикс
TRIP2G_к любой переменной окружения.
Плагин синхронизации Obsidian + CLI
- Что.
- Принимает файлы
.canvas,.base,.excalidraw. - Показывает детали GraphQL-ошибки при неудачном push (больше нет «тихих» сбоев).
- Новый флаг
--exclude <glob>(можно повторять). Исключённые пути никогда не пушатся; если они уже есть на сервере, они скрываются. Простое имя вродеdevсовпадает с этой папкой и всем внутри неё. По умолчанию ничего не исключается: грузится всё.
- Принимает файлы
- Зачем. Держать тестовые/демо или внутренние папки (например,
dev/,demo/) в репозитории, но вне опубликованного сайта, и обратимо скрывать их на сервере. - Как.
trip2g-sync ./docs --exclude dev --exclude demo. Если вернуть путь обратно, он снова публикуется и автоматически перестаёт быть скрытым.
v0.4.0 (2026-05-21)
Мутация updateNotes: атомарный find/replace по заметкам
- Что. Новая GraphQL-мутация патчит несколько заметок одной транзакцией через
PathMapвида{path → [{find, replace}]}. - Зачем. Позволяет внешним инструментам, агентам и скриптам согласованно править весь vault без отдельных запросов на каждую заметку. Никаких частичных состояний, если одна замена не прошла.
- Как.
- Документация:
docs/en/user/update_notes.md,docs/ru/user/update_notes.md - Пример:
updateNotes(input: { pathMap: { "notes/post.md": [{ find: "old", replace: "new" }] } }). - End-to-end спецификация:
e2e/updatenotes/*(см. коммитtest(e2e): add updateNotes e2e spec and demo fixture).
- Документация:
Forms admin: обработка отправок формы
- Что. Новая мутация
markFormSubmitProcessedи поляprocessedу submit-ов; админ помечает отправку как обработанную, UI по умолчанию скрывает обработанные. - Зачем. Замыкает работу с формами внутри админки. Не нужно дёргать внешние сервисы.
- Как.
- Документация:
docs/en/user/forms.md,docs/ru/user/forms.md - Dev-справка:
docs/dev/forms.md - Frontmatter
can_submit/success_urlзадаёт условия отправки и страницу-подтверждение.
- Документация:
Smoke-render шаблонов: runtime-ошибки Jet видны сразу при загрузке
- Что. При загрузке заметок каждый успешно распарсенный Jet-шаблон выполняется на первых 10 заметках, которые ссылаются на него через frontmatter
layout:. Ошибки и паники рендера превращаются вNoteWarningна шаблоне. - Зачем. Раньше шаблон, который парсится, но падает на рендере (например,
{{ note.NoSuchField }}), ломался только при открытии страницы в браузере. Агент, делающий push-notes, об этом не знал. Smoke-render переносит ошибку на этап загрузки: варнинги попадают в тот же канал, что и parse-ошибки, и видны черезpushNotes/ админский список шаблонов без браузера. - Как. Автоматически, без флагов. После синхронизации ищите в варнингах шаблона
smoke render error/smoke render panic. Шаблоны безView(parse-ошибка) и без ссылающихся на них заметок пропускаются.
Отладка шаблонов: Meta.Debug() и глобальный debug()
- Что. В Jet-шаблонах:
{{ Meta.Debug() }}выводит метаданные заметки; глобальный{{ debug(<expr>) }}через reflection печатает тип, значение и набор методов любого выражения. - Зачем. Убирает цикл «угадай, что видит шаблон» при работе с layout-ами и компонентами.
- Как.
- Документация:
docs/en/user/jet.md,docs/ru/user/jet.md(раздел про отладку). - Пример:
{{ debug(note.M()) }}или{{ debug(note.Title) }}.
- Документация:
CLI renderlayout.py: рендер шаблона на заметке из терминала
- Что. Отдельный скрипт
scripts/renderlayout.pyплюс skillcheck_templatesдля агента. - Зачем. Дает preview шаблонов и воспроизведение проблем без браузера, удобно в CI и при правке
_layouts/. - Как.
- Документация:
docs/en/user/renderlayout.md,docs/ru/user/renderlayout.md - Skill:
docs/skills/check_templates.md
- Документация:
Исправления
layoutloader: nil-guard дляYieldNode.Parameters; ID шаблона в preview нормализуется как в продакшене.- Preview
renderlayout: autoimport, проводкаyield_blocksиhtmlInjectionsсовпадают с продакшеном. renderpreviewпарсит YAML-frontmatter изnote.srcкак реальный загрузчик.templateviews.GetStringsвозвращает пустой слайс вместоnil(нет сюрпризов при итерации в шаблоне).
Документация и chore
- Dev-справка по Forms + roadmap, BEM rendering skill, секция про
debug()в Jet. - Lint чист в
updatenotes,layoutloader,noteloader.
v0.3.1 (исторический backfill)
Формы в заметках (первая версия)
- Что. Формы встраиваются в vault-заметки через frontmatter и рендерятся стандартным шаблоном. Поддержка нескольких форм в одной заметке; submit хранит
note_version_idиform_id. - Зачем. Опубликованный сайт собирает ввод (регистрации, контакты, опросы) без внешних сервисов.
- Как. См.
docs/dev/forms.mdиdocs/{en,ru}/user/forms.md.
HTML-инъекции в layout-ах
- Что. Кастомные HTML-инъекции (placement
head/body_end) теперь применяются и внутри Jet-layout-ов, не только в стандартном шаблоне. - Зачем. Аналитика, кастомные скрипты и SEO-теги работают и на страницах с кастомными layout-ами.
- Как. Админка → HTML injections, placement
headилиbody_end.
Переработанный переключатель языка
- Что. Dropdown с полными нативными названиями языков и нормализацией; флаг US для английского; многоязычные документы.
- Зачем. Мультиязычные сайты выглядят корректно и правильно выбирают альтернативу по языку.
- Как. Frontmatter
lang:/lang_redirect:; переключатель рендерится автоматически.
Preview-эндпоинт renderlayout (/_system/renderlayout)
- Что. Админский эндпоинт для рендера произвольного layout-а на заметке.
- Зачем. Редактор / IDE-интеграции могут показывать живой preview изменений в
_layouts/*. - Как. Спецификация:
docs/superpowers/specs/2026-05-10-renderlayout-endpoint-design.md.
Админка: включение/отключение API-ключей
- Что. GraphQL-мутация и кнопка в админке для переключения состояния API-ключа; автоматическая чистка логов API-ключей через 90 дней.
- Зачем. Можно приостановить ключ без его ротации; логи не разрастаются.
- Как. Админка → панель API keys.
Onboarding vault: конфиги для агентов
- Что. Скачиваемый онбординг-vault теперь содержит
.mcp.json,codex.json, antigravity-конфиг иAGENTS.md. - Зачем. Drop-in настройка AI-агентов над Obsidian-vault без ручной проводки.
- Как. Скачайте онбординг-vault, конфиги уже внутри.
Cronjob-ы заблокированы для редактирования по умолчанию
- Что. Редактирование cronjob-ов выключено, пока не передан
--cronjobs-allow-edit. - Зачем. Снижает риск ошибок в общих / production-развёртываниях.
- Как. Добавьте флаг в стартовый скрипт, если правда нужно редактировать cron через админку.
Заметные исправления
- Backlinks / similar notes исключают системные заметки (пути с
_). - TOC-якоря снова работают (id у заголовков попадает в HTML).
- Mesh template:
yield_blocksперенесён в<head>с документированными ограничениями.
v0.3.0 (исторический backfill)
Документированный self-hosted деплой
- Что. End-to-end гайд по запуску trip2g на своей инфраструктуре.
- Зачем. Воспроизводимая установка вне хостинга.
- Как.
docs/{en,ru}/user/hosting.mdи связанные документы.
Sign-in wall + капча (Auth phase 1A)
- Что. Требование входа на конкретные заметки, капча на auth-флоу, усиленные сессии.
- Зачем. Закрывает приватный контент; снижает абьюз публичных auth-эндпоинтов.
- Как. Frontmatter / subgraph
require_signin; стандартный шаблон рендерит стену.
Vault-секции layout-а
- Что. Header / footer / sidebar могут браться из vault-заметок.
- Зачем. Авторы правят «обвязку» так же, как контент, без хаков в шаблонах.
- Как. Размещайте заметки по конвенциональным путям (см.
docs/dev/default_template.md).
Vault-патчи frontmatter
- Что. Markdown-файлы в vault могут декларировать патчи, которые применяются к frontmatter других заметок при загрузке.
- Зачем. Массово тегировать, ставить layout или нормализовать метаданные без правки каждой заметки.
- Как.
docs/dev/frontmatter_patches.md.
Поиск: эмбеддинги bge-m3 + микросервис эмбеддингов
- Что. Переход на модель
bge-m3; отдельный сервис embedding-server; топ-K векторного поиска сmatchOrigin. - Зачем. Лучше семантический поиск и развязка embedding-нагрузки.
- Как. Настройте эндпоинт эмбеддингов; векторный поиск доступен через существующие search-API.
Улучшения MCP
- Что. Результаты MCP-поиска адресуемы и самоописываемы, открываются как сфокусированный chunk-read; текстовые хиты маппятся в ближайшие чанки.
- Зачем. RAG-клиенты получают богаче и проще навигируемые результаты.
- Как. Подключайтесь по MCP; для проводки клиента см.
docs/user/ai-agent-docs-setup.md(если есть в вашем vault).
«Читать в Telegram» + Telegram UX
- Что. Кнопка на странице заметки; для опубликованных постов используются публичные TG-ссылки; UTM-теги на трафике из Telegram.
- Зачем. Замыкает связь между публикациями на сайте и аудиторией Telegram.
- Как. Автоматически для постов, опубликованных через TG; схема UTM описана в
docs/superpowers/specs/.
CLI exporttgchannel
- Что. Новая CLI-команда для экспорта Telegram-канала в Obsidian-markdown.
- Зачем. Один шаг от существующего канала к vault-у.
- Как.
go run ./cmd/exporttgchannel --help.
Заметные исправления
- Настраиваемая нормализация URL с 301-редиректами для альтернативных вариантов.
- Аудио рендерится как
<audio>, документы рендерятся как ссылки. - Очередь: устранена утечка горутин при двойном старте и deadlock при остановке.
- Wikilink
[[slug#anchor]]: якорь отрезается до lookup-а заметки. - Все варнинги
golangci-lintисправлены, добавлен pre-push хук.