Архитектура
Итак: rdc на вашей рабочей станции, renet на ваших серверах, связь через SSH. На этой архитектурной разделении держится вся система Rediacc. На этой странице описывается, как оба инструмента распределяют ответственность, как обнаружение адаптера маршрутизирует состояние, как выглядит модель безопасности и как структурирована конфигурация.
Обзор полного стека
Трафик поступает из интернета через обратный прокси в изолированные Docker-демоны, каждый из которых опирается на зашифрованное хранилище:
Каждый репозиторий получает собственный Docker-демон, loopback IP-подсеть (/26 = 64 IP-адреса) и LUKS-зашифрованный BTRFS-том. Сервер маршрутов обнаруживает запущенные контейнеры на всех демонах и передаёт конфигурацию маршрутизации в Traefik.
Архитектура из двух инструментов
Rediacc использует два бинарных файла, работающих совместно через SSH:
- rdc работает на вашей рабочей станции (macOS, Linux или Windows). Он считывает локальную конфигурацию, подключается к удаленным машинам по SSH и вызывает команды renet.
- renet работает на удаленном сервере с привилегиями root. Он управляет LUKS-зашифрованными образами дисков, изолированными Docker-демонами, оркестрацией сервисов и конфигурацией обратного прокси.
Каждая команда, которую вы вводите локально, транслируется в SSH-вызов, выполняющий renet на удаленной машине. Вам никогда не нужно подключаться к серверам по SSH вручную.
Для правила-практики, ориентированного на операторов, см. rdc vs renet. Вы также можете использовать rdc ops для запуска локального кластера VM для тестирования, см. Экспериментальные VM.
Конфигурация
Всё состояние CLI хранится в плоских JSON-файлах конфигурации в ~/.config/rediacc/.
Локальный адаптер (по умолчанию)
Режим по умолчанию для самостоятельного размещения. Всё состояние хранится в файле конфигурации на вашей рабочей станции (например, ~/.config/rediacc/rediacc.json).
- Прямые SSH-подключения к машинам
- Не требуются внешние сервисы
- Один пользователь, одна рабочая станция
- Конфигурация по умолчанию создаётся автоматически при первом использовании CLI. Именованные конфигурации создаются с помощью
rdc config init --name <name>
Облачный адаптер (экспериментальный)
Активируется автоматически, когда конфигурация содержит поля apiUrl и token. Использует API Rediacc для управления состоянием и командной работы.
- Состояние хранится в облачном API
- Многопользовательские команды с ролевым доступом
- Веб-консоль для визуального управления
- Настраивается с помощью
rdc auth login
Примечание: Команды облачного адаптера являются экспериментальными. Включите их, установив
REDIACC_EXPERIMENTAL=1.
Оба адаптера используют одни и те же команды CLI. Адаптер влияет только на то, где хранится состояние и как работает аутентификация.
Пользователь rediacc
При выполнении rdc config machine setup renet создает системного пользователя rediacc на удаленном сервере:
- UID: 7111
- Оболочка:
/sbin/nologin(не может входить через SSH) - Назначение: Владеет файлами репозиториев и выполняет функции Rediaccfile
Пользователь rediacc не может быть доступен через SSH напрямую. Вместо этого rdc подключается как SSH-пользователь, которого вы настроили (например, deploy), а renet выполняет операции с репозиториями через sudo -u rediacc /bin/sh -c '...'. Это означает:
- Вашему SSH-пользователю нужны привилегии
sudo - Все данные репозитория принадлежат
rediacc, а не вашему SSH-пользователю - Функции Rediaccfile (
up(),down()) выполняются от имениrediacc
Такое разделение обеспечивает единообразное владение данными репозитория независимо от того, какой SSH-пользователь управляет им.
Изоляция Docker
Каждый репозиторий получает собственный изолированный Docker-демон. При монтировании репозитория renet запускает выделенный процесс dockerd с уникальным сокетом:
/var/run/rediacc/docker-{networkId}.sock
Например, репозиторий с идентификатором сети 2816 использует:
/var/run/rediacc/docker-2816.sock
Это означает:
- Контейнеры из разных репозиториев не могут видеть друг друга
- Каждый репозиторий имеет собственный кеш образов, сети и тома
- Docker-демон хоста (если есть) полностью отделен
Функции Rediaccfile автоматически получают переменную DOCKER_HOST, указывающую на правильный сокет.
Когда AI-агент входит в репозиторий через rdc term connect -r <repo>, действует та же изоляция: сессия выполняется от имени непривилегированного пользователя rediacc (UID 7111), в отдельном пространстве имён монтирования, с DOCKER_HOST, ограниченным сокетом демона этого единственного репозитория. Подход «сначала форк» сочетает эту изоляцию во время выполнения с примитивом CoW-клонирования: агент работает с форком на каждую задачу, никогда не с grand-репозиториями (продакшн). См. Безопасность и ограничения AI-агентов для полной модели песочницы, семантики переопределений и границы ответственности разработчика для учётных данных внешних сервисов.
Структура путей демона
Данные и конфигурация Docker хранятся внутри точки монтирования репозитория, обеспечивая полную изоляцию каждого демона от хоста и других репозиториев.
Структура для каждого репозитория:
{datastore}/mounts/{guid}/.rediacc/docker/data/ # Корень данных Docker
{datastore}/mounts/{guid}/.rediacc/docker/config/ # Конфигурация Docker
Автономная структура (демоны без привязки к точке монтирования репозитория):
{datastore}/standalone/{N}/.rediacc/docker/data/
{datastore}/standalone/{N}/.rediacc/docker/config/
Общий путь среды выполнения (без изменений):
/run/rediacc/docker-{N}.sock
Эта унифицированная структура устраняет коллизии монтирования в режимах только для чтения и чтения-записи, которые возникали, когда пути демона были разделены между файловой системой хоста и зашифрованным томом. Мы столкнулись с этим разделением не один раз, прежде чем остановились на этом решении. Как демоны репозиториев, так и автономные демоны следуют одинаковой структуре каталогов, поэтому инструменты и диагностика работают одинаково в обоих случаях.
Шифрование LUKS
Репозитории представляют собой LUKS-зашифрованные образы дисков, хранящиеся в хранилище данных сервера (по умолчанию: /mnt/rediacc). Каждый репозиторий:
- Имеет случайно сгенерированную парольную фразу шифрования (называемую “учетными данными”)
- Хранится как файл:
{datastore}/repos/{guid}.img - Монтируется через
cryptsetupпри доступе
Учетные данные хранятся в вашем файле конфигурации, но никогда на сервере. Без учетных данных данные репозитория не могут быть прочитаны. При включении автозапуска на сервере хранится вторичный LUKS-ключевой файл для автоматического монтирования при загрузке.
Структура конфигурации
Каждая конфигурация является JSON-файлом, хранящимся в ~/.config/rediacc/. Конфигурация по умолчанию: rediacc.json; именованные конфигурации используют имя в качестве имени файла (например, production.json). Поля сгруппированы по назначению: resources содержит развёртывания, credentials содержит секреты, account содержит облачные значения по умолчанию, infra содержит TLS/DNS, а encryption содержит состояние шифрования покоящихся данных на уровне каждого поля. Дискриминатор верхнего уровня schemaVersion: 2 закрепляет совместимость в будущем.
{
"schemaVersion": 2,
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"version": 47,
"defaults": {
"language": "en",
"machine": "prod-1",
"nextNetworkId": 2880,
"universalUser": "rediacc"
},
"credentials": {
"ssh": {
"privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----",
"publicKey": "ssh-ed25519 AAAA...",
"knownHosts": "..."
},
"cfDnsApiToken": "cf-token-xxxxxxxxxxxx"
},
"resources": {
"machines": {
"prod-1": {
"ip": "203.0.113.50",
"user": "deploy",
"port": 22,
"datastore": "/mnt/rediacc",
"knownHosts": "203.0.113.50 ssh-ed25519 AAAA..."
}
},
"storages": {
"backblaze": {
"provider": "b2",
"vaultContent": { "...": "..." }
}
},
"repositories": {
"webapp": {
"repositoryGuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"credential": "base64-encoded-random-passphrase",
"networkId": 2816
}
}
},
"infra": {
"certEmail": "admin@example.com",
"cfDnsZoneId": "..."
},
"encryption": {
"mode": "plaintext"
}
}
Ключевые корзины:
| Корзина | Содержание |
|---|---|
schemaVersion | Дискриминатор (в настоящее время 2). Загрузчики отклоняют неизвестные версии. |
id / version | Неизменяемый UUID + монотонный счётчик; используется для оптимистичной блокировки в удаленном хранилище конфигурации. |
defaults.* | Нечувствительные к безопасности значения по умолчанию (machine, language, pruneGraceDays, universalUser, nextNetworkId). |
credentials.ssh | Встроенная SSH-пара ключей + knownHosts. Заменяет устаревший ssh.privateKeyPath (не больше пути к файлу с косвенной ссылкой; содержимое разрешается при загрузке и хранится встроенным). |
credentials.cfDnsApiToken | Cloudflare DNS-01 ACME-токен. |
credentials.masterPasswordVerifier | Присутствует только когда encryption.mode === "master-password". |
resources.machines.* | SSH-детали подключения на машину. |
resources.storages.* | Совместимые с rclone учетные данные резервной копии вне сайта. |
resources.repositories.* | Per-repo GUID + LUKS учетные данные + SSH-ключ для доступа изолированного в песочнице агента. |
infra.acmeCertCache.* | Кеш Traefik acme.json, gzip+base64, индексированный по домену. |
encryption.mode | "plaintext" (по умолчанию) или "master-password". |
encryption.encryptedFields | При шифровании - карта BLOB-объектов AES-GCM на указатель (/resources/repositories/webapp/credential → {ciphertext, nonce, tag}). Один запрос разблокировки на сеанс расшифровывает при чтении полей. |
remote | Присутствует только когда конфигурация синхронизирована в хранилище зашифрованной конфигурации; см. Хранилище зашифрованной конфигурации. |
Редактируйте безопасно через CLI, а не через vim:
# Редактирование отдельных полей, адресованные указателем (шлюзование знаний для чувствительных путей)
rdc config field set --pointer /resources/machines/prod-1/port --new 2222
rdc config field set --pointer /credentials/cfDnsApiToken --current "$OLD" --new "$NEW"
# Полный редактор с редактированной JSONC-проекцией (только для людей)
rdc config edit
# Дамп JSONC только для чтения, безопасный для скриптов и агентов
rdc config edit --dump
# Проверяйте каждую мутацию + отказ + раскрытие в журнале аудита
rdc config audit log --since 24h
rdc config audit verify
Этот файл содержит конфиденциальные данные (SSH приватные ключи, LUKS учетные данные, Cloudflare токены). Он хранится с правами
0600(чтение/запись только для владельца). Не делитесь им и не добавляйте в систему контроля версий. Когда любая командаrdcчитает его, конфиденциальные поля редактируются по умолчанию: простой текст появляется только с--revealна интерактивном человеческом TTY.
Envelope v2 и серверная реализация
Когда конфигурация синхронизируется с хранилищем зашифрованной конфигурации, CLI оборачивает каждое конфиденциальное поле в обязательство HMAC для каждого поля и носит эти обязательства в открытом конверте. Сервер видит только шестнадцатеричные дайджесты: никогда значения: но может реализовать шлюзование знаний при каждой записи:
- Проверка предусловия: на
PUT /configs/<id>клиент отправляет дайджесты, которые он утверждает, что знает для путей, которые он хочет изменить. Сервер сравнивает с сохранённым обязательством конверта. Несовпадение →409 precondition_failedсmismatchedPaths. Без знания: сервер никогда не видит открытый текст. - Защита от понижения версии: новый конверт должен брать на себя каждый конфиденциальный путь, на который брался предыдущий конверт. Агент не может убрать путь из обязательств, чтобы обойти будущее шлюзование предусловия.
- Закрепление версии конверта: сервер отклоняет конверты без
envelopeVersion: 2с400 unsupported_envelope_version. Нет окна двойного принятия. - Шифрование на уровне поля в покое (со стороны CLI): когда
encryption.mode === "master-password", каждый секрет становится отдельным BLOB AES-GCM, ключ которого задан главным паролем. Чтения не вызывают приглашение, если команда не затрагивает секрет (поэтомуrdc machine listостаётся без приглашения).
Ключ обязательства (FCK) производится на стороне клиента из CEK через HKDF-SHA256(ikm=CEK, salt=fckSalt, info="rediacc-config-fck-v1") с солью на каждую конфигурацию. Ротация fckSalt делает все предыдущие обязательства недействительными, принуждая полный пересчёт: полезно при ротации CEK.