> ## Documentation Index
> Fetch the complete documentation index at: https://omniflow-vpn.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Документация

> Как устроен исходник Mintlify, какие компоненты использовать, как проверить его локально и какое правило удерживает русское дерево в актуальном состоянии

Всё, что лежит под `docs/`, — это публичная документация, публикуемая через Mintlify. Она не
дополнение к коду: для оператора, решающего, доверить ли установке деньги, и для клиента,
решающего, доверить ли ей свой трафик, она — единственное описание продукта самим продуктом.
Поэтому `AGENTS.md` считает изменение поведения и его документацию одним изменением: обновляйте
документацию вместе с поведением и прогоняйте проверки документации до слияния.

В дереве два полных языковых дерева — английское в корне и русское под `docs/ru/`, — и оба доступны
для навигации на опубликованном сайте. Держать их в согласии — самое легко нарушаемое правило на
этой странице, поэтому ниже под него отведён отдельный раздел.

## Как устроен исходник

| Путь | Что содержит |
| - | - |
| `docs/docs.json` | Тему, цвета и всю навигацию для обоих языков |
| `docs/index.mdx`, `docs/comparison.mdx` | Две корневые страницы |
| `docs/getting-started/` | Быстрый старт, первую продажу и справочник переменных окружения |
| `docs/architecture/` | Модель процессов, границу владения и стек |
| `docs/customer/` | Всё, чего касается клиент: бот, веб-кабинет, подключение, уведомления, приватность |
| `docs/commerce/` | Каталог, заказы, кошелёк, выполнение заказов, подарки, кампании, реферальную программу |
| `docs/integrations/` | Remnawave, платёжных провайдеров, Telegram, OIDC и админский API |
| `docs/operations/` | Развёртывание, панель оператора, наблюдаемость, безопасность и рабочие инструкции |
| `docs/contributing/` | Этот раздел |

`docs.json` следует схеме `https://mintlify.com/docs.json`. Его массив `navigation.languages`
содержит две записи: `en`, помеченную `default: true`, и `ru`. Каждая несёт собственные названия
групп — русская запись использует русские заголовки групп (`Начало`, `Коммерция`, `Эксплуатация`), —
и каждая группа перечисляет пути страниц без расширения `.mdx`. Страница, которая существует как
файл, но не появляется ни в одной группе, недостижима из навигации, а записи группы без файла за ней
нечего отдавать, поэтому файлы и записи навигации добавляются и удаляются вместе.

Группа справочника API — не страница. Она направляет Mintlify на
`https://raw.githubusercontent.com/kewldan/omniflow/main/api/openapi.yaml`, поэтому каждый эндпоинт,
параметр и схема в опубликованном справочнике берутся из закоммиченного контракта в `main`. Эндпоинт
документируется изменением `api/openapi.yaml`, а не написанной руками страницей, которая его
описывает, — то же самое правило не даёт разойтись серверным типам Go и клиенту на TypeScript. См.
[Разработку](https://omniflow-vpn.mintlify.site/ru/contributing/development.md) — там о генераторах, читающих тот же файл.

## Анатомия страницы

Frontmatter — ровно два ключа, на каждой из существующих страниц:

```mdx theme={null}
---
title: Wallet and ledger
description: How the double-entry ledger records money and why a balance is never edited
---
```

`title` даёт заголовок страницы, поэтому тело начинается с `##` и никогда не содержит `#`.
`description` — одно предложение о том, что читатель сможет сделать после прочтения; именно оно
появляется в результатах поиска и превью ссылок.

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

Для каждой возможности, о которой рассказывает страница, отвечайте на три вопроса по порядку:

1. **Что это** — одно-два конкретных предложения.
2. **Зачем это существует** — какой сбой это предотвращает, какое ограничение обеспечивает или какая
   альтернатива была отвергнута. Обоснуйте чем-то видимым в коде: путём отказа, инвариантом,
   границей приватности. Никогда не выдумывайте обоснование.
3. **Как этим пользоваться** — точный маршрут, экран или команда; поля; кнопка; и что читатель видит
   при успехе, в ожидании и при отказе.

Страница, перечисляющая возможности, но не говорящая, как до них добраться, провалена. Страница,
дающая шаги, но не говорящая, зачем возможность существует, тоже провалена.

## Используемые компоненты

Mintlify предлагает большой набор компонентов; эта документация намеренно использует лишь малую его
часть, чтобы читатель усваивал один визуальный словарь.

| Компонент | Для чего используется |
| - | - |
| `<Note>` | Уточнение или оговорка, ошибка в которой ничего не стоит |
| `<Warning>` | То, что приводит к потере данных, денег, доступа или приватности |
| `<Steps>` с `<Step title="…">` | Упорядоченная процедура, которую выполняет читатель |
| `<CardGroup cols={2}>` с `<Card title="…" icon="…" href="…">` | Обзор подразделов, в основном на индексных и обзорных страницах |

`icon` принимает имя из Font Awesome — `database`, `server`, `wallet`, `shield-halved`, `bullhorn` и
подобные уже используются; предпочитайте иконку, которая в дереве уже есть, новой. Не ставьте три
выноски подряд: страница, где выделено всё, не выделяет ничего.

У блоков кода всегда указан язык (`bash`, `env`, `json`, `yaml`, `sql`, `mdx`). Диаграммы mermaid
допустимы для конечных автоматов и потоков и сегодня используются на трёх страницах; держите их
достаточно небольшими, чтобы читать без увеличения.

Таблицы — правильная форма для переменных окружения, ролей, прав доступа, состояний, провайдеров и
любой перечислимой матрицы. Держите набор столбцов согласованным со страницами, которые уже
документируют то же самое: все таблицы переменных окружения используют Variable / Default / Purpose,
и отклонение от этого делает два справочника невозможными для чтения рядом.

## Ссылки

Внутренние ссылки — абсолютные пути с ведущим слешем и без расширения: `/operations/admin-panel`,
`/commerce/orders-payments`. Якорь — это целевой заголовок, приведённый к нижнему регистру и
соединённый дефисами: `/commerce/orders-payments#refunds`.

CI выполняет `mint broken-links --check-anchors` по английскому дереву, поэтому якорь, не совпадающий
с реальным заголовком на целевой странице, роняет сборку так же, как отсутствующая страница.
Переименовав заголовок, поищите по дереву ссылки на его якорь.

Единственный случай, которого следует избегать, — ссылка с одной русской страницы на якорь другой
русской страницы: заголовки цели переведены, поэтому английского якоря там больше нет. Ссылайтесь на
страницу целиком. Якорь на заголовок той же самой страницы допустим: оба конца под вашим контролем.

Ссылайтесь между страницами щедро. Почти у каждой возможности есть клиентская страница, операторская
страница и справочник по конфигурации, и читателю, попавшему на одну из трёх, нужны две остальные.

## Тон

Ориентируйтесь на страницы, которые уже соответствуют планке, — `docs/customer/web-panel.mdx`,
`docs/operations/admin-panel.mdx`, `docs/commerce/gifts-and-goods.mdx`.

* Утвердительно, точно, неспешно. Полные предложения. Никаких маркетинговых прилагательных, никаких
  «просто», «всего лишь», никаких восклицательных знаков и эмодзи в тексте.
* Объясняйте причинность прямо в тексте: «…поэтому…», «…потому что…», «…а не…». Называйте
  отвергнутую альтернативу, когда код делает её очевидной.
* Второе лицо для инструкций («Откройте `/admin/catalog`», «Вставьте токен»); третье лицо для
  описания поведения.
* Предпочитайте «оператор» и «клиент» слову «пользователь».
* Точные идентификаторы в обратных кавычках: `APP_BACKUP_ENABLED`, `/v1/panel/orders`,
  `catalog.write`.
* Время в UTC, деньги в минорных единицах, даты по ISO.

Не превращайте настоящее ограничение в сноску. Отказы, лимиты и то, чего Omniflow намеренно не
делает, — часть продукта, и им место на странице.

## Русское зеркало

`docs/ru/**` зеркалит английское дерево файл в файл: у `docs/index.mdx` есть `docs/ru/index.mdx`, у
`docs/commerce/wallet-ledger.mdx` — `docs/ru/commerce/wallet-ledger.mdx`, и так для каждой записи в
`docs.json`.

> [!WARNING]
>
> Изменение английской страницы должно быть внесено в её русский аналог в том же коммите. Не в том
> же pull request, не отдельной задачей потом — в том же коммите. Частично переведённое дерево не
> читается русскоязычным оператором как неполное; оно читается как корректное и при этом молча
> устаревает, а чаще всего правятся как раз рабочие инструкции, где устаревание стоит денег или
> доступа.

Русский здесь не перевод из вежливости. Новая запись клиента создаётся с `locale` по умолчанию
`'ru'`, `APP_DEFAULT_CURRENCY` по умолчанию равна `RUB`, а панель оператора отказывается включать
режим обслуживания, пока оператор не предоставит и русское, и английское уведомление для клиентов.
Продукт рассчитан на русскоязычную аудиторию, поэтому отстающая русская страница — дефект в основной
документации, а не во второстепенной.

### Что переводить, а что не трогать

Переводите тело, `title` и `description` во frontmatter, заголовки таблиц, текст выносок и текст
подписей внутри компонентов.

Никогда не переводите:

* имена переменных окружения (`APP_TELEMETRY_ENABLED`), маршруты и пути API (`/v1/panel/orders`),
  имена прав доступа (`system.write`), идентификаторы базы данных, команды CLI, строки логов и ключи
  JSON или YAML;
* имена компонентов (`<Note>`, `<Card>`) и значения `icon`;
* названия продуктов: Omniflow, Remnawave, Telegram, YooKassa, CryptoBot, Telegram Stars, Xray,
  PostgreSQL, Valkey, River, Next.js.

Внутренние ссылки на русской странице ведут на русскую страницу: `/ru/operations/admin-panel`, а не
`/operations/admin-panel`. Русская страница, ссылающаяся в английское дерево, выбрасывает читателя
из его языка до конца сессии.

Используйте профессиональный технический русский, «вы» со строчной буквы и «ё» там, где эта буква
уместна (кошелёк, счёт). Некоторые устоявшиеся термины:

| Английский | Русский |
| - | - |
| operator / customer | оператор / клиент |
| plan / plan version | тариф / версия тарифа |
| wallet / ledger / entry | кошелёк / книга проводок / проводка |
| refund / top-up | возврат / пополнение |
| support ticket / canned response | обращение / шаблон ответа |
| campaign (настроенный объект) / send | кампания / рассылка |
| permission / role / audit trail | право доступа / роль / журнал аудита |
| backup / maintenance mode / provisioning | резервная копия / режим обслуживания / выдача доступа |
| entitlement / drift / idempotency | право на услугу / расхождение / идемпотентность |

Держите длину предложений и разбиение на абзацы близкими к английской странице. Два дерева
рецензируются как diff друг относительно друга, и русская страница, перестроенная ради стиля, делает
следующее обновление невозможным для проверки.

## Локальная проверка

```bash theme={null}
make docs-check
```

Это запускает все три проверки, которые запускает CI, из каталога `docs`:

```bash theme={null}
cd docs
PUPPETEER_SKIP_DOWNLOAD=true bunx mint@latest validate
PUPPETEER_SKIP_DOWNLOAD=true bunx mint@latest broken-links --check-anchors \
  --files "*.mdx" "architecture/**/*.mdx" "commerce/**/*.mdx" "customer/**/*.mdx" \
  "getting-started/**/*.mdx" "integrations/**/*.mdx" "operations/**/*.mdx" \
  "contributing/**/*.mdx"
sh scripts/check-russian-mirror.sh
```

`validate` проверяет `docs.json` против его схемы, а страницы — против правил MDX от Mintlify:
испорченный frontmatter, неизвестный компонент, незакрытый тег. Он покрывает оба языковых дерева.
`PUPPETEER_SKIP_DOWNLOAD=true` не даёт CLI подтягивать headless-браузер, который ему не нужен.

`broken-links` получает явный список файлов, а не весь сайт, потому что он разрешает каждую ссылку
относительно языка по умолчанию: ссылка `/ru/…` объявляется битой, хотя страница отдаётся ровно по
этому адресу, в чём легко убедиться через `mint dev`. Чтобы не терять проверку для русского дерева,
её берёт на себя `scripts/check-russian-mirror.sh`: он требует, чтобы у каждой английской страницы
был русский двойник и не было сирот в обратную сторону, чтобы ни одна русская страница не ссылалась
наружу в английское дерево и чтобы каждая ссылка `/ru/…` разрешалась в реальный файл.

> [!WARNING]
>
> Не «чините» русскую страницу удалением префиксов `/ru/`, чтобы успокоить проверку ссылок. Это молча
> выбрасывает русского читателя в английское дерево, и скрипт зеркала на этом упадёт.

Задача `docs` в `.github/workflows/ci.yml` выполняет ровно эти три команды, поэтому зелёный локальный
прогон означает зелёный CI по этому участку.

## Обязательства по точности

* **Никогда не документируйте то, что существует только в `ROADMAP.md`.** Запланированная
  возможность либо опускается, либо прямо помечается как ещё недоступная с указанием версии, к
  которой она нацелена.
* **Никогда не выдумывайте переменную окружения, значение по умолчанию, маршрут, право доступа или
  экран.** Если вы не можете открыть файл, который это определяет, не пишите об этом.
* **Изменение телеметрии несёт свою документацию.** Любое новое поле или событие требует обновления
  публичной документации по телеметрии и схемы в том же изменении — см.
  [Анонимную телеметрию](https://omniflow-vpn.mintlify.site/ru/operations/telemetry.md).
* **Никогда не вставляйте на страницу настоящий секрет, учётные данные, ссылку доступа,
  идентификатор клиента или платёжный payload,** в том числе внутри примера ответа. Синтезируйте
  их.
* **Архитектурные решения с долгосрочными последствиями** оформляются нумерованным файлом
  `docs/architecture/decision-NNNN-<slug>.mdx`, перечисленным на странице
  [Архитектурные решения](https://omniflow-vpn.mintlify.site/ru/architecture/decisions.md), а не текстом на странице о возможности.
  Держите их плоскими: `mint broken-links` не разрешает ссылку на страницу, вложенную на два уровня
  каталогов, поэтому папка `docs/architecture/decisions/` роняет задание docs, хотя страницы
  собираются.

## Добавление страницы

**1. Напишите английскую страницу**

Создайте `docs/<section>/<slug>.mdx` с frontmatter `title` и `description` и телом, которое
начинается с `##`.

**2. Напишите русскую страницу**

Создайте `docs/ru/<section>/<slug>.mdx` по зеркальному пути, переведённую по правилам выше, с
внутренними ссылками, ведущими в `/ru/`.

**3. Зарегистрируйте обе в навигации**

Добавьте `<section>/<slug>` в подходящую группу языкового блока `en` в `docs.json`, а
`ru/<section>/<slug>` — в соответствующую группу блока `ru`. Страница в одном языковом блоке и
отсутствующая в другом — самый частый способ рассинхронизировать это дерево.

**4. Поставьте на неё ссылку**

Добавьте ссылку хотя бы с одной страницы, на которую читатель правдоподобно попадёт первой.
Страница, на которую никто не ссылается, находится только поиском.

**5. Проверьте**

Запустите `make docs-check` и исправьте каждую сообщённую ссылку и каждый якорь до открытия
pull request.

Используйте Conventional Commit типа `docs:` с областью по участку — `docs(telemetry): document the
heartbeat payload`. Соглашения о коммитах и остальной процесс контрибуции — на странице
[Разработка](https://omniflow-vpn.mintlify.site/ru/contributing/development.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.