Клиент применения с открытым исходным кодом для баз данных схем. Запустите его на любой машине, у которой есть доступ к вашей собственной PostgreSQL, выполните сопряжение один раз — и он будет поддерживать эту базу данных синхронизированной с вашими обогащениями: загрузится из снимка, а затем применит живой поток дельт через единственное исходящее WebSocket-соединение. Строка подключения никогда не покидает эту машину.
Каждый оператор защищён по ревизии, поэтому повторно доставленный пакет сходится к тем же строкам. Ошибка SQL откатывает пакет и останавливает работу — «ядовитая» дельта никогда не пропускается молча.
Клиент получает состояние, а не операции: каждая дельта несёт полные текущие строки изменённой сущности в виде идемпотентного INSERT … ON CONFLICT … DO UPDATE, поэтому целевая база сходится, даже если пакет был пропущен.
Синхронизации базы данных можно потреблять несколькими способами — n8n, Make.com, MCP, сырые webhook'и или REST-поток дельт. Клиент синхронизации — полностью автоматизированный путь: меньше всего разрабатывать и меньше всего утечек.
Никакого сценария n8n, никакого cron, никакого связующего кода. Выполните сопряжение один раз — и клиент загружается из снимка, а затем применяет каждую дельту по мере поступления.
Строка подключения передаётся в командной строке или хранится локально с режимом 600 — она никогда не отправляется в Entity Enricher. Клиент устанавливает только исходящие соединения.
Каждая дельта — это идемпотентный upsert с защитой по ревизии. Если клиент завершается посреди пакета, пакет повторно доставляется после истечения аренды, и повторное применение сходится к тем же строкам.
Ошибка SQL откатывает пакет и сообщает о сбойной дельте. Сервер помещает весь пакет этого обогащения в карантин и заново отправляет очередь без него, поэтому клиент остаётся на связи и продолжает применять изменения — одна плохая строка не может застопорить всё, что идёт за ней, а работа в карантине остаётся в списке, пока вы ею не займётесь.
Сначала зарегистрируйте базу данных на схеме, затем выполните сопряжение клиента и запустите его на машине, у которой есть доступ к вашей базе данных.
На странице Database Sync зарегистрируйте базу данных для схемы, которую хотите зеркалировать, и проверьте её ключи базы данных. Полное описание модели см. в разделе Database Sync. На этом шаге объявляется целевой диалект, который применит клиент.
Вставьте это в терминал. Скрипт проверяет подпись cosign перед установкой.
curl -fsSL https://entityenricher.ai/install-eedatabase.sh | sh
Windows: iwr -useb https://entityenricher.ai/install-eedatabase.ps1 | iex. Или скачайте подписанный бинарный файл из раздела Releases, либо соберите из исходного кода (Go ≥ 1.23): go build -o ee-database .
Исходный код и подписанные релизы — на TOT-Concept/ee-database (MIT).
Запустите ee-database pair. Откроется вкладка браузера на /database/connect с коротким кодом — подтвердите его и выберите, какую базу данных должен синхронизировать этот клиент.
ee-database pair --server https://entityenricher.ai Open this URL in your browser to confirm pairing: https://entityenricher.ai/database/connect?code=7QX-KP2 Code: 7QX-KP2 Waiting for confirmation...
Предпочитаете токен? Выпустите его на странице Database Sync (Клиент синхронизации → Привязать клиент) и передайте напрямую: ee-database pair --server … <refresh-token>.
При первом запуске клиент загружает снимок .sql и применяет его, затем подключается и получает поток дельт. --save-dsn сохраняет строку подключения локально, поэтому последующие запуски не требуют аргументов.
Каждый запуск также самостоятельно проверяет права логина на подготовку (создание базы данных, DDL, DML) и сообщает результат на карточку клиента синхронизации, поэтому отсутствующее разрешение видно до того, как дельты не смогут примениться. Если ещё не опубликована связанная схема, клиент остаётся подключённым и ждёт — первая публикация запускает поток сама, перезапуск не нужен.
ee-database run --dsn "postgres://user:pass@localhost:5432/mydb" --save-dsn
«Рядом» означает сетевую близость, а не размещение на сервере базы данных: подойдёт любая машина или контейнер, способные достучаться до DSN, — включая облачный управляемый PostgreSQL (Azure, OVHcloud, AWS RDS…), который обычно требует TLS: …/mydb?sslmode=require.
Несколько баз данных на одной машине? Хост синхронизации поднимает процедуру привязки на уровень выше: привяжите машину один раз — и каждая синхронизация базы данных, которую вы ей назначаете, автоматически подхватывается, разворачивается и поддерживается в актуальном состоянии; регистрация новой синхронизации больше никогда не требует сеанса в терминале. Требуется клиент 1.5.0 или новее: он выполняет привязку один раз для каждого сервера, а не один раз для машины, — поэтому один хост может обслуживать сразу несколько экземпляров Entity Enricher.
На странице Database Sync нажмите кнопку Sync hosts на панели инструментов и добавьте хост с именем машины. Одноразовый токен сопряжения показывается ровно один раз — внутри готовой к копированию команды host pair с пошаговыми инструкциями по настройке.
Выполните команду на машине, которая имеет доступ к вашему серверу баз данных. --dsn — это базовая строка подключения, указывающая сервер, без имени базы данных: каждая назначенная синхронизация выводит из неё собственную базу данных. Как и любой DSN, она хранится локально в режиме mode-600 и никогда не отправляется в Entity Enricher.
ee-database host pair --server https://entityenricher.ai \ --dsn "postgres://user:pass@host:5432/" <token>
Привязка самостоятельно проверяет права логина на подготовку (создание базы данных, DDL, DML) и сразу завершается ошибкой при отсутствии разрешения. Предпочитаете логин с минимальными привилегиями? Добавьте --admin-dsn, и подготовка создаст каждую отсутствующую роль и базу данных через административное подключение — административный DSN используется только во время подготовки и никогда не сохраняется.
ee-database host run
Хост поддерживает один WebSocket плоскости управления и реагирует на назначения, сделанные в интерфейсе: выберите хост при регистрации базы данных или позже на вкладке Обзор базы данных. Каждая назначенная синхронизация захватывается, её база данных создаётся при отсутствии (имя в snake_case из имени синхронизации; переопределяется для каждой синхронизации через database_names в файле config.json хоста), затем синхронизируется обычным циклом ниже.
База данных, уже связанная с другим клиентом, отмечается и пропускается — она никогда не перехватывается. Отзыв хоста в интерфейсе мгновенно отключает машину, включая все учётные данные для каждой базы данных, которые она заявила; назначения и уже синхронизированные данные сохраняются, поэтому повторно связанный хост продолжает с того места, где остановился прежний.
Дельты покидают Entity Enricher через строгую очередь FIFO, отдельную для каждой базы данных. Сервер арендует видимое окно на 120 секунд и отправляет его одним пакетом; клиент применяет весь пакет в одной транзакции и отвечает ack , что продвигает курсор и немедленно запускает следующее окно. Если клиент прерывается посреди пакета, его защищают истечение аренды и повторная отправка на стороне сервера — ничего не теряется и не фиксируется дважды.
Первоначальная загрузка и установившийся режим используют один и тот же код. Пропустите первоначальную загрузку с помощью --skip-bootstrap, если ваша база данных уже заполнена.
Каждый оператор содержит _sync_revision, поэтому более старая строка никогда не перезаписывает более новую, даже при нарушении порядка.
Ошибка SQL откатывает пакет и сообщает о сбойной дельте вместе с полным проблемным запросом. Сервер помещает пакет этого обогащения в карантин и заново отправляет очередь без него — клиент продолжает применять остальное. Ненулевой код возврата даёт только сбой, в котором дельта не названа.
Каждое применённое окно сообщает форму записанных данных в разбивке по таблицам — поэтому для оценки объёма ночного повторного обогащения не придётся раскапывать логи по дельтам, которые уже подтверждены и удалены.
applying 12 delta(s) (10831 .. 10842) in one transaction applied 12 delta(s) in 84ms — 38 statement(s): mushroom 4 upserts, mushroom_common_names 12 upserts + 4 prunes, mushroom_human_uses 14 upserts + 4 prunes acked up to delta 10842
Один upsert — это одна строка, поэтому счётчики показывают количество строк; prune — это единственный DELETE с защитой по ревизии, который удаляет дочерние или связующие строки, больше не заявленные новой полезной нагрузкой. Дочерние записи согласуются на месте — их никогда не удаляют и не вставляют заново. Добавьте --verbose, чтобы получить по одной строке на дельту с типом сущности, фактическим временем и собственной формой записи.
Целевой диалект задаётся при регистрации схемы-базы данных в Entity Enricher — клиент применяет тот SQL, который формирует сервер. PostgreSQL — стартовый диалект; генераторы для MySQL / MariaDB, SQL Server и Oracle запланированы (драйвер MySQL уже включён). Применение нескольких инструкций обрабатывается на уровне драйвера (простой протокол pgx, multiStatements в MySQL).
Если целевая база данных уже существует, учётной записи нужны только CONNECT для базы данных и USAGE + CREATE для целевой схемы. (Начиная с PostgreSQL 15, public по умолчанию больше не выдаёт CREATE всем подряд.)
Всё остальное вытекает из владения: клиент сам создаёт таблицы реплики, поэтому владеет ими, а владение подразумевает права на чтение и запись, необходимые для дельт данных. Владение не опционально — поток также передаёт миграционные команды (ALTER TABLE …, CREATE INDEX …), которые PostgreSQL разрешает только владельцу таблицы, и никакая комбинация грантов его не заменит.
Если таблицы реплики уже существуют и принадлежат другому владельцу, предварительная проверка прав всё равно пройдёт — учётная запись может создавать новые таблицы, — но первая миграционная дельта завершится ошибкой. Передайте их командой ALTER TABLE … OWNER TO <login> (или включите учётную запись в роль-владельца), а не добавляйте права.
Запрос, который отвергает ваша база данных — обычная причина в том, что под новым уникальным индексом уже есть дубликат, — не останавливает поток. Пакет откатывается, клиент сообщает о сбойной дельте вместе с полным проблемным запросом (без обрезки), а сервер помещает пакет этого обогащения в карантин и заново отправляет очередь без него. Ваш клиент продолжает применять всё последующее.
Помещённая в карантин работа остаётся во вкладке Карантин страницы Database Sync, пока вы ею не займётесь: устраните причину в своей базе данных и выполните повторную отправку — она заново проецирует сущность из её текущего состояния, а не повторяет устаревший запрос, — либо отбросьте её, если строка больше не важна.
С неудавшимся bootstrap дело обстоит иначе: снимок — это одна транзакция, поэтому ничего не применяется частично, а клиент сохраняет его в каталог профиля сопряжения как snapshot-failed.sql (режим 0600, перезаписывается при каждой попытке, удаляется после следующего успеха), чтобы вы могли изучить или воспроизвести его через psql -f.
Клиент инициирует WebSocket по :443/wss. Хост вашей базы данных никогда не принимает входящие подключения — не нужно открывать порты и настраивать входящий трафик.
Учётные данные привязаны к одной синхронизации базы данных. Повторное сопряжение обновляет их и мгновенно разрывает предыдущее активное соединение.
365-дневный токен обновления (хранится с режимом 600) обменивается на 15-минутные токены доступа, которые аутентифицируют WebSocket. Отзыв в интерфейсе отключает активный клиент примерно за 1 секунду.
Управляемый хост подключается по короткому ключу eeh_…, а не по JWT: сервер хранит только его хеш, срок действия не истекает, и завершает его только отзыв хоста в интерфейсе.
Блокировка на уровне профиля не даёт двум процессам одновременно работать с одним сопряжением — иначе они бесконечно выбивали бы WebSocket-сессии друг друга.
Запускайте клиент под отдельной ролью, ограниченной синхронизируемой схемой, чтобы скомпрометированный токен не мог затронуть ничего другого, — но позвольте этой роли создать таблицы реплики, чтобы она стала их владельцем. Миграционным запросам нужно именно владение, а не выданные права.
| Команда | Что это делает |
|---|---|
| ee-database pair --server URL | Сопряжение по коду устройства с подтверждением в браузере. Выберите базу данных для синхронизации. |
| ee-database pair --server URL <token> | Сопряжение с помощью токена, выданного на странице Database Sync (подходит для headless-режима). |
| ee-database run --dsn DSN [--save-dsn] [--skip-bootstrap] | Выполните первоначальную загрузку из снимка (если не пропущена), затем подключитесь и примените дельты. |
| ee-database run … --create-missing | Сначала создавать целевую базу данных, если она не существует, используя учётные данные самого DSN (для postgres нужна привилегия CREATEDB, для mysql — CREATE). |
| ee-database run … --create-missing --admin-dsn DSN | Создайте всё, что указывает целевой DSN, через административное подключение: отсутствующую роль/пользователя (с паролем из DSN) и принадлежащую ей базу данных. После этого целевому DSN не требуются права на создание, а административный DSN нигде не сохраняется. |
| ee-database run --all | Синхронизировать все сопряжённые базы данных параллельно из одного процесса (для каждой нужен сохранённый DSN). |
| ee-database run … --verbose | Логировать форму записи и фактическое время для каждой дельты, а не только сводку по окну. Также принимается командой host run. |
| ee-database host pair --server URL --dsn BASE_DSN [--admin-dsn DSN] <token> | Выполните однократное сопряжение этой машины как управляемого хоста синхронизации: базовый DSN указывает на ваш сервер баз данных (без имени базы) и никогда не покидает машину; токен берётся из диалога Sync hosts (кнопка Sync hosts на панели инструментов страницы Database Sync). Одно сопряжение на сервер: можно параллельно сопрягать машину с несколькими серверами Entity Enricher. |
| ee-database host run [--server URL] | Управляемый режим: каждая синхронизация базы данных, назначенная этому хосту, захватывается, создаётся при отсутствии и автоматически поддерживается в актуальном состоянии — без отдельной привязки для каждой базы и сразу на всех привязанных серверах (--server ограничивает одним). Если база данных уже привязана к другому клиенту, об этом сообщается, но она никогда не перехватывается. |
| ee-database host status / host disconnect [--server URL] | Показать или удалить привязки этой машины к хостам. Отозвать привязку на стороне сервера можно в карточке «Хосты синхронизации». |
| ee-database status | Показать состояние сопряжения, URL сервера и сопряжённые базы данных. |
| ee-database disconnect | Удалить локальные учётные данные одного сопряжения. Отзыв на стороне сервера выполняется через интерфейс. |
| ee-database version | Версия для печати. |
Учётные данные хранятся с правами 600, по одному профилю на каждую сопряжённую базу данных, в каталоге ~/.config/ee-database/profiles/ — сопряжение выполняется один раз для каждой базы, а --database NAME выбирает нужную, если сопряжено несколько. Вообще не нужна автоматизация? Тот же поток доступен как обычный REST: GET /api/databases//changes, затем POST /api/databases//ack — см. Database Sync.
Клиент распространяется под лицензией MIT и находится в публичном репозитории, чтобы любой мог проверить, что именно выполняется в его базе данных.
Исходный код: github.com/TOT-Concept/ee-database
Релизы: github.com/TOT-Concept/ee-database/releases — каждый бинарный файл подписывается с помощью cosign перед публикацией.
Проверьте установщик: curl -fsSL https://entityenricher.ai/install-eedatabase.sh | less