← Harvester

Sources

harvester · workzone

Что такое источник данных и как он подключается. Подключение — это заполненная админом модель источника: тип, адрес, учётные данные и scope. Дальше платформа проходит аутентификацию, прячет секреты в крипто-ядро, проверяет связку через Test Connection и начинает выборку только по объектам в scope. Дальше источник проходит свой жизненный цикл — пауза, отключение, удаление. Экран подключения живёт в Admin Panel.

Модель источника Админ заполняет при подключении.
connector_type Тип источника — Jira, Confluence, GitLab, Slack и другие. Выбирает коннектор из реестра; его манифест и задаёт, какие поля и какого вида учётные данные просит форма.
base URL Адрес инстанса источника — куда коннектор шлёт запросы.
credential Чем платформа представляется источнику: под кем (сервисная учётка или личный аккаунт) и чем (токен, в v2 OAuth). Зависит от connector_type. Подробности — ниже.
scope Какие объекты источника забираем — проекты, пространства, каналы. Режим и списки, заданные админом. См. scope.
Аутентификация

Вход в источник раскладывается на два независимых вопроса: под кем платформа ходит в источник и чем это доказывает. Оси ортогональны и комбинируются — сервисная учётка предъявляет свой токен.

Под кем — личность
Сервисная учётка
Отдельный технический read-only аккаунт под интеграцию. Права видны и отзываемы в одном месте, scope урезан до выбранного. Путь по умолчанию.
Личный аккаунт админа
Запасной путь для инстансов, где сервисную учётку завести нельзя или избыточно. Доступ привязан к человеку — годится для мелких и разовых источников, не как норма.
Чем — механизм
Статический токен
API-токен или personal access token, выпущенный для выбранной личности. Долгоживущий, вводится один раз — платформа прячет его в крипто-ядро.
OAuth v2
Авторизация через провайдера источника, без ручного ввода долгоживущего токена.

Токены и пароли сервисных учёток платформа хранит зашифрованными — оригинал нужен обратимо (с ним коннектор ходит в источник), поэтому это шифрование, не хэш. Ключ и алгоритм — не самодельные: ту же связку (AES-256-GCM), что шифрует пароль SMTP в модуле Email, даёт крипто-ядро Auth. Harvester своего хранилища секретов не заводит — кладёт секрет в поле sources.credential_enc и обращается к ядру за расшифровкой в момент запроса к источнику.

Test Connection

Перед первой выборкой связку проверяют по шагам — чтобы при ошибке сразу было видно, где именно она: в адресе, в кредах или в правах. Это тот же приём проверки доступности, что у провайдеров моделей и у Email (is_available()): не «работает / не работает», а понятная диагностика. Шаги «креды» и «права» — это метод check_connection() коннектора; шаг доступности URL — общий слой, одинаковый для любого типа, не дело коннектора.

1
URL доступен
Инстанс по base URL отвечает. Нет — адрес неверен или источник недоступен по сети.
2
Креды валидны
Источник принимает credential. Нет — токен истёк, отозван или сервисная учётка не та.
3
Прав достаточно
Учётка реально видит выбранные объекты. Нет — креды валидны, но read-доступа к проекту или каналу не хватает.
Каждый шаг возвращает свою ошибку: админ чинит ровно то звено, что оборвалось, а не гадает по общему «не удалось подключиться».

Та же проба работает на два триггера.
По требованию — админ жмёт при подключении и правке, со всеми тремя шагами.
По расписанию — платформа сама гоняет лёгкую версию (шаги 1–2: связь и валидность кред) между синхронизациями, отдельно от тяжёлого прогона. На провале выборки: проба роняет источник в health-состояние (error) и поднимает уведомление тем же каналом, что и провал прогона (видимость). Триггеры и итог пробы видны в блоке «Проверка соединения» карточки источника.

Scope

Scope — это режим, а не перечень, набитый руками. Как только креды приняты (Test Connection, шаг 2), коннектор строит каталог объектов источника — проекты, пространства, каналы — своим методом list_catalog(), и админ выбирает из живого списка на экране подключения. Без опечаток и угадывания ключей.

Режим scope
Всё
Берём источник целиком и подхватываем новые объекты автоматически; deny-list убирает лишнее — архивы, песочницы, личные пространства. Для крупных инстансов, где перечислять каждый объект — оверкил. Путь по умолчанию.
Только выбранное
Поимённый allow-list из каталога, с поиском. Новые объекты остаются снаружи, пока админ не добавит их сам. Для пилотов и точечных подключений.

Scope хранится как политика — режим плюс списки, не замороженный снимок: конкретные объекты платформа сверяет с каталогом на каждой синхронизации, иначе появившиеся и удалённые пространства разошлись бы с настройкой. Оговорка про права: чтобы собрать каталог, учётке нужно листать инстанс, даже когда выборка потом сужена. Scope — часть модели источника и граница, внутри которой работает выборка.

Механика выборки

Как коннектор достаёт данные из источника и крутит один цикл: метод листинга отдаёт страницу, коннектор передаёт её на вход конвейера и идёт за следующей по курсору. Сбой отдельного запроса — таймаут или 429 от источника — цикл не рвёт: повтор с backoff держит reliability.

listing Метод API источника, отдающий объекты постранично.
page Очередная страница объектов.
emit Объекты страницы идут потоком в конвейер.
есть следующий курсор → обратно к listing; нет → выборка завершена
Пагинация · cursor Источник отдаёт данные страницами; курсор — закладка на следующую. Коннектор листает до конца, не пытаясь забрать всё одним запросом.
Инкремент · fetch(since) Граница «забирать только изменённое после». При полном импорте since = epoch, при инкременте — момент прошлой синхронизации.
Webhooks Near-real-time: источник сам шлёт событие об изменении — это спусковой крючок, не новая логика (путь — ниже). Канал один на источник; endpoint и секрет админ заводит в настройках источника вручную (авто-провижн через API источника — v2).
Путь webhook-события
событие Источник шлёт вызов на endpoint: «объект изменился».
проверка входящих Подпись и анти-replay — security отсекает чужое.
инкремент-задача Событие становится задачей «подтянуть дельту».
конвейер Тот же конвейер, что и прогон по расписанию.

Конкуренция — между источниками: их прогоны независимы и идут параллельно (per-source). Внутри одного источника листинг последователен — курсор не даёт забежать вперёд, — но обработка объектов порядко-независима.

Те же API источника отдают и пользователей с правами доступа — но это отдельная тема: как identity и ACL переезжают в платформу, держит acl-identity.

Жизненный цикл источника

Подключённый источник живёт дольше одного прогона: его ставят на паузу, отключают, удаляют. За кнопками экрана стоят состояния и правила. аутентификация делит на «под кем» и «чем».

Состояние — что решил админ
Active
Подключён и синхронизируется по расписанию. Рабочее состояние.
Paused
Расписание на паузе на время простоя источника. Конфигурация и собранные данные целы, новые прогоны не запускаются; снимается Resume.
Disconnected
Креды сняты, прогоны остановлены, но конфиг-оболочка и данные сохранены. Источник переподключают, не настраивая заново, — для ротации доступа или временного вывода.
Здоровье — что наблюдает платформа
idle
Прогонов нет, источник ждёт следующего по расписанию.
syncing
Идёт прогон; его ход, прогресс и итог держит SyncRun.
error
Последний прогон или health-check завершился сбоем. Конфигурацию не блокирует — чинится повтором, провал виден и поднимает уведомление.

Оси независимы: active-источник бывает и idle, и syncing, а error говорит про последний прогон, не про намерение админа. Бейджи на экранах Admin Panel · Источники и Admin Panel · Синхронизация — отрисовка этих осей, не отдельная истина.

Во время прогона источник под замком. Пока он в syncing, операции над ним недоступны — правка конфигурации, пауза, отключение, удаление и повторный запуск: один источник — один активный прогон (single-flight per-source). Единственный доступный рычаг — Cancel.

Cancel безопасен и потому отката не требует. Прогон останавливается на ближайшем чекпоинте, уже обработанное лежит консистентно — Load идемпотентен по source_id + source_type + source_entity_id, полусобранного состояния не возникает. Откат к снимку был бы вреден и не нужен: данные самосогласуются идемпотентным upsert, Incremental и Reconciliation.

Pause при этом глушит расписание, а не замораживает живой прогон. Resume с чекпоинта лечит крах воркера в коротком окне, с бюджетом свежести; прогон, отменённый или приостановленный надолго, терминален — следующий стартует свежим, а не доедает стухшую выборку.

Удаление — выбор при подтверждении
Только конфигурация
Источник убран, его данные осиротевают, но остаются в Knowledge Store — на случай переподключения или если ими ещё пользуются.
Конфигурация + данные
За type-to-confirm: каскадно чистит данные источника из графа, векторов и метаданных. Необратимо — для полного вывода из эксплуатации.

Pause и Disconnect обратимы и данных не трогают; судьбу собранного решает только Delete. Чистка «конфигурация + данные» — явный жёсткий снос, в отличие от мягкого архивирования записи, исчезнувшей в самом источнике (она хранится для аудита). Сущность, которую питает ещё и другой источник, при этом не пропадает — отцепляется лишь вклад удаляемого; межисточниковую склейку и её границу держит data-model → Knowledge Store.