Почему Cradle больше не десктоп: цена Electron для одного разработчика
Open Cradle начинался как десктопное приложение на Electron. Весь движок сидел внутри: node-llama-cpp для инференса, better-sqlite3 и sqlite-vec для хранения и векторного поиска, операторский triage с двухслойной оценкой рисков. Логика была прямая: данные не покидают машину оператора, потому что приложение и есть эта машина.
Сейчас это не так. Ниже — почему я отказался от Electron, во что он обходился и что пришло на замену.
Что изменилось в постановке
Первая версия отвечала на вопрос «как дать оператору локального ассистента». Текущая отвечает на другой: как поставить control layer между агентами, данными, инструментами и людьми. Формула, вокруг которой строится продукт:
LLM proposes → Evidence proves → Ontology constrains → Policy permits → Cradle executes + records.
Свой multi-agent framework я не строю. Агентов делают внешние инструменты, Cradle задаёт границы: что агент может предложить, что из предложенного разрешено, что в итоге исполнено и записано.
Честная оговорка про онтологии. Это направление архитектуры, а не работающая функция. Движок верификации есть, реестр верификаторов пока пуст. Если где-то прочитаете, что Cradle «ограничивает действия онтологией», это описание цели, а не текущего состояния.
Из новой постановки десктоп перестал следовать. Control layer — это сервер, а не окно на ноутбуке.
Что стоил Electron
Пока продукт был десктопом, издержки казались платой за локальность. Для одного разработчика они складывались так:
- Подпись и notarization под macOS. Каждый релиз — 3–10 минут ожидания ответа от notary service, и временами процесс просто зависает.
- macOS-runner в GitHub Actions считается с множителем 10× по минутам. Поэтому mac-сборка делалась локально, а Windows NSIS — в CI.
- Нативные модули пересобираются между ABI. Тесты бегут под Node, приложение — под Electron, у них разный ABI. better-sqlite3 и node-llama-cpp пересобирались туда и обратно; скрипт тестов до сих пор начинается с
pnpm rebuild better-sqlite3. - sqlite-vec — платформенные бинарники, свои для каждой ОС и архитектуры.
- main-процесс без hot reload. Любая правка в IPC или движке — перезапуск приложения.
- Три платформы = три релизных конвейера, у каждого своя подпись, свой инсталлятор и свои способы упасть.
Ни один пункт сам по себе не фатален. Вместе они съедали время, которого у одного человека на ранней стадии продукта нет.
Что вместо
cradle-server — headless-процесс. Ставится локально или на GPU-сервер. UI — веб-консоль: тот же код интерфейса, который жил в рендерере Electron, но поверх HttpBackend вместо IPC-моста.
Ключевой ход — реестр RPC-хендлеров. Тело хендлера пишется один раз. В десктопе его привязывает к IPC функция bindRpcToIpc, на сервере — маршрут POST /api/v1/rpc/:channel. События, которые раньше летели через webContents.send, теперь идут по SSE.
// One handler body, two transports.
registerRpc('agents:list', {
handler: (_args, ctx) => listAgents(ctx.projectId),
scopes: ['agents:read']
})
// Desktop: ipcMain.handle('agents:list', ...) — via bindRpcToIpc('agents')
// Server: POST /api/v1/rpc/agents:list — via registerRpcRoutes(server)
// Events: webContents.send(...) — now SSE on GET /api/v1/events
Маршрут сначала проверяет scopes ключа и только потом говорит, существует ли канал: анонимный вызов всегда получает 401, а не подсказку о составе API.
API-ключ точки хранится только в браузере. Консоль кладёт его в localStorage, в учётной записи остаются имя и URL точки. Сервер, на котором крутится консоль, ключей к вашим cradle-server не видит.
Полностью локальный режим
pnpm console:local
Одна команда поднимает сайт с консолью на localhost:3000 и cradle-server на :31416. Без Firebase, без почты, без внешних сервисов: код входа печатается в терминале, админский API-ключ — в логе сервера при первом старте. Это и есть режим «всё на одной машине», с которого десктоп когда-то начинался, только без Electron.
Три вида точек
В настройках консоли переключаются точки, к которым она подключена:
- Локальная машина — cradle-server на ноутбуке, модели GGUF.
- Свой GPU-сервер — тот же cradle-server, вынесенный на железо в контуре.
- Тестовый EC2 worker с моделями Bedrock — только для экспериментов. GPU-инстанс в EC2 оказался слишком дорогим, а Bedrock позволяет гонять сценарии без своего железа. Для закрытого контура этот вариант не подходит по определению.
Ограничения
- Electron из репозитория ещё не удалён. Forge, preload и IPC-привязки живут рядом с сервером, пока консоль не достигнет паритета по функциям.
- Сайт на https не может ходить на
http://<ip>: браузер режет mixed content.http://localhostбраузеры пускают, а удалённой точке нужен TLS. - Онтологии — см. выше. Направление, не функция.
Источники
- Electron, Code Signing — требования к подписи и notarization под macOS.
- Electron, Native Node Modules — почему нативные модули пересобираются под ABI Electron.
- GitHub Docs, About billing for GitHub Actions — множители минут для Windows и macOS runner'ов.