All checks were successful
Deploy Obsidian / deploy-obsidian (push) Successful in 1m59s
255 lines
11 KiB
Markdown
255 lines
11 KiB
Markdown
# 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 | `.forgejo/workflows/deploy.yml` | `.forgejo/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
|