Berth — кодовые агенты на удалённых машинах: worktree'ы, терминалы, оркестрация

· 4 мин чтения
coding-agents git-worktrees remote-access orchestration self-hosted
📂 Исходный код на GitHub

Открытая платформа для запуска кодовых агентов на собственных машинах для разработки: worktree'ы, терминалы, десктоп-приложение, оркестрация агентов, автоматизации по событиям и одинаковая настройка проектов на всех машинах. Написано на TypeScript и Go, лицензия MIT.

Berth — кодовые агенты на удалённых машинах: worktree'ы, терминалы, оркестрация

Berth: кодовые агенты на ваших машинах для разработки

Большинство кодовых агентов живут в терминале на вашем ноутбуке. Закрыли ноутбук — агент встал. Заснули ноутбук — агент встал. Это главная проблема: агент, который работает над задачей двадцать минут, должен пережить закрытие ноутбука.

Berth решает её просто. Он соединяет ноутбук с любым числом машин для разработки — VPS, облачная VM, домашний компьютер под столом — и даёт каждому репозиторию на них отдельные worktree'ы, терминалы, агентов и автоматизации. Агенты работают на самой машине. Приложение на ноутбуке — это окно, которое можно закрыть и открыть в любой момент.

В тексте ниже эти машины я называю машинами. В документации проекта для них есть и отдельное слово — «бокс», оно встречается в именах команд.

Что входит в проект

Часть Что делает
berthd Демон на каждой машине: worktree'ы, сессии агентов на tmux, сервисы, хуки, сценарии автоматизации
berth CLI и фоновый агент на ноутбуке: соединения, приватные адреса, API приложения
app/ Настольное приложение (Tauri и React)
plugins/ Встроенные плагины, написанные поверх packages/plugin-sdk
kits/ Указатели на наборы настроек, которые живут в своих репозиториях

berthd — один статический бинарный файл. На машине ему нужны только git и tmux. Всё остальное — терминалы, прокси, плагины — приложение приносит с собой.

Как всё устроено

Главный принцип простой: машина обслуживает, ноутбук подключается.

На машине живут репозитории, worktree'ы, сессии tmux и агенты, сервисы и публичные ссылки. На ноутбуке живут соединения, приватные адреса и очередь неотправленных промптов. Ничего, на что полагаются ваши коллеги, не живёт только на чужом ноутбуке.

Связь идёт по TLS 1.3 с взаимными сертификатами. Каждая сторона принимает только ключ второй стороны, никаких удостоверяющих центров. Поверх этого HTTP/2 мультиплексирует проверки связи, вызовы API, потоки TCP и терминалы в одном соединении на машину. По умолчанию berthd слушает адрес в tailnet — приватной сети, которую обычно даёт Tailscale — и отказывается стартовать, если такого адреса нет. Чтобы открыть доступ из интернета, нужно явно указать --listen 0.0.0.0:7444.

Три части, три роли

  • berthd работает на машине как пользовательский сервис systemd (на macOS — launchd). Хранит идентичность машины, список доверенных ноутбуков, а также worktree'ы, сессии, сервисы, хуки и сценарии. Локальные инструменты на машине ходят в него через приватный Unix-сокет с тем же API.
  • Агент berth работает на ноутбуке. Держит соединение с каждой машиной, открывает приватные адреса, пересылает события, выполняет локальные хуки и хранит очередь промптов для недоступных машин. С ним общаются и CLI, и приложение, поэтому закрытие приложения ничего не роняет.
  • Приложение — оболочка Tauri вокруг интерфейса на React. Оно обращается к агенту по локальному HTTP и WebSocket на 127.0.0.1:1378 с токеном. Приложение не держит ни соединений, ни состояния, которое жалко потерять.

Рабочие места на каждом worktree

Для каждого worktree Berth поднимает своё рабочее место:

  • терминалы через ghostty-web, с возможностью делить окно на части;
  • вкладки браузера на дев-сервер этого worktree;
  • приватные адреса вида http://checkout.shop.devl.localhost:1377/.

Все операционные системы считают *.localhost локальным адресом, поэтому настройка DNS не нужна. Прокси отдаёт приложению запрос так, будто тот пришёл на localhost:<port>, и переписывает обратно редиректы самого приложения. Дев-серверы из-за этого настраивать не нужно. Неизвестные адреса отклоняются — это блокирует DNS rebinding.

Агенты, которых видно и которыми можно управлять

Berth работает с Claude Code, Codex и другими агентами. В приложении есть доска kanban с теми, кому нужны вы, и входящие для готовой работы. Агенты видны не как чёрный ящик: у сессии есть экран, состояние и номер хода.

Управлять ими можно четырьмя командами, и все четыре работают одинаково из приложения, из CLI и через API:

Что сделать CLI API машины
Начать работу: worktree с агентом berth task new BOX/LOC/NAME --agent claude --prompt … POST /v1/tasks
Отправить промпт работающему агенту berth session send BOX/SESSION TEXT POST /v1/sessions/{name}/send
Дождаться конца хода berth session wait BOX/SESSION --turn ID GET /v1/turns/{id}/wait
Прогнать проверку там же, где код berth exec BOX/LOC[/WT] -- COMMAND POST /v1/exec

На самой машине демон berthd принимает те же команды без префикса BOX/. Именно так ими пользуются агенты, которые работают там же.

Отправка промпта возвращает номер хода, который она запустила:

berth session send devl/shop-checkout-claude "Rebase on main and fix any conflicts"
# Sent to shop-checkout-claude (turn shop-checkout-claude#4)
{ "sent": true, "turn": "shop-checkout-claude#4", "seq": 812, "at": "2026-10-04T09:12:03.411Z" }

У команды отправки есть несколько режимов. По умолчанию промпт уходит агенту сразу. Флаг --when idle ждёт, пока агент освободится, и только тогда отправляет промпт. --force отправляет промпт даже агенту, который ждёт человека — этот флаг для людей, а не для скриптов. --idem KEY делает повтор запроса безопасным: с тем же ключом вы получите уже созданный ход, и ничего не отправится второй раз. --wait ждёт конца хода. --queue не падает, если машина сейчас недоступна, а оставляет промпт на ноутбуке.

Агенты, которые управляют агентами

Когда агент запускает работу через Berth, машина запоминает, кто попросил. Когда работа закончилась, упала или вышла, машина записывает в сессию агента одно короткое сообщение. Сообщение попадёт туда, когда агент освободится.

В сообщении есть сессия, ветка, статус и время работы. Там же последние слова агента, список изменённых файлов с числом строк и подсказка, как посмотреть и влить ветку. Так агент заканчивает свой ход вместо того, чтобы крутить session wait, и подхватывает отчёт, когда тот придёт.

Цикл, ревью, передача работы и попытка нескольких способов решения оформляются как запуски с журналом. Выполняет их сама машина. Поэтому такой запуск продолжается, пока ноутбук спит, и возобновляется после перезапуска berthd.

Одинаковая настройка проектов везде

Файл .berth/config.json в репозитории описывает, как поднимать и убирать каждый worktree этого репозитория. Он едет вместе с кодом, поэтому на любой машине получается одно и то же.

{
  "setup": "pnpm install && createdb $BERTH_WORKTREE_SLUG",
  "archive": "dropdb --if-exists $BERTH_WORKTREE_SLUG",
  "ports": 2,
  "env": {
    "DATABASE_URL": "postgres://localhost/$BERTH_WORKTREE_SLUG",
    "NEXT_PUBLIC_WEBAPP_URL": "$BERTH_URL"
  },
  "services": [
    { "name": "web", "run": "pnpm dev --port $BERTH_PORT", "autostart": true },
    { "name": "worker", "run": "pnpm worker" }
  ],
  "agents": [
    { "id": "claude", "name": "Claude Code (Opus)", "command": "claude --model opus" }
  ],
  "hooks": [
    { "on": "worktree.created", "run": "cp ../shop/.env .env" }
  ],
  "flows": []
}

Каждый worktree получает свои порты, своё окружение, свои сервисы и свои хуки. Поле setup выполняется сразу после создания worktree, внутри него, через login-оболочку, максимум 30 минут. Поле check — это проверка, что всё получилось правильно, например pnpm test && pnpm lint. Если его не задать, Berth берёт то, что репозиторий уже предлагает: скрипт test из package.json, go test ./..., cargo test или цель test из Makefile.

Доверие к файлу репозитория

Этот файл по сути код. Его скрипты, сервисы, хуки, сценарии и команды агентов выполняются на машине. Поэтому машина не выполнит из него ничего, пока вы не доверите этот файл — здесь и для этого проекта. Доверие даётся на конкретные байты: машина хранит SHA-256 файла. Любое изменение в коммите снова требует доверия.

Пока доверия нет, применяется только поле ports, а приложение показывает, что именно собирается запустить. С терминала то же самое делает berth location config BOX/LOC с флагами --trust HASH и --untrust.

Простое правило: доверяйте репозиторию только тогда, когда доверяете всем, кто может в него коммитить.

Автоматизации

Автоматизации в Berth — это сценарии: когда что-то произошло, выполни такие-то шаги. Они исполняются на машине и не останавливаются, когда ноутбук спит.

{
  "id": "check-after-turn",
  "name": "Run tests after every agent turn",
  "enabled": true,
  "trigger": { "event": "agent.finished", "where": { "location": "shop" } },
  "steps": [
    { "id": "test", "kind": "run", "command": "pnpm test --changed", "timeout": "15m" },
    { "kind": "prompt", "when": "failure",
      "text": "The tests failed (exit {{prev.exit_code}}):\n\n{{steps.test.output}}\n\nFix them." },
    { "kind": "notify", "when": "success", "title": "{{worktree.name}} passes its tests" }
  ]
}

Сценарий работает только при "enabled": true. Триггером может быть событие, расписание или активность в GitHub. Шаги выполняются по успеху, по ошибке или всегда. Готовые шаблоны берут на себя типовые случаи: прогон тестов после каждого хода агента, уведомление, когда агенту нужен человек, ревью чужой работы, ночной ребейз с тестами, комментарии из pull request в агенту и сообщение в Slack, если настройка упала.

Сценарий может жить в четырёх местах. В файле ~/.berth/flows.json на машине — тогда он видит все события этой машины. В собственном конфиге проекта на этой же машине — только события одного репозитория. В закоммиченном .berth/config.json — тогда сценарий работает на всех машинах, где есть этот репозиторий. Или в наборе настроек проекта — тогда на всех машинах, куда набор применён.

Плагины и наборы настроек

Плагины — это React-модули для приложения. Встроенные плагины показывают изменения в git, pull request'ы, дев-серверы, состояние машин, активность и заметки. Свой плагин может добавить экраны, панели worktree, команды, элементы строки состояния и темы.

Наборы настроек (kits) упаковывают то, как поднимается проект, и переходят по ссылке. Пример — набор настроек для Cal.com. Он лежит в своём репозитории.

Безопасность

Главная мысль разработчиков: каждое соединение — между двумя ключами, которые уже встречались. Без сопряжённого ноутбука на машину нельзя попасть. Наружу тоже ничего не открыто, пока кто-то не попросит.

Кому и что доверено

Кто Что может Что это ограничивает
Сопряжённый ноутбук Всё, что может пользователь машины: запускать оболочки и агентов, выполнять команды, менять конфигурацию, обновлять berthd Сопряжение по одноразовому коду, закреплённые ключи, berthd revoke
Любой процесс под пользователем машины То же самое, через сокет berthd и ~/.berth Ничем сверх операционной системы: на той машине это вы
Кто может коммитить в репозиторий Ничего, пока вы не доверите его .berth/config.json на этой машине Доверие по хешу файла
Автор набора настроек Ничего, пока вы не примените то, что посмотрели Хеш, закреплённый за вашим просмотром
Автор плагина Всё, что умеет приложение, как только вы разрешите плагин Согласие пользователя, закреплённое за хешем кода
Тот, кто пишет данные события Только текст в сценариях, никогда не команды Значения подставляются через переменные окружения

Сопряжение

Команда berthd pair на машине создаёт случайный одноразовый код на 256 бит. Код живёт десять минут. Команда печатает ссылку berth://HOST:PORT?code=…&fp=…. Ноутбук подключается и закрепляет отпечаток машины. Дальше ноутбук доказывает, что у него есть код, не отправляя сам код: это HMAC с ключом из кода над экспортированным ключевым материалом TLS (RFC 5705) и отпечатком ноутбука. Доказательство привязано к этой сессии и к этому ключу. Машина тратит код под файловой блокировкой и закрепляет ключ ноутбука.

Команда berth add ssh HOST делает то же самое за одну SSH-сессию: определяет платформу машины, загружает подходящий berthd, ставит сервис и сопрягает. Установочный скрипт тоже работает на самой машине, так что ноутбуку SSH не нужен вовсе.

Публичные ссылки

Публичный доступ включается вручную и по одному порту: berth share BOX PORT. За ссылкой стоит быстрый туннель Cloudflare, поднятый на машине, поэтому ссылка переживает сон ноутбука. Ссылки заканчиваются, когда их отозвали или когда berthd остановился или обновился. Ни одна ссылка не живёт дольше демона, который ею управляет.

Секреты

В окружении проекта можно вместо значения указать ссылку на секрет: op://… для 1Password и env://… для переменной в окружении самого berthd. Значение читается, когда понадобится, и остаётся в памяти. Сессии и сервисы читают своё внутри своего процесса, так что значение никогда не попадает в командную строку. На диск, в лог, в событие или в API значение не записывается. В вебхуках, уведомлениях и сохранённом выводе шага секрет показывается как [secret].

Границы

Разработчики честно перечисляют то, что ещё не сделано:

  • Плагины не изолированы. Согласие решает, запустится плагин или нет, но не то, что ему доступно после запуска.
  • before:-хуки — это удобство, а не граница безопасности. Сопряжённый ноутбук и так может выполнять команды на машине.
  • Первый промпт попадает в командную строку агента, а обычные значения env проекта передаются в tmux как -e KEY=VALUE. Другие процессы на машине могут их увидеть, например через ps. Ссылки на секреты (op://, env://) так не видны — они раскрываются внутри сессии.
  • Архивы релизов не подписаны. checksums.txt доказывает, что файл скачался целиком, но не доказывает, кто его опубликовал. Подписанные суммы в планах.
  • Парный протокол и всё, что происходит до аутентификации, ещё не проходили независимый аудит.
  • Одна машина — один человек. Общая машина для команды с разными правами не поддержана.

Установка

На каждой машине, от имени того пользователя, под которым будут работать агенты:

curl -fsSL https://berthd.app/install | sh

Скрипт скачивает berthd из последнего релиза, сверяет его с контрольными суммами релиза и ставит для этого пользователя без root. Дальше он запускает демон как пользовательский сервис systemd (на macOS — launchd), ставит хуки, через которые Claude Code, Codex и Cursor сообщают своё состояние, и печатает ссылку для сопряжения. Ссылка работает один раз и десять минут. Повторный запуск обновляет на месте. Флаг --no-integrations пропускает хуки, а sh -s -- --help печатает список опций.

На Mac скачайте Berth — сборка универсальная, для Apple silicon и Intel, подписанная и нотарированная Apple. Перетащите в Applications и откройте. Приложение несёт с собой CLI berth и Linux-демоны, которые заливает berth add ssh. Обновление скачивается в фоне, а кнопка Restart to update в строке состояния или в Settings → About его применяет. Приложение никогда не перезапускается само, и перезапуск не останавливает ни одного агента.

Чтобы пользоваться berth в терминале на Mac, откройте Settings → General → Command line → Install — команда создаст ссылку ~/.local/bin/berth на копию внутри приложения, спросив разрешение. На Linux-ноутбуке или если нужен только CLI, скачайте архив с подходящим именем (linux-arm64, darwin-arm64 или darwin-amd64):

mkdir -p ~/.local/bin
curl -fsSL https://github.com/sean-brydon/berthd/releases/latest/download/berth-linux-amd64.tar.gz | tar -xz -C ~/.local/bin

Вставьте ссылку машины в приложение через Add a box или выполните:

berth pair 'berth://100.101.102.103:7444?code=…&fp=…'

Дальше добавьте проект и запустите агента:

berth add ssh me@my-box                    # или: поставить на машине по SSH и сопрячь
berth location add my-box/app ~/work/app
berth task new my-box/app/fix-login --agent claude --prompt "Fix the login redirect"

Команда berth help печатает все команды. Любой список можно получить в JSON, добавив --json.

Сборка из исходников

Нужны Go 1.27, Node 22, pnpm и Rust. Команда make all собирает berth и berthd в каталог bin/, а приложение использует bin/berth из этого же клона как агента ноутбука.

git clone https://github.com/sean-brydon/berthd
cd berthd && make all
cd app && pnpm install && pnpm tauri dev

make app-build собирает Berth.app и образ диска вместе с CLI и Linux-демонами внутри образа. Для разработки: make test запускает go vet и go test -race, pnpm -C app build проверяет типы и собирает приложение, pnpm -C docs-site dev поднимает сайт документации.

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

Сайт документации — docs.berthd.app. Исходники страниц лежат в папке docs/ репозитория.

Лицензия — MIT, файл LICENSE. Проект молодой: репозиторий создан в начале октября 2026 года, и список ограничений выше ещё будет меняться.

Источник: https://github.com/sean-brydon/berthd