# 06 — Хранение

**Русский** · [English](../06-storage.md)

Состояние iapeer лежит в двух местах: общий каталог хоста `~/.iapeer/` и каталог `.iapeer/` внутри рабочей папки каждого пира.

## Два уровня хранения

**Общий уровень** `~/.iapeer/` — всё, что касается хоста целиком: реестр пиров, состояние демона, общие настройки, установленные пакеты рантаймов.

**Уровень пира** `<папка-пира>/.iapeer/` — всё, что касается одного пира: его профиль, доктрина, состояние его плагинов, журналы его сессий.

Один и тот же плагин может хранить данные на обоих уровнях: общие — в `~/.iapeer/`, привязанные к конкретному пиру — в его папке.

## Раскладка общего уровня

```text
~/.iapeer/
├── IAPEER.md                  доктрина хоста (общая для всех пиров; слой 2 промпта)
├── peers-profiles.json        реестр всех пиров (пишется под блокировкой)
├── peers-profiles.lock        файл блокировки реестра
├── memory-provider.json       слот памяти: кто провайдер (если занят)
├── fragments/                 общие доктринальные фрагменты (слой 5 промпта)
├── peers/
│   └── <имя>/                 рабочая папка пира по умолчанию
│       └── .iapeer/           (раскладка уровня пира — см. ниже)
├── state/
│   ├── iapeer/                состояние демона (router.sock, router.json …)
│   │   └── attachments/       копии IAP-вложений, принадлежащие получателям
│   └── <плагин>/              изменяемое состояние плагина
├── logs/
│   ├── iapeer/                журналы демона: lifecycle.log, delivery.log, exits.log
│   └── <имя>/                 журналы инфраструктурного пира (по имени)
├── cache/
│   ├── iapeer/                кеш ядра
│   └── <плагин>/              кеш плагина (можно удалять)
├── docs/
│   ├── iapeer/                справочные доки фундамента (версия, установленная на хосте)
│   └── <пакет>/               доки каждого пакета экосистемы (копируются при его установке)
├── plugins/
│   ├── iap/                   установка ядрового плагина IAP
│   └── <плагин>/              установка плагина (неизменяемая)
└── runtimes/
    ├── claude/
    │   └── plugins/<плагин>/  конфиг плагина под рантаймом claude
    ├── codex/
    │   └── plugins/<плагин>/
    ├── telegram/
    │   └── runtime.json       манифест рантайм-пакета
    └── notifier/
        └── runtime.json
```

Несколько узлов стоит выделить:

- **`peers-profiles.json`** — реестр. Пишется только через единственного писателя под файловой блокировкой `peers-profiles.lock`; прямое редактирование запрещено на уровне ядра.
- **`state/iapeer/`** — здесь живёт демон: его локальный сокет `router.sock`, конфигурация `router.json` и inbox-каталоги `attachments/<получатель>/<sha256>/<имя-файла>`. Inbox — долгоживущее состояние получателя, а не кэш; удаление пира очищает его inbox.
- **`logs/<имя>/`** — журналы инфраструктурных пиров кладутся по имени пира на общем уровне (а не в папке пира), потому что это вывод службы под launchd.
- **`runtimes/<рантайм>/`** — двойное владение: корень принадлежит самому рантайму (Telegram держит здесь токены ботов, состояние опроса), а подпапка `plugins/<плагин>/` — это пространство плагинов, где лежит их конфиг под этим рантаймом.
- **`docs/<пакет>/`** — справочные доки на хосте, по одной папке на каждый пакет экосистемы (см. ниже).

## Доки экосистемы на хосте

`~/.iapeer/docs/<пакет>/` хранит копию справочных доков каждого пакета экосистемы, чтобы агент мог читать контракт офлайн по стабильному версионированному пути. Это нужно потому, что скомпилированный бинарь доков не несёт, а `docs/` из npm-тарбола выбрасывается после установки — ссылка во временный каталог установки или кеша умерла бы на первом же обновлении.

Конвенция — **копирование при установке, по пакету**: каждый пакет копирует свои публичные доки (исключая `internals/`) в свою папку `~/.iapeer/docs/<пакет>/` при каждой своей установке и обновлении, атомарной заменой, best-effort — сбой копирования никогда не валит установку. Поскольку копирование происходит в той же установке, что кладёт артефакт, доки пакета на хосте всегда соответствуют его установленной версии. Каждый пакет пишет только свою подпапку — та же изоляция владения, что и везде; имя папки — короткое unscoped-имя пакета (`iapeer`, `iapeer-memory`, `telegram-runtime`, `notifier-runtime`).

Системный промпт пира указывает сюда — `~/.iapeer/docs/`, по папке на пакет, начиная с `iapeer/README.md`, — чтобы вопрос «как iapeer делает X» отвечался из доков, а не угадывался.

## Раскладка уровня пира

```text
<папка-пира>/.iapeer/
├── peer-profile.json          профиль пира — источник истины о нём
├── IAPEER.md                  персональная доктрина (слой 2 промпта)
├── fragments/                 персональные доктринальные фрагменты (слой 5)
├── plugins/
│   ├── iap/
│   └── <плагин>/              данные плагина для этого пира
├── state/
│   ├── iap/
│   └── <плагин>/
├── logs/
│   ├── iap/
│   └── <плагин>/
├── cache/
│   ├── iap/
│   └── <плагин>/
└── runtimes/
    ├── claude/
    │   ├── launch.env         аргументы и переменные запуска для этого рантайма
    │   └── plugins/<плагин>/
    └── codex/
        ├── launch.env
        └── plugins/<плагин>/
```

- **`peer-profile.json`** — карточка пира и единственный источник истины о нём (поля разобраны в [03 — Пиры, рантаймы, идентичность](03-пиры-рантаймы-идентичность.md)).
- **`launch.env`** — файл вида `KEY=VALUE` для конкретной пары пир+рантайм: дополнительные аргументы запуска (`PEER_START_ARGS`) и переменные окружения. Так одному пиру можно задать особые флаги запуска, не трогая остальных. `create`/`init` скаффолдят его как скелет из одних комментариев для каждого агентского рантайм-скоупа (до правки ведёт себя ровно как отсутствие файла), поэтому массовые операции над launch.env не пропускают пира из-за отсутствия файла; существующий файл никогда не перезаписывается.

Рабочая папка пира не обязана лежать в `~/.iapeer/peers/`. По умолчанию `iapeer create` кладёт её туда, но пиром можно сделать любую папку командой `iapeer init` — тогда `.iapeer/` появится прямо в ней.

## Кто чем владеет

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

| Владелец | Что принадлежит |
|---|---|
| **Ядро (`iapeer`)** | `peers-profiles.json`, `state/iapeer/`, `logs/iapeer/`, `cache/iapeer/` |
| **Рантайм** | корень `runtimes/<рантайм>/` — кроме подпапки `plugins/` |
| **Плагин** | своя папка `<плагин>/` во всех категориях (`state`, `logs`, `cache`, `plugins`) на обоих уровнях |

Имя `iapeer` в категориях зарезервировано за ядром. Рантайм никогда не трогает `plugins/<плагин>/` внутри своего пространства, а плагин — корень рантайма.

## Классы данных по сроку жизни

Установка отделена от изменяемых данных — это важно для обновлений и для уборки:

- **Установка** (`plugins/<плагин>/`) — неизменяемая. Это разложенный код плагина; обновление заменяет его целиком.
- **Состояние** (`state/`) — изменяемые данные, которые нужно беречь: их потеря — потеря работы.
- **Журналы** (`logs/`) — вывод процессов; полезны для разбора, но не критичны. Что пишется в `lifecycle.log`, `delivery.log`, `exits.log` и как настроить их ротацию — [14 — Конфигурация и журналы](14-конфигурация-и-журналы.md).
- **Кеш** (`cache/`) — восстановимые данные; их можно удалить без последствий, система пересоздаст.

Такое разделение означает, что обновить плагин — значит заменить его папку установки, не задев его состояние; а почистить место — значит удалить кеш, не рискуя данными.

## Реестр как производная

Стоит повторить ключевой принцип хранения: **реестр `peers-profiles.json` — это проекция локальных профилей, а не отдельный источник истины.** Источник истины о пире — его `peer-profile.json`. Реестр можно пересобрать из профилей в любой момент командой `iapeer verify --fix`. При расхождении между реестром и профилем прав профиль. Поэтому потеря или порча реестра не фатальна — он восстанавливается обходом рабочих папок пиров.
