# 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 ``` (тот же 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: 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 | `` | | 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