Календарь как личная инфраструктура

Календарь как личная инфраструктура

Я тут задумался по поводу календаря.

Много лет использую гугловский. Немного пытался использовать календарь от Microsoft, но он в итоге всё равно интегрировался с гугловским. Как и эпловский. То есть как ни крути, всё равно получается один центр тяжести — Google.

И вроде всё работает, интеграций море. «Созвон по ссылке», «приглашение на встречу», «напоминание» — всё привычно и понятно.

Но вот наступила эпоха деглобализации, а значит, есть вероятность, что по политическим причинам меня, как и других жителей РФ, лишат доступа к гуглосервисам. Не сам Google заблокирует, так Роскомнадзор подсуетится. И в связи с этим появились опасения.

Не такие, которые доводили бы до стрессового состояния. Скорее конструктивные: «хм, неплохо бы подстелить соломки». Чтобы не зависеть ни от доброй воли ИТ-гигантов, ни от непрошенной заботы со стороны родного государства.

А ещё есть второй слой, менее драматичный, но более личный: я вообще-то не хочу делиться с третьими лицами своим расписанием. Всё-таки это штука часто довольно интимная. Календарь — это же не только встречи и звонки. Это лог жизни: где был, куда иду, что планирую, когда «дырки», когда занят, когда нет. И совсем не улыбается, чтобы какой-нибудь Google или Яндекс обучали свои языковые модели на моих конфиденциальных данных.

Да, Яндекс-календарь я уже попробовал, конечно. Он работает и в целом его вполне можно было бы применять — если бы не сомнения в конфиденциальности. Ну и интеграций с ним я пока не наблюдаю. Все заточены на Google.

Поиск решения: от очевидного к реальному

Что же остаётся?

Первая мысль — заселфхостить какой-нибудь NextCloud и получить всё в одном. Логично. Но останавливает отсутствие адекватных предложений VDS в РФ. А хостить за рубежом, как показывает личный опыт — дело неблагодарное: заслуживший всеобщую нелюбовь регулятор зачем-то ограничивает скорость доступа к зарубежным хостингам до околонулевой, причём даже к тем, на которых нет никаких запрещённых ресурсов. ТСПУ делает своё дело: подключение к зарубежному дедику иногда напоминает работу через dial-up.

Есть ещё вариант разместить сервер дома, и он в целом норм, но вот только есть ряд важных моментов:

  • стабильность электропитания (отключения, как ни печально, случаются);
  • охлаждение (летом в квартире и так жарко);
  • шум (не хочу слушать вентиляторы круглосуточно);
  • белый IP (у меня он есть, к счастью).

Ну и всё техобслуживание и сопровождение этого сервера ложится на мои пусть не хрупкие, но довольно нагруженные множеством задач плечи.

И вот тут интересный момент: чем дольше об этом думаешь, тем яснее становится, что календарь — это не «приложение». Это элемент личной инфраструктуры. Такой же, как электричество, интернет и батарейка в телефоне.

Если он есть — живёшь. Если его внезапно нет — удивляешься, насколько много завязано на эту маленькую штуку.

В чём настоящая задача — и почему «просто переехать» не получается

Проблема не в том, что «нет календарей». Календарей море. Проблема в том, что календарь без экосистемы — это как телефон без связи.

Календарь — это:

  • якорь для задач, встреч, дедлайнов;
  • напоминания (без них всё рассыпается);
  • интеграция с почтой (инвайты), мессенджерами, видеозвонками;
  • и — самое неприятное — метаданные о жизни.

И поэтому выбор часто выглядит так:

  • либо «удобно, но я — продукт и заложник рубильника»;
  • либо «автономно, но это превращается во вторую работу».

А мне, если честно, хочется третий путь: чтобы было надёжно и достаточно приватно, но без героизма. Без ощущения, что я теперь админ собственного дата-центра, а календарь стал pet-проектом на полгода.

После долгих раздумий и изучения вопроса я сформулировал два адекватных решения.

Решение №1. Прагматичное: остаюсь на Google, но делаю себе аварийный выход

Это подход для тех, кто не хочет превращать переезд в эпопею, но хочет одного: не умереть вместе с сервисом, если его отрубят.

Суть: не «уходим навсегда», а делаем так, чтобы в момент, когда Google внезапно станет недоступен, не остаться с пустым экраном.

Как это выглядит на практике.

1) Регулярный экспорт и резервные копии

Не «однажды сохранил и забыл», а привычка: раз в неделю или раз в месяц выгрузка календаря в формате .ics (или нескольких календарей, если они разделены) в место, которое контролируешь сам.

Даже банально — в локальную папку на маке плюс резервная копия на внешний диск. Уже лучше, чем ничего.

Смысл здесь простой: в худшем случае хотя бы не теряется история и планы.

Экспорт можно делать руками через Google Takeout, но это быстро надоедает. Дальше в этом посте будет скрипт, который делает это автоматически.

2) Локальный «аварийный» календарь, который живёт без облака

Нужен способ открыть своё расписание, даже если облако недоступно. На macOS это решается банально: стандартный Calendar.app умеет хранить локальные календари и импортировать .ics. На Android — аналогично, чтобы можно было быстро импортировать файл и жить дальше.

То есть задача не «идеально синхронизироваться», а не остаться без расписания.

3) Разделить «личное» и «сервисное»

Самый токсичный канал утечки — это приглашения, корпоративные созвоны, всякие интеграции. Они по умолчанию тянут в Google.

Поэтому можно сделать очень простую и полезную вещь: держать личные события (врачи, семья, здоровье, бытовые планы, то, что реально интимно) в отдельном календаре, который не участвует ни в каких интеграциях и вообще нигде не светится.

А «рабочие созвоны и внешние приглашения» пусть живут там, где им удобно.

Так снижается объём «интимного лога», который отдаётся наружу. Рабочие встречи пусть знает Google — там и так всё корпоративное. А вот когда я иду к врачу или встречаюсь с семьёй — это не его дело.

4) Резервный провайдер «на случай пожара»

Это не обязательно «переезд». Это страховка: иметь вторую площадку, куда можно быстро «перелить» расписание и продолжить жить.

Идея здесь простая, как аптечка в машине: пусть лежит — лучше пусть никогда не пригодится. Но если понадобится, не придётся в этот момент метаться.

Плюсы решения №1: минимум усилий, максимум практической пользы, почти нет риска превратить всё в долгострой.

Минусы: это не полный уход от зависимости и не абсолютная приватность. Это «умный компромисс».

Решение №2. Автономия без фанатизма: отдельный сервер CalDAV/CardDAV вместо комбайна Nextcloud

Если хочется действительно снять зависимость, но без «домашнего сервера с вентиляторами, бесперебойником и нервным тиком», то главный трюк вот какой:

Не пытаться решать всё Nextcloud’ом.

Nextcloud — классная штука, но это комбайн: файлы, фоточки, офис, совместная работа, пользователи, права, обновления, плагины… и всё это ради календаря и контактов выглядит как оверкилл. У меня уже есть файлы, заметки (Obsidian), офис (ну, вот он, локально стоит). Зачем мне ещё один слой абстракции?

А календарь и контакты решаются отдельно и намного легче:

  • CalDAV — для календарей;
  • CardDAV — для контактов.

Это открытые стандарты. Их понимают macOS, iOS и Android (на Android иногда нужен клиент-синхронизатор типа DAVx⁵, но это решается за пять минут). Плюс сохраняется самая важная вещь: независимость от конкретного приложения. Если клиент сменился — сервер остался. Если телефон поменялся — календарь остался.

У решения №2 два подварианта:

Вариант 2A: управляемый хостинг (платишь деньгами, экономишь голову)

В теории можно взять сервис, где CalDAV/CardDAV уже поднят, обновляется, бэкапится — и заниматься только пользованием.

Но тут есть проблема: большинство privacy-ориентированных сервисов заблокированы в РФ:

  • Proton — заблокирован РКН с 2019–2020
  • Fastmail — заблокирован с 2021 (отказались выполнять требования о локализации данных)
  • mailbox.org — был в процессе блокировки, статус неопределённый
  • Tutanota — заблокирован

То есть как «аварийный выход» на случай отключения Google — эти сервисы не годятся. Они уже требуют VPN для доступа, а значит, добавляют ещё одну точку отказа вместо того, чтобы её убирать.

Остаются либо российские сервисы (Яндекс, Mail.ru) — но там те же вопросы к приватности, что и у Google, плюс добавляется риск «внутренней» блокировки. Либо — свой сервер.

Вариант 2B: лёгкий VPS + минимальная поддержка

Если хочется контролировать больше и платить меньше, можно поднять лёгкий сервер на VPS. Но именно лёгкий, без «платформы для всего». С автообновлениями, с нормальными бэкапами, с минимальной площадью атаки.

И важный момент: не нужен «идеальный VPS в РФ». Нужен вариант, который доступен стабильно и не превращается в черепаху по скорости. Иногда это локальный хостинг, иногда — ближнее зарубежье с хорошим пирингом (Финляндия, Казахстан, Армения — у кого что лучше работает).

Плюсы решения №2: настоящая независимость, приватность, стандарты, переносимость между устройствами, меньше «рубильников».

Минусы: всё равно есть элемент ответственности и настройки. Не огромный, но не нулевой.

Легковесные серверы для CalDAV/CardDAV: что реально работает

Вот мы и добрались до конкретики. Какие есть лёгкие решения, которые можно поднять за вечер и не тратить на них потом нервы?

Radicale — минимализм в чистом виде

Radicale — это Python-сервер для CalDAV и CardDAV, который весит почти ничего и делает ровно одну вещь: хранит календари и контакты. Никаких веб-интерфейсов для редактирования (хотя есть простой веб-интерфейс для просмотра), никаких пользовательских порталов. Просто сервер, который отвечает на запросы по стандарту.

Плюсы:

  • Установка в одну команду (pip install radicale)
  • Конфигурация — один файл
  • Потребляет считанные мегабайты RAM
  • Данные хранятся в обычных файлах (легко бэкапить, можно даже в Git положить)
  • Работает за reverse proxy (nginx, Caddy)
  • Поддерживает несколько пользователей

Минусы:

  • Нет красивого веб-интерфейса для редактирования событий (нужен клиент)
  • Нужно самому настраивать HTTPS (но с Caddy это тривиально)
  • Нет встроенного механизма напоминаний (напоминания — дело клиента)

Baikal — если хочется веб-интерфейс

Baikal — PHP-сервер, тоже лёгкий, но с веб-интерфейсом для администрирования. Можно создавать пользователей, календари и адресные книги через браузер.

Плюсы:

  • Веб-интерфейс для управления
  • Чуть более «законченный» продукт
  • Данные в SQLite (один файл) или MySQL

Минусы:

  • Нужен PHP (а значит, чуть больше настройки)
  • Немного тяжелее Radicale
  • Веб-интерфейс — только для администрирования, не для редактирования событий

Xandikos — для тех, кто любит Git

Xandikos — ещё один Python-сервер, который хранит данные в Git-репозитории. Это значит, что вся история изменений сохраняется, и можно откатиться на любой момент.

Плюсы:

  • История изменений из коробки
  • Можно пушить в удалённый Git-репозиторий для бэкапа
  • Лёгкий

Минусы:

  • Более экзотический выбор
  • Меньше документации

Что выбрать?

Для большинства случаев я бы рекомендовал Radicale:

  1. Минимум зависимостей (только Python)
  2. Минимум потребления ресурсов
  3. Данные в plain-text файлах (легко понять, что происходит)
  4. Достаточно зрелый проект

Если хочется кнопочки в браузере для создания пользователей — Baikal.

Если хочется гиковского контроля над историей — Xandikos.

Caddy: современный веб-сервер с автоматическим HTTPS

Прежде чем переходить к настройке Radicale, стоит сказать пару слов о Caddy — потому что именно он превращает «голый сервер на localhost» в нормальный защищённый сервис.

Caddy — это веб-сервер, написанный на Go. Его главная особенность: автоматическое получение и обновление SSL-сертификатов от Let’s Encrypt. То есть не нужно возиться с certbot, cron-задачами для обновления, ручной конфигурацией — Caddy делает всё сам.

Почему Caddy, а не nginx:

  • Конфигурация в разы проще. Вместо десятков строк nginx-конфига — буквально три строчки.
  • HTTPS из коробки. Просто указываем домен — и Caddy сам получает сертификат.
  • Меньше шансов ошибиться. Меньше настроек — меньше дыр в безопасности.

Установка на Ubuntu/Debian:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

После установки Caddy автоматически запускается как systemd-сервис.

Минимальная конфигурация для проксирования Radicale (/etc/caddy/Caddyfile):

cal.example.com {
    reverse_proxy localhost:5232
}

Это всё. Три строки. Caddy сам:

  • Получит сертификат для cal.example.com
  • Настроит редирект с HTTP на HTTPS
  • Будет автоматически обновлять сертификат

После изменения конфига:

sudo systemctl reload caddy

Настройка Radicale: пошаговый рецепт

Допустим, есть VPS с Ubuntu 22.04. Вот минимальный рецепт для production-ready установки.

1. Установка зависимостей

sudo apt update
sudo apt install python3-pip python3-bcrypt apache2-utils
pip3 install --user radicale

(Caddy устанавливается отдельно — см. раздел выше.)

2. Создание структуры директорий и конфига

sudo mkdir -p /var/lib/radicale/collections
sudo mkdir -p /etc/radicale

# Конфигурация Radicale
sudo tee /etc/radicale/config << 'EOF'
[server]
hosts = 127.0.0.1:5232

[auth]
type = htpasswd
htpasswd_filename = /etc/radicale/users
htpasswd_encryption = bcrypt

[storage]
filesystem_folder = /var/lib/radicale/collections

[logging]
level = warning
mask_passwords = True
EOF

# Создание пользователя для доступа к календарю
sudo htpasswd -B -c /etc/radicale/users myuser
# Введите пароль при запросе

3. Systemd-юнит для автозапуска

sudo tee /etc/systemd/system/radicale.service << 'EOF'
[Unit]
Description=Radicale CalDAV/CardDAV server
After=network.target

[Service]
Type=simple
User=radicale
Group=radicale
ExecStart=/home/myuser/.local/bin/radicale --config /etc/radicale/config
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

# Создание системного пользователя (без домашней директории, без shell)
sudo useradd -r -s /bin/false radicale
sudo chown -R radicale:radicale /var/lib/radicale /etc/radicale

# Запуск
sudo systemctl daemon-reload
sudo systemctl enable radicale
sudo systemctl start radicale

4. Настройка Caddy для HTTPS

sudo tee /etc/caddy/Caddyfile << 'EOF'
cal.example.com {
    reverse_proxy localhost:5232
}
EOF

sudo systemctl reload caddy

Caddy автоматически получит сертификат от Let’s Encrypt (при условии, что DNS для cal.example.com указывает на этот сервер). Всё, сервер готов.

URL для подключения клиентов: https://cal.example.com/myuser/

Настройка клиентов

macOS / iOS

  1. Открыть «Системные настройки» → «Интернет-аккаунты»
  2. Нажать «Добавить другую учётную запись…»
  3. Выбрать «Учётная запись CalDAV» (или CardDAV для контактов)
  4. Ввести:
    • Тип учётной записи: «Вручную»
    • Имя пользователя: myuser
    • Пароль: тот, что создали в htpasswd
    • Адрес сервера: cal.example.com

Android

На Android стандартный календарь обычно не умеет CalDAV напрямую. Нужен синхронизатор:

  1. Установить DAVx⁵ из F-Droid или Google Play
  2. Добавить аккаунт, ввести URL: https://cal.example.com/myuser/
  3. Ввести логин и пароль
  4. DAVx⁵ будет синхронизировать календарь и контакты со стандартными приложениями Android

Автоматическая синхронизация с Google

А теперь самое интересное: как не терять данные из Google и при этом иметь их копию на своём сервере?

Есть два подхода:

  1. Односторонняя синхронизация — периодически копировать данные из Google к себе (бэкап)
  2. Двусторонняя синхронизация — изменения в обе стороны (сложнее, больше риск конфликтов)

Для большинства случаев достаточно первого варианта.

Подготовка: получение доступа к Google API

Для работы скриптов понадобится создать проект в Google Cloud Console и получить credentials. Это делается один раз, но шагов довольно много.

Создание проекта и включение API:

  1. Зайти на https://console.cloud.google.com/
  2. Создать новый проект (или использовать существующий)
  3. В боковом меню: APIs & Services → Library
  4. Найти и включить Google Calendar API
  5. Найти и включить Google People API (для контактов)

Настройка OAuth consent screen:

  1. В боковом меню: APIs & Services → OAuth consent screen
  2. Нажать Get Started (или Configure, если уже настраивался)
  3. Заполнить App name (например, «CalSync») и выбрать User support email
  4. В разделе Audience выбрать External (если не Google Workspace)
  5. Заполнить Contact information и согласиться с политикой

Добавление себя как тестировщика (важно!):

  1. После создания OAuth consent screen перейти в раздел Audience
  2. Пролистать до секции Test users
  3. Нажать + Add users
  4. Ввести свой Google-аккаунт (тот, с которого будете авторизовываться)
  5. Сохранить

Без этого шага при авторизации будет ошибка «access_denied» — приложение в тестовом режиме доступно только одобренным тестировщикам.

Создание credentials:

  1. В боковом меню: APIs & Services → Credentials
  2. Нажать + Create Credentials → OAuth client ID
  3. Выбрать Application type: Desktop app
  4. Скачать JSON-файл и сохранить как credentials.json рядом со скриптами

Скрипты для бэкапа и синхронизации

Я написал набор скриптов на Python, которые делают всю работу:

Скрипты написаны с явной типизацией, валидацией входных данных и понятными сообщениями об ошибках. Никаких голых except, никаких неявных зависимостей.

Установка зависимостей

pip install google-auth-oauthlib google-api-python-client caldav

Или через requirements.txt:

pip install -r scripts/requirements.txt

Использование скриптов

Бэкап календарей:

# Положите credentials.json рядом со скриптом
python3 scripts/google_calendar_backup.py
# При первом запуске откроется браузер для авторизации
# Бэкапы сохранятся в backup/calendars/

Бэкап контактов:

python3 scripts/google_contacts_backup.py
# Бэкапы сохранятся в backup/contacts/

Синхронизация с CalDAV:

# Вариант 1: через переменные окружения
export CALDAV_URL="https://cal.example.com"
export CALDAV_USER="myuser"
export CALDAV_PASSWORD="mypassword"
python scripts/sync_google_to_caldav.py

# Вариант 2: через файл конфигурации
cp scripts/config.example.ini scripts/config.ini
# Отредактируйте config.ini
python scripts/sync_google_to_caldav.py

Альтернатива: vdirsyncer

Для полноценной двусторонней синхронизации есть замечательный инструмент vdirsyncer. Он умеет синхронизировать CalDAV/CardDAV серверы между собой, с локальными файлами и даже с Google (через специальные адаптеры).

pip install vdirsyncer

Конфигурация ~/.config/vdirsyncer/config:

[general]
status_path = "~/.local/share/vdirsyncer/status/"

# Локальное хранилище
[storage local_calendar]
type = "filesystem"
path = "~/.local/share/calendars/"
fileext = ".ics"

# Google Calendar
[storage google_calendar]
type = "google_calendar"
token_file = "~/.config/vdirsyncer/google_token"
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"

# Пара для синхронизации
[pair calendars]
a = "local_calendar"
b = "google_calendar"
collections = ["from b"]
conflict_resolution = "b wins"

Запуск:

vdirsyncer discover
vdirsyncer sync

Автоматизация через cron или systemd

Чтобы синхронизация работала автоматически:

Вариант 1 — cron:

# crontab -e
# Синхронизация каждые 6 часов
0 */6 * * * cd /path/to/scripts && python3 sync_google_to_caldav.py >> /var/log/calendar-sync.log 2>&1

Вариант 2 — systemd timer:

# /etc/systemd/system/calendar-sync.service
[Unit]
Description=Sync Google Calendar to CalDAV

[Service]
Type=oneshot
User=myuser
WorkingDirectory=/home/myuser/scripts
ExecStart=/usr/bin/python3 sync_google_to_caldav.py
Environment="CALDAV_URL=https://cal.example.com"
Environment="CALDAV_USER=myuser"
Environment="CALDAV_PASSWORD=mypassword"
# /etc/systemd/system/calendar-sync.timer
[Unit]
Description=Run calendar sync every 6 hours

[Timer]
OnCalendar=*-*-* 00/6:00:00
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl enable calendar-sync.timer
sudo systemctl start calendar-sync.timer

Почему эти два решения оптимальны

У меня нет желания делать из календаря отдельный проект жизни. Нужны две вещи:

  1. Чтобы не остаться без инфраструктуры, если что-то отвалится.
  2. Чтобы личные данные не были бесплатным кормом для чужих систем.

И вот тут логика простая:

  • Решение №1 — для спокойствия прямо сейчас. Это «страховка», а не революция. Минимум телодвижений, максимум пользы.
  • Решение №2 — для тех, кто всё-таки хочет сделать шаг к автономии, но не готов платить за это шумом в квартире и вторым рабочим местом «администратор собственной жизни».

Radicale на маленьком VPS за 3–5 долларов в месяц — это не «домашний дата-центр». Это скорее как внешний диск для бэкапов: купил, настроил один раз, работает.

Календарь — это вход в более широкую тему

Если уж заниматься «анти-рубильниковой» гигиеной, календарь — это хороший первый шаг. Но в идеале рядом с ним в скелете личной инфраструктуры должны быть ещё:

  • контакты (CardDAV, Radicale их тоже поддерживает)
  • менеджер паролей (Bitwarden self-hosted или хотя бы KeePass с синхронизацией)
  • 2FA-коды (Aegis на Android, не завязанный на облако)
  • резервные копии (не в одном месте, не только в облаке)
  • заметки (Obsidian, Logseq — локально с синхронизацией через Syncthing)

Потому что календарь — это больно, но не единственная точка зависимости. Просто самая заметная.

Вывод

Вся эта история, на самом деле, не про «какой календарь выбрать». Она про то, чтобы перестать жить в мире, где одна кнопка (у корпорации или у регулятора) может выключить кусок жизни.

И хорошая новость: между «пофиг, живём как жили» и «поднимаю сервер дома и становлюсь системным администратором» есть нормальная человеческая середина.

Вариант №1: остаюсь на Google, но делаю аварийный выход и разделяю личное/сервисное.

Вариант №2: строю автономию аккуратно — через CalDAV/CardDAV без тяжёлых комбайнов и без домашнего дата-центра. Radicale + VPS + скрипт синхронизации = календарь, который принадлежит мне.

А дальше уже дело вкуса: хочется ли просто подстелить соломки — или хочется действительно забрать себе обратно кусок цифровой свободы.

Предыдущий пост
Наверх