# Добавить источник (/ru/docs/build-add-source)

У вас есть сервис — календарь, приложение для встреч, хранилище файлов, — и вы хотите, чтобы его записи были в общем
хранилище WireCat и ими пользовались поиск и все остальные инструменты. Эта страница показывает шаги: выбрать сущность,
которую питают ваши записи, записать их как записи источников, дать cli-core их связать и не пустить внутрь секреты.
Она предполагает, что вы знаете [основы](/llms.mdx/docs/ru/build-concepts/content.md).

## Шаги [#шаги]

1. **Выберите сущность.** Решите, что владелец имеет в виду под вашими записями: человека, событие или документ. Ваши
   записи — записи источников этой сущности. Если ничего не подходит, это новая общая сущность, и она требует изменения
   в cli-core, а не своей таблицы.
2. **Пишите через cli-core.** Сохраняйте записи через области хранилища cli-core и никогда своим SQL
   ([WC-STORE-5](https://github.com/WireCatLabs/cli-base/blob/main/docs/standards/store.md#wc-store-5--a-cli-writes-no-sql-against-the-shared-store)).
   Если в области нет нужного метода, добавьте его в cli-core вместе с тестом.
3. **Ключ — id провайдера.** У каждой записи есть `account_id` и id провайдера в `external_id`. Повторное сохранение
   той же записи обновляет её, а не добавляет копию.
4. **Используйте стандартные имена.** Поле, по которому фильтруют, сортируют, соединяют или которое показывают в списке,
   — колонка со стандартным именем: `title`, `url`, `starts_at`, `ends_at`, `external_updated_at`. Время — в
   миллисекундах UTC. Всё остальное — в `metadata`, по списку разрешённых полей
   ([WC-DATA-4](https://github.com/WireCatLabs/cli-base/blob/main/docs/standards/data-model.md#wc-data-4--use-the-standard-field-names),
   [WC-DATA-5](https://github.com/WireCatLabs/cli-base/blob/main/docs/standards/data-model.md#wc-data-5--a-field-the-store-filters-sorts-joins-or-lists-on-is-a-column-the-rest-is-allow-listed-json)).
5. **Дайте сопоставлению их связать.** cli-core связывает вашу запись с сущностью по правилу: тот же ключ, общий id
   вроде `ical_uid` календаря, ссылка для входа или точный хеш содержимого. Два равных кандидата не дают связи.
6. **Мягко удаляйте то, что исчезло.** Когда провайдер перестаёт возвращать запись, она получает `deleted_at`. Её
   сущность и связи владельца остаются
   ([WC-DATA-7](https://github.com/WireCatLabs/cli-base/blob/main/docs/standards/data-model.md#wc-data-7--a-source-record-the-provider-stops-returning-is-soft-deleted-its-entity-and-links-stay)).

## Пример: встречи Zoom [#пример-встречи-zoom]

Инструмент `zm` уже сейчас так переносит встречи Zoom в хранилище:

* каждая встреча — строка `meetings`, запись источника, сохранённая через хранилище встреч cli-core;
* `account_id` — аккаунт Zoom, `external_id` — id встречи в Zoom, вместе они уникальны;
* `event_id` указывает на событие — сущность — и выставляется при связывании событий;
* участники — учётные записи, поэтому участник Zoom и тот же человек в Telegram могут сойтись в одном человеке.

Запись разговора, пришедшая позже, находит ту же строку по ключу и обновляет её.

## Никогда не хранить [#никогда-не-хранить]

* токены, пароли и ключи ([правила безопасности](https://github.com/WireCatLabs/cli-base/blob/main/docs/standards/security.md));
* коды доступа к встречам, PIN-коды и параметры запроса в ссылке для входа;
* сырой ответ провайдера; храните только разрешённые поля.

## Зарегистрируйте провайдера [#зарегистрируйте-провайдера]

При каждом запуске ваш инструмент записывает своего провайдера в хранилище: имя, вид, псевдонимы и команду. Тогда все
остальные инструменты узнают о новом источнике без выпуска cli-core. Строки провайдера, которого ни один инструмент не
записал, пропускаются с пояснением и никогда не ищутся как сообщения.

```ts
import { openStore } from "@wirecat/cli-core/store"

const store = await openStore()
await store.accounts.registerProvider({ name: "acme", kind: "meetings", aliases: ["acme"], command: "acme" })
```

`kind` — как поиск читает ваши записи: `messages`, `mail`, `notes`, `documents`, `meetings` или `events`.

## Что сломается, если это пропустить [#что-сломается-если-это-пропустить]

Без `external_id` каждая синхронизация добавляет ту же запись снова. Без мягкого удаления неудачная синхронизация стирает
то, что владелец связал или написал. Без общих областей хранилища следующая миграция cli-core ломает ваш инструмент.

Дальше: [Хранить свои данные](/llms.mdx/docs/ru/build-own-data/content.md).
