# Obsidian Self-hosted LiveSync (на CouchDB)

Single-tenant self-hosted vault на VPS. Multi-device sync через
[Self-hosted LiveSync plugin](https://github.com/vrtmrz/obsidian-livesync).

## Архитектура (split deploy)

Obsidian-стек **отделён** от основного приложения (backend/frontend/postgres/redis).
Каждый деплоится своим GHA workflow, никто не блокирует другого:

```
                   ┌────────────────────────────────────┐
[laptop / mobile]──┤  Caddy (main-stack)                │
                   │   обслуживает gendsgn.ru + api     │
                   │   + reverse_proxy obsidian.* →     │
                   │     ───shared network───┐          │
                   └─────────────────────────┼──────────┘
                                             │
                                             ▼
                   ┌──────────────────────────────────┐
                   │  CouchDB (obsidian-stack)        │
                   │   gendesign_shared network        │
                   │   volumes couchdb_data            │
                   └──────────────────────────────────┘
```

| | Main stack | Obsidian stack |
|---|---|---|
| Compose | `docker-compose.prod.yml` | `docker-compose.obsidian.yml` |
| Project | `gendesign` (`docker compose -p gendesign`) | `gendesign-obsidian` |
| Workflow | `.github/workflows/deploy.yml` | `.github/workflows/deploy-obsidian.yml` |
| Триггер push paths | `backend/**`, `frontend/**`, `Caddyfile`, main compose | `docker-compose.obsidian.yml`, `scripts/setup-couchdb.sh`, docs |
| Сервисы | backend, frontend, postgres, redis, worker, beat, caddy | couchdb |
| Восстанавливается отдельно | ✓ | ✓ |

**Связь**: внешняя docker-сеть `gendesign_shared` (создаётся вручную или
в первом деплое). Caddy в main-stack подключён к ней + к default —
маршрут `obsidian.gendsgn.ru` ходит в `couchdb:5984`.

При отсутствии obsidian-стека main работает без проблем (Caddy просто
вернёт 502 на obsidian-route).

## Deploy

### 1. DNS

A-запись:
```
obsidian.gendsgn.ru.   A   <VPS_IP>
```
(тот же IP что `gendsgn.ru`)

### 2. Secrets на VPS

Добавить в `backend/.env.runtime` (НЕ в репо!):
```bash
COUCHDB_USER=obsidian
COUCHDB_PASSWORD=<сгенерированный 32-байтный пароль>
```

Сгенерировать пароль:
```bash
openssl rand -base64 32
```

### 3. Первое создание shared network на VPS (один раз)

```bash
ssh gendesign
docker network create gendesign_shared
```

(автоматически создаётся в каждом из двух workflow если её нет, но
для первого ручного запуска удобнее самому).

### 4. Деплой

Изменения в `docker-compose.obsidian.yml` / `scripts/setup-couchdb.sh` /
`docs/obsidian-livesync.md` → GHA триггерит **`deploy-obsidian.yml`**
(не трогает main!).

Изменения в `backend/**`, `frontend/**`, `docker-compose.prod.yml`,
`Caddyfile` → триггерит **`deploy.yml`** (main).

Для ручного старта впервые:
```bash
git push   # любой из 2 workflow задеплоит свою часть
```

Или вручную с VPS:
```bash
cd /opt/gendesign
# main:
docker compose -p gendesign -f docker-compose.prod.yml up -d
# obsidian:
docker compose -p gendesign-obsidian -f docker-compose.obsidian.yml up -d
```

Caddy автоматически получит Let's Encrypt сертификат для `obsidian.gendsgn.ru`
при первом обращении (если DNS уже резолвится).

### 5. Bootstrap CouchDB (один раз, идемпотентный)

`deploy-obsidian.yml` запускает bootstrap автоматически после `up -d`.
Если нужно вручную:

```bash
cd /opt/gendesign
source backend/.env.runtime  # подгружаем COUCHDB_PASSWORD
COUCHDB_HOST=http://localhost:5984 \
COUCHDB_PASSWORD="$COUCHDB_PASSWORD" \
docker compose -p gendesign-obsidian -f docker-compose.obsidian.yml \
    exec -T couchdb bash -c "$(cat scripts/setup-couchdb.sh)"
```

Скрипт:
- конвертирует CouchDB в "configured single-node" mode
- создаёт database `gendesign-vault`
- включает CORS (нужно для мобильных Obsidian)
- увеличивает лимиты на размер документов (для крупных attachments)
- настраивает auth-timeout (для длинных sync-сессий)

Идемпотентен — повторный запуск безопасен.

### 5. Проверка работы

```bash
# из любой точки интернета:
curl https://obsidian.gendsgn.ru/_up
# → {"status":"ok",...}

curl -u obsidian:<password> https://obsidian.gendsgn.ru/gendesign-vault
# → {"db_name":"gendesign-vault","doc_count":0,...}
```

## Настройка клиента (Obsidian Desktop)

### 1. Установить Obsidian
- https://obsidian.md/download

### 2. Создать vault
- "Create new vault" → название "gendesign" → выбрать локальную папку

### 3. Установить Self-hosted LiveSync plugin
- Settings → Community plugins → "Browse"
- Search: "Self-hosted LiveSync" (автор: vrtmrz)
- Install → Enable

### 4. Настроить подключение

Settings → Self-hosted LiveSync → **Setup wizard** (рекомендуется первый раз):

| Поле | Значение |
|---|---|
| URI | `https://obsidian.gendsgn.ru` |
| Username | `obsidian` (или то что в `COUCHDB_USER`) |
| Password | `<COUCHDB_PASSWORD>` |
| Database name | `gendesign-vault` |
| End-to-End Encryption | **OFF** (см. ниже про trade-off) |

Затем:
- "Test Database Connection" → должно быть зелёное ✓
- "Check database configuration" → если требует — выполнить "Fix all"
- "Apply settings"
- "LiveSync" → "Enable LiveSync"

### 5. Первичный sync

При первом подключении vault пустой и в БД ничего нет. После первой
правки .md файла LiveSync запушит изменение в CouchDB.

С другого устройства/у другого пользователя:
- Та же установка
- При first sync клиент скачает всё содержимое БД в локальный vault

### 6. Mobile (iOS / Android)

- Установить **Obsidian** из App Store / Google Play
- Создать **пустой** vault
- Установить Self-hosted LiveSync (как на desktop)
- Те же настройки → первый sync скачает всё с сервера

⚠️ На iOS: включить **Background App Refresh** для Obsidian, иначе sync только при
открытом приложении.

## End-to-End Encryption (E2EE)

| | E2EE ON | E2EE OFF (default у нас) |
|---|---|---|
| Контент в БД | Зашифрован (admin сервера не может прочитать) | Plain text |
| Производительность | Тяжелее на клиенте | Быстрее |
| Claude (через MCP/HTTP) сможет писать извне? | Нет (нужен passphrase у Claude) | Да |
| Восстановление при потере passphrase | Невозможно | Возможно (через CouchDB admin) |

**Рекомендую OFF** — vault это рабочие заметки, не банковская тайна.
HTTPS + basic auth + закрытый DNS защищают от внешнего читателя.

Если позже потребуется E2EE — переключить в plugin, ребутнуть БД (replicate from scratch).

## Multi-user

CouchDB поддерживает per-user permissions, но для простого MVP:
- Один shared `gendesign-vault` database
- Все пользователи логинятся как `obsidian` (один аккаунт)

Для разделения (read-only для одних, write — для других) — нужно добавить
users в `_users` DB и настроить security object базы:
```bash
curl -X PUT -u obsidian:PWD https://obsidian.gendsgn.ru/_users/org.couchdb.user:viewer \
    -H "Content-Type: application/json" \
    -d '{"name":"viewer","password":"VIEWER_PWD","roles":[],"type":"user"}'
```

Затем в vault DB:
```json
PUT /gendesign-vault/_security
{
  "admins": {"names": ["obsidian"], "roles": []},
  "members": {"names": ["viewer"], "roles": []}
}
```

## Backup

```bash
# Snapshot DB → tar.gz на VPS (можно поставить в cron)
docker compose exec couchdb tar czf /tmp/vault-backup-$(date +%F).tar.gz /opt/couchdb/data
docker cp gendesign-couchdb:/tmp/vault-backup-*.tar.gz /opt/gendesign-backups/
```

Опционально — `_replicate` через CouchDB API на S3-совместимое хранилище.

## Troubleshooting

### `Failed to connect` в LiveSync
- Проверить DNS: `dig obsidian.gendsgn.ru`
- Проверить HTTPS: `curl https://obsidian.gendsgn.ru/_up` (должен вернуть `{"status":"ok"}`)
- Проверить пароль: `curl -u obsidian:PWD https://obsidian.gendsgn.ru/gendesign-vault`

### CORS errors на mobile
- Запустить `setup-couchdb.sh` ещё раз (или проверить через `_config/cors/origins`)

### Большие attachments не синкаются
- В LiveSync plugin: "Customization & advanced" → "Chunk size" = 100 KB
- На сервере: `couchdb/max_document_size` уже = 50 MB

### Конфликты при одновременной правке
- LiveSync создаёт conflict-маркер в файле
- Plugin показывает UI для merge ("Show conflict resolution dialog")

## Что дальше

После того как multi-device sync работает:
- Решить как Claude пишет в vault: MCP filesystem + bridge / самописный CouchDB-MCP / headless Obsidian
- Мигрировать существующий `memory-gendesign.jsonl` knowledge graph → .md файлы в vault
