Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@
Формат — [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
версионирование — [SemVer](https://semver.org/lang/ru/).

## [3.3.0] — 2026-11-10

### Added

- **Единый дашборд `pegas status`** ([#9](https://github.com/netrunner714/pegas/issues/9), [#30](https://github.com/netrunner714/pegas/issues/30)) — одна таблица по всем проектам и сценариям машины: PROJECT / SERVICE / ENV / DRIVER / ENDPOINT / TARGET / GATEWAY / HEALTH / MEM / UP:
- контейнеры сопоставляются с маршрутами по меткам Pegas (`pegas.project`, `pegas.service`, `pegas.scenario`), а не по совпадению имён: одинаковые имена сервисов в разных проектах и сценарии с дефисом (`pre-prod`) разбираются корректно; алиасная ветка дополнительно сверяет имя проекта;
- проект текущего каталога помечается `★` (флаг `--cwd` проброшен в команду); здоровье host-сервисов проверяется реальным TCP-пробником, для docker — состоянием контейнера; проблемы собираются в итоговый блок `⚠️ Обнаружено проблем: N` с подсказкой по каждому сервису;
- JSON сохраняет обратную совместимость: рядом с новым `services[]` остаются legacy `projects[]` и добавляется `issues[]`.
- **Метаданные маршрута** ([#30](https://github.com/netrunner714/pegas/issues/30)) — перед блоком домена пишется комментарий `# pegas:project=… | service=… | env=… | driver=… | owner=… | port=…`: `connect` ставит `driver: bun, owner: connect`, `deploy` — `driver: docker, owner: deploy`. Merge и strip блоков (#24) сохраняют метаданные, а legacy-файлы без них по-прежнему разбираются выводом сервиса из домена/upstream и сценария из имени файла.
- **Аудит Next.js `allowedDevOrigins`** ([#28](https://github.com/netrunner714/pegas/issues/28)) — `pegas check` предупреждает, если dev-домены проекта на шлюзе (`*.<проект>.localhost`) не покрыты `allowedDevOrigins`, и печатает готовый рецепт. Рецепт — `['<проект>.localhost', '**.<проект>.localhost']`, а не `['*.localhost']` из текста issue: в Next.js `*` совпадает ровно с одной DNS-меткой, поэтому `*.localhost` не покрывает `<сервис>.<проект>.localhost`, а само наличие опции переводит проверку из warn в block и дало бы жёсткий 403 (проверено по исходникам Next.js 15.3.0). Добавлено руководство `docs/how-to/nextjs.md`.
- **Разделяемые инфраструктурные пулы** ([#29](https://github.com/netrunner714/pegas/issues/29)) — поля контракта `scope: "shared"` и `shared: { name, secret? }` для PostgreSQL/Redis-подобных образов:
- один инстанс пула на профиль образа и сценарий (`pegas-shared-postgres-18-development`) обслуживает несколько проектов: изоляция — отдельная база и роль (postgres) или префикс ключей (redis); имя арендатора объявляется в контракте (`shared.name`), а не выводится из имени проекта, поэтому переименование проекта не создаёт вторую базу;
- пулы не несут метку `pegas.project`, поэтому `pegas down` одного проекта не может удалить общую базу; `down` только снимает аренду проекта и останавливает опустевший инстанс, данные арендаторов не удаляются;
- `deploy` поднимает пул и готовит арендатора до `compose up`; shared-сервисы не попадают в compose-манифест, а `pegas://<сервис>` и `PEGAS_CONTEXT` указывают на алиас пула. Пароль суперпользователя — файл с правами 0600, монтируемый как `POSTGRES_PASSWORD_FILE`; пароль арендатора идёт в `psql` через stdin — секреты не попадают в argv;
- валидатор контракта и JSON-схема: shared запрещён с `bun`, `build`, `volumes` и `dependsOn`, требует поддерживаемый образ и `port`, а `shared.secret` обязан быть объявлен в `services.<name>.secrets`.

### Changed

- **Выравнивание таблиц** — ширина строк считается по кодпоинтам: эмодзи с Emoji_Presentation, модификаторы VS15/VS16 и широкие CJK-символы больше не ломают колонки дашборда (`★`, `⚠️`, `➖` и подобные).

## [3.2.0] — 2026-11-09

### Added
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ bunx @netrunner714/pegas init

`scenarios` — открытый список: имён может быть сколько угодно, имена произвольны (`^[a-z0-9-]+$`); три выше — конвенция, не ограничение. Каждая сценарная карта (runtime, port, domains…) обязана содержать ровно эти ключи.

Проверка: `pegas check` — рид-онли аудит (контракт, паритет секретов, git-гигиена, свежесть артефактов).
Проверка: `pegas check` — рид-онли аудит (контракт, паритет секретов, git-гигиена, свежесть артефактов,
dev-конфигурация Next.js на доменах `*.localhost`).

### 3. Локальная разработка с HTTPS

Expand Down Expand Up @@ -199,7 +200,7 @@ const key = secret("DATABASE_URL"); // секрет с понятной о
| Раздел | Содержание |
|---|---|
| [Быстрый старт](docs/tutorials/quickstart.md) | Пошагово от init до деплоя |
| [Руководства](docs/how-to/) | [init](docs/how-to/init.md), [локальный HTTPS](docs/how-to/local-https.md) |
| [Руководства](docs/how-to/) | [init](docs/how-to/init.md), [локальный HTTPS](docs/how-to/local-https.md), [Next.js](docs/how-to/nextjs.md) |
| [Справочники](docs/reference/) | [CLI](docs/reference/cli.md) (генерируется), [контракт](docs/reference/contract.md) |
| [Архитектура](docs/explanation/architecture.md) | Устройство и обоснование решений |
| [Контекст для агентов](llms.txt) | Полный контекст для ИИ-ассистентов |
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

* [Инициализация проекта](./how-to/init.md) — `pegas init`: артефакты для агентов, git-гигиена, канон docs/pegas.md.
* [Локальная разработка с HTTPS](./how-to/local-https.md) — `pegas connect`, доверенный CA, единый шлюз, `pegas disconnect`.
* [Next.js и домены шлюза](./how-to/nextjs.md) — `allowedDevOrigins`: HMR и dev-ресурсы Next.js на доменах `*.localhost`.

## Справочники

Expand Down
11 changes: 10 additions & 1 deletion docs/explanation/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,19 @@ upstream'ы маршрутов шлюза и значений `pegas://` — о
вектор повышения привилегий исключён.
- Дефолтные security-заголовки: HSTS, `X-Content-Type-Options: nosniff`,
`Referrer-Policy`.
- Локальный dev — user-space: без sudo, Caddy из `~/.pegas/bin`.
- Локальный dev — user-space: Caddy из `~/.pegas/bin`, порты `:80/:443`
привязываются без sudo (macOS/Windows; на Linux — при
`CAP_NET_BIND_SERVICE` или `net.ipv4.ip_unprivileged_port_start=0`).
- Ротация логов контейнеров (`max-size: 50m`, `max-file: 3`) — диск не
переполняется.

## Единственный Caddy

В Pegas нет отдельного «gateway-движка»: `pegas-gateway` — это тот же
user-space Caddy из `~/.pegas/bin`, запущенный как контейнер. Один бинарник,
одна модель конфигурации (JSON), один способ работы с сертификатами — и локально
(`pegas serve`/`pegas up`), и внутри шлюза.

## Ноль зависимостей

CLI собран целиком на стандартных модулях Node (`node:fs`,
Expand Down
24 changes: 24 additions & 0 deletions docs/how-to/local-https.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,30 @@

Pegas решает эту задачу с помощью **единого общего Docker Gateway** (`pegas-gateway`) и прозрачной привязки хостовых портов (`pegas connect`).

### Чем это НЕ является

* **Не туннель наружу.** `*.localhost` резолвится только на вашей машине: ни
публичного адреса, ни внешнего трафика. Если нужен URL из интернета
(`https://xyz.ngrok.io`, `*.ts.net`) — это задача ngrok / cloudflared /
Tailscale Funnel, и Pegas их не заменяет (но и не мешает).
* **Не прокси внутри проекта.** Ничего не запускается per-project: один gateway
на машину обслуживает все домены.
* **Не правка dev-скриптов.** Ни `--https` флагов, ни конфигов vite/webpack:
приложение слушает обычный HTTP, TLS терминируется снаружи.
* **Не root.** Caddy user-space, порты `:80/:443` без sudo (см.
`docs/explanation/architecture.md`).

### Сравнение с альтернативами

| Подход | Доверенный сертификат | URL из интернета | Налог на проект |
|---|---|---|---|
| `vite --https`, `node --experimental-https` | самоподписанный → предупреждение браузера | нет | флаг/конфиг в каждом проекте |
| mkcert | да (локальный CA) | нет | сертификаты вручную на каждый домен, прокси поднимать самому |
| Traefik + Docker socket | да | нет | `/var/run/docker.sock` внутри прокси = root-эквивалент |
| nginx + свои конфиги | вручную | нет | шаблон на сайт, reload при изменениях |
| ngrok / cloudflared / tailscale funnel | да (публичный CA) | **да** | внешний сервис, аккаунт, трафик через чужую сеть |
| **Pegas** (`up` / `serve` / `connect`) | да (локальный Root CA, один раз) | нет — по дизайну | ноль: один бинарник, один общий gateway |

---

## 1. Установка доверенного Root CA
Expand Down
130 changes: 130 additions & 0 deletions docs/how-to/nextjs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Next.js и домены `*.localhost` шлюза Pegas (`allowedDevOrigins`)

> How-to: настроить dev-сервер Next.js так, чтобы HMR и dev-ресурсы работали на
> доменах шлюза Pegas (`<project>.localhost`, `<service>.<project>.localhost`).

## Проблема

Pegas публикует локальные сервисы на доменах зоны `.localhost` через общий
шлюз (Caddy): `pegas connect` и `pegas deploy -s development` строят маршруты из
поля `services.<name>.domains` сценария `development`.

Next.js, начиная с версии 15.3 (и в backport-ветке 14.2.30), по умолчанию
защищает собственные dev-ресурсы (`/_next/*`, `/__nextjs*`) от обращений с
посторонних origin. Хост из заголовка `Origin` (для запросов без него — из
`Referer`) сравнивается со списком разрешённых; домен шлюза в этот список не
входит. Dev-сервер печатает предупреждение и/или отвечает `403` на HMR-сокете,
что выглядит как ошибка CORS на стороне шлюза:

```text
⚠ Blocked cross-origin request to Next.js dev resource /_next/webpack-hmr
from "site.my-app.localhost".
Cross-origin access to Next.js dev resources is blocked by default for safety.
To allow this host in development, add it to "allowedDevOrigins" in next.config.js
```

Причина не в маршрутизации Pegas, а в настройке самого Next.js: домен шлюза
нужно разрешить опцией `allowedDevOrigins`.

## Решение

Опция `allowedDevOrigins` — массив строк в конфигурации Next.js. Значения
сопоставляются только с hostname запроса: схему, порт и путь указывать не нужно.
Поддерживаются шаблоны: `*` заменяет ровно одну метку hostname, `**` — одну и
более, но только в начале шаблона.

Рекомендуемый набор значений для проекта Pegas — точное имя зоны и рекурсивный
шаблон на каждый `<project>.localhost`:

```ts
// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
allowedDevOrigins: ["my-app.localhost", "**.my-app.localhost"],
};

export default nextConfig;
```

```js
// next.config.js
module.exports = {
allowedDevOrigins: ["my-app.localhost", "**.my-app.localhost"],
};
```

```js
// next.config.mjs
export default {
allowedDevOrigins: ["my-app.localhost", "**.my-app.localhost"],
};
```

После изменения конфигурации dev-сервер Next.js перезапускают.

### Почему `*.localhost` недостаточно

В сообщении Next.js и в его документации встречается универсальный на первый взгляд
шаблон `['*.localhost']`. Он соответствует ровно одной метке перед `.localhost`,
то есть покрывает `<project>.localhost`, но не покрывает
`<service>.<project>.localhost`. Проект Pegas, у которого есть и корневой домен,
и домены сервисов, такой шаблон покрывает лишь частично.

Есть следствие, которое важно учитывать. В Next.js 14.2.30–16.1 режим реакции
зависит от того, задана ли опция вообще: пока `allowedDevOrigins` отсутствует,
dev-сервер ограничивается предупреждением и пропускает запрос; как только опция
появляется, непокрытый origin получает жёсткий ответ `403`. Начиная с 16.2
запрос с непокрытого origin блокируется всегда. Поэтому запись
`allowedDevOrigins: ['*.localhost']` в проекте с доменами вида
`<service>.<project>.localhost` превращает предупреждение в блокировку:
HMR перестаёт работать окончательно.

Точное имя зоны плюс рекурсивный шаблон (`"my-app.localhost", "**.my-app.localhost"`)
покрывают и корень зоны, и поддомены любого уровня. Этот набор работает на всех
версиях Next.js, где опция существует, включая ветку 14.2 (в ней шаблоны
`*.localhost` и `**.localhost` не сопоставляются вовсе, а точное имя и
`**.<project>.localhost` — работают).

В Next.js 16.3+ встроенный список разрешённых origin содержит `**.localhost`
вместо `*.localhost`, поэтому домены шлюза любой глубины разрешены без
дополнительной настройки.

## Поведение `pegas check`

`pegas check` инспектирует проект и выдаёт предупреждение (не ошибку), если:

- в `package.json` объявлен Next.js версии, поддерживающей `allowedDevOrigins`
(15.3.0+ или 14.2.30+), и
- в сценарии `development` контракта есть домены зоны `.localhost`, и
- файл `next.config.{js,mjs,ts,mts,cjs}` отсутствует, либо в нём нет ключа
`allowedDevOrigins`, либо его значение не покрывает dev-домены проекта.

Предупреждение содержит готовый список значений и ссылку на этот гайд. Проверка
учитывает встроенный в Next.js список разрешённых origin: `localhost` разрешён
всегда, `*.localhost` — в версиях до 16.2, `**.localhost` — с 16.3. Поэтому на
Next.js 16.3+ предупреждение о доменах `.localhost` не выдаётся: они покрыты
встроенным списком.

Если значение `allowedDevOrigins` вычисляется динамически (переменная, вызов
функции, spread, интерполяция), `pegas check` не может разобрать его
текстовым анализом. Вместо ложного предупреждения команда выдаёт подсказку
проверить покрытие доменов вручную.

Аудит работает только с корнем проекта. Монорепо, где приложение Next.js лежит
в подкаталоге (например, `apps/web`), не инспектируется: предупреждение в этом
случае не выдаётся.

## Версии Next.js без `allowedDevOrigins`

Опция `allowedDevOrigins` появилась в Next.js 15.3.0 и в ветке 14.2 начиная с
14.2.30. На более ранних версиях (15.0–15.2, 14.2.29 и ниже) защита dev-ресурсов
не реализована, опция отсутствует, и `pegas check` не выдаёт предупреждение о
ней. Наличие ключа `allowedDevOrigins` в конфигурации такой версии Next.js
отмечает как неизвестную опцию.

## Связанные материалы

* [Локальная разработка с HTTPS](./local-https.md) — `pegas connect`, доверенный CA, домены `*.localhost`.
* [Контракт pegas.json](../reference/contract.md) — поле `services.<name>.domains` и сценарные карты.
* Документация Next.js: `allowedDevOrigins` — <https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins>.
Loading