Перейти к основному содержимому

Основа платформы

Libre WebUI поддерживает локальный профиль solo и общий team. Solo использует SQLite, зашифрованные локальные bloby, зашифрованные встроенные векторы, локальную координацию и встроенного durable workera. Team использует PostgreSQL, приватные S3-совместимые bloby, PGVector, Redis и внешнего workera. Запуск отклоняет смешанные профили вместо тихого разделения состояния между локальными и общими бэкендами.

Текущий этап

ОбластьРеализованная основаОставшаяся работа вызывающих
ПостоянствоРепозитории SQLite/PostgreSQL, неизменяемые миграции, транзакции пулаНовые домены должны соблюдать границы репозитория
BlobyЗашифрованные локальные/S3 потоковые хранилища, диапазоны, checksum, квотыПеренести вложения, аватары и оставшиеся inline-бинарные поля
ВекторыЗашифрованные встроенные векторы, ACL PGVector и безопасная перестройка индексаНовые embedding-вызовы должны сохранять контракт авторитета и жизненного цикла
КоординацияЛокальные/Redis события, cache, leases, rate limits, invalidation, healthRedis должен оставаться неавторитетным
Jobs/eventsОчереди SQLite/PostgreSQL, транзакционные события, workеры, retry, cancel, adminКаждый новый эффект требует idempotency или outbox
ОперацииHealth gates, подписанные/зашифрованные архивы, чистое восстановление, проверкаТренировать восстановление и межрепличную приёмку для каждой среды

Профили среды

LIBRE_PLATFORM_MODE=solo по умолчанию выбирает SQLite, локальные bloby, встроенные векторы, локальную координацию и встроенного workera. Redis можно выбрать в solo, но он не делает SQLite или файлы безопасными для нескольких реплик.

LIBRE_PLATFORM_MODE=team одновременно требует:

  • DATABASE_BACKEND=postgres с DATABASE_URL;
  • BLOB_STORE_BACKEND=s3;
  • VECTOR_STORE_BACKEND=pgvector;
  • COORDINATION_BACKEND=redis с REDIS_URL; и
  • JOB_WORKER_MODE=external.

Это целостный набор. Team не запускается без любой общей зависимости или при смешении локального бэкенда.

Миграция существующей solo-установки

Остановите все приложения и workеры. Примеры используют установленный libre-webui из npm/Homebrew; без установки замените на npx --yes libre-webui@latest. Из исходников соберите один раз и замените libre-webui migrate-postgres на npm run migrate:postgres --. Настройте целевые PostgreSQL, S3 и версии ключей точно как team, затем выполните анализ только для чтения:

libre-webui migrate-postgres \
--source /absolute/path/to/data.sqlite \
--plugins /absolute/path/to/plugins \
--mode dry-run

Применяйте только к пустой цели отчёта. Сбой оставляет журнал с checksum; возобновляйте ту же пару, не начинайте несвязанный импорт:

libre-webui migrate-postgres \
--source /absolute/path/to/data.sqlite \
--plugins /absolute/path/to/plugins \
--mode apply

# Only after an interrupted apply of this exact source and target:
libre-webui migrate-postgres \
--source /absolute/path/to/data.sqlite \
--plugins /absolute/path/to/plugins \
--mode apply --resume

libre-webui migrate-postgres \
--source /absolute/path/to/data.sqlite \
--plugins /absolute/path/to/plugins \
--mode validate

Маркер завершения появляется лишь после переноса и аутентификации реляционных строк, плагинов, локальных зашифрованных blob-объектов, встроенных векторов и старых векторов персон в PostgreSQL/S3/PGVector. Команда не создаёт ключ источника: ENCRYPTION_KEY должен совпадать с .encryption_key, а STORAGE_ENCRYPTION_KEYS содержать активный ключ и legacy.

Запуск встроенного team-профиля

Начните с шаблона с безопасным отказом. Готовый файл держите вне репозитория с доступом оператора:

cp deploy/team/.env.example /absolute/path/to/libre-team.env
chmod 600 /absolute/path/to/libre-team.env

Замените все REPLACE_*. Пароль PostgreSQL создайте URL-safe алфавитом, например openssl rand -hex 32, поскольку литерал используется в сервере и DATABASE_URL. ENCRYPTION_KEY и значения STORAGE_ENCRYPTION_KEYS должны иметь 64 hex-символа. В новой установке legacy равен ENCRYPTION_KEY; при миграции оба равны источнику. Для новых blob-записей используйте иной активный ключ и сохраняйте старые до подтверждения отсутствия использования.

Файл также задаёт POSTGRES_MIGRATION_MODE, POSTGRES_POOL_MAX, PostgreSQL timeouts, REDIS_CONNECT_TIMEOUT_MS, OLLAMA_BASE_URL, OLLAMA_TIMEOUT, OLLAMA_LONG_OPERATION_TIMEOUT, OLLAMA_MAX_CONTEXT; Compose одинаково передаёт их приложению и workerу. Тайм-ауты принимают 1 000–3 600 000 мс, контекст 128–2 097 152 токенов, длинный не короче обычного; неверные значения останавливают оба входа до состояния. Локальные Agent CLI и файлы Codex OAuth не поддерживаются внешними workерами, поэтому team закрепляет их выключенными и отклоняет включение. Запустите реплики, worker, PostgreSQL/PGVector, Redis, версионированный MinIO и gateway:

docker compose --env-file /absolute/path/to/libre-team.env \
-f docker-compose.team.yml up --build --scale libre-webui=3 -d
docker compose --env-file /absolute/path/to/libre-team.env \
-f docker-compose.team.yml ps

Базовый team не подключает Docker-сокет, поэтому Docker Work недоступен. Включайте только production-overlay во всех командах:

docker compose --env-file /absolute/path/to/libre-team.env \
-f docker-compose.team.yml -f docker-compose.team.work.yml \
up --build --scale libre-webui=3 -d
docker compose --env-file /absolute/path/to/libre-team.env \
-f docker-compose.team.yml -f docker-compose.team.work.yml ps

Overlay направляет приложение и worker к одному фильтрованному socket proxy во внутренней сети. Они не получают сырой сокет или группу, хост-папки Work отключены. Proxy предоставляет info, images, containers, exec, volumes, networks и необходимые write-методы. Это сужает API, но не создаёт tenant-границу: контейнер может bind-mount пути хоста. Для изоляции используйте выделенную VM или отдельный/rootless демон Work.

Не открывайте Compose PostgreSQL, Redis и MinIO. Для управляемых зависимостей используйте Helm team с TLS; Compose выключает PostgreSQL TLS лишь внутри приватной сети. Readiness не проходит без внешнего workera.

Граница постоянства и миграции

Идентичность и авторизация используют асинхронные репозитории. Callback транзакции получает единицу работы на том же соединении; глобальный репозиторий внутри отклоняется. Это граница пула PostgreSQL с сохранением поведения SQLite.

Координатор SQLite принимает старую установку только после проверки схемы. Он записывает номер, имя и checksum миграции, проверяет на каждом старте, отклоняет новые/неизвестные/несовпавшие журналы и останавливает старт при ошибке. Readiness и recovery используют тот же контракт.

До импорта служб с состоянием старт копирует SQLite и активные WAL/SHM в приватный временный каталог и проверяет. PLATFORM_PREFLIGHT_TMP_DIR должен вмещать базу и WAL. Docker/Helm монтируют выделенный дисковый temp, не ограниченный /tmp tmpfs. Отсутствующий старый ключ или вложенный каталог блокирует старт до создания замены, базы или плагинов.

Схема v4 добавляет ключевой equality token для encrypted e-mail. Recovery требует наличия и совпадения. В узком crash-окне после commit v4 старт допускает отсутствующий token при аутентифицированном e-mail или старое значение без envelope, чтобы инициализация завершила backfill. Старые релизы принимали любые строки и очищали пустой; adoption сохраняет непустые и нормализует пустые в NULL. Повреждённые envelope-shaped и несовпадения fail preflight.

Службы используют асинхронные dialect repositories. Нативный SQLite ограничен адаптерами, инспекцией и явно внедрёнными health checks. Runtime storage создаётся из Persistence; PostgreSQL не падает обратно в SQLite singleton или cwd JSON.

Общий durable-job runtime нейтрален к драйверу. Авторизация читается из identity repository, создание native job repository ограничено adapter-composition. Публикаторы получают непрозрачный executor SQLite или transaction-bound PostgreSQL, никогда better-sqlite3. Тест отвергает native handles в job, resource, identity, chat и Work contracts.

Основа blob- и vector-хранилищ

Галерея и источники документов используют BlobStore; RAG и память персон — VectorStore. Старые строки галереи SQLite читаются двойным путём и при первом доступе превращаются в blob references. Relational metadata и durable reference авторитетны; provider URL и физические S3 keys не сохраняются как содержимое. Вложения, аватары и другие inline-поля ещё не мигрированы.

Recovery последовательно аутентифицирует каждый постоянный объект и обёртку вектора под лимитами. Распознаваемые старые текстовые обёртки и бинарные обёртки сохранённых голосов строго проверяются ENCRYPTION_KEY; резервный механизм среды decrypt-and-original не используется. Источник не инициализируется, чинится, переписывается или удаляется; повреждённый шифротекст, неверные ключи, неканонические layouts и лимиты блокируют снимок.

Лимиты: 250 000 объектов, 64 GiB encrypted/открытый текст blob bytes, 250 000 vector rows, 4 GiB vector шифротекст и 500 миллионов components. Тесты могут переопределить через RecoveryInventoryOptions; CLI не сэмплирует и не пропускает.

Legacy-проверка ограничена миллионом полей и 16 GiB stored/authenticated открытый текст. Настоящий открытый текст старых схем совместим, поскольку marker отсутствует; считаются аутентифицированные envelopes. Saved voice однозначны и используют profile/owner/field AAD.

Зашифрованные локальные bloby

BlobStore привязан к владельцу и предоставляет streaming put/read, metadata/stat, inclusive ranges и idempotent delete. LocalEncryptedBlobStore пишет UUID-объекты под корнем приложения; цель ${DATA_DIR}/blobs. Использует exclusive staging, fsync, atomic rename, каталоги 0700 и файлы 0600.

У объекта случайный 256-bit data key. AES-256-GCM шифрует приватные metadata и независимо аутентифицирует chunks. AAD связывает blob ID, owner, purpose, chunk index и открытый текст length. Versioned keyring оборачивает data key. Descriptor содержит size, SHA-256, content type, time, format version, key ID. Full read проверяет SHA-256; range — все затронутые chunks.

Quota резервирует ёмкость до стрима, учитывает реальные bytes, commit только после atomic visibility, освобождает сбои. SQLite использует BEGIN IMMEDIATE; PostgreSQL serializable transactions и row locks. S3 metadata и quota commit/rollback одной транзакцией. Старт согласует expired reservations и записи без физического blob. BLOB_QUOTA_BYTES_PER_USER задаёт limit, BLOB_QUOTA_RESERVATION_TTL_MS — abandoned TTL.

BLOB_STORE_BACKEND=s3 использует приватный S3-compatible bucket. Libre загружает opaque keys и encrypted chunk streams, хранит descriptors в PostgreSQL, поддерживает HTTP ranges, проверяет открытый текст/шифротекст SHA-256 и idempotent delete. Deleting row остаётся до физического и atomic metadata/quota удаления; reconciliation повторяет interrupted deletes и удаляет aged orphans. MinIO-тест покрывает cross-replica read/delete, tenant isolation, quota contention, unconsumed streams и DB failures.

Зашифрованные встроенные векторы

VectorStore требует actor при каждом query/mutation. Records несут namespace, opaque tenant ID, owner, resource, embedding model, dimensions, version, source revision, equality attributes и user/group grants.

SQLite применяет namespace/model/dimension/version, owner/grant, resource и attribute predicates до выхода шифротекст. Только ограниченные авторизованные candidates расшифровываются и cosine-scored. Одинаковый ID изолирован по owner без раскрытия tenants. Upsert атомарно заменяет embeddings, ACL, attributes; delete owner-scoped и cascade.

Embeddings используют AES-256-GCM с identity/model AAD. Queryable identity, grants, model, version, revision и filters остаются открытый текст, поэтому секреты запрещены. Embeddings — sensitive derived data.

VECTOR_STORE_BACKEND=pgvector применяет все predicates в том же SQL, что distance order и LIMIT. Post-filter global neighbors запрещён. Группы разрешаются из trusted current membership при каждом query; caller groupIds игнорируются. Revocation немедленный, forged claims не работают.

Document ingestion и regeneration захватывают immutable spec до работы: enabled, model, vector/chunker versions, size, overlap, threshold. Одна spec управляет chunks, relational publish, upsert и query; изменение preference не смешивает модели или пороги. Metadata фиксируют aggregate revision и spec, SQL остаётся manifest.

Regeneration держит auto-renew lease на документ и проверяет owner row и deletion tombstone до publish, до и после mutation. Delete может commit во время upsert; post-check удаляет восстановленные векторы. PostgreSQL/team reads не мутируют PGVector. SQLite лениво публикует только при точном manifest и lease; busy/superseded пропускается для keyword fallback или explicit regeneration.

Индексы заменяются compensated batches до 1 000; проверки постранично обходят полный manifest. Документ может иметь 100 000 chunks; 100 001 отклоняется до embedding/publish и dead-letter без retry. Увеличьте chunk size или уберите лишние разрывы.

Pre-manifest solo может иметь authenticated inline vectors без model/chunker. Первое semantic use лишь по наличию запускает rechunk authoritative text и полную regeneration под текущей spec и lease. Legacy payload не копируется и не маркируется текущей preference. Provider failure или busy lease оставляет legacy keyword-searchable.

SQLite-to-team fail-closed, если legacy-документ не покрыт current manifest metadata и точным encrypted platform index. Текущие preferences не доказывают historical model. При блокировке запустите текущий solo/SQLite с теми же DATA_DIR и ENCRYPTION_KEY, выберите модель, используйте Settings -> Documents -> Regenerate embeddings для владельцев и повторите dry-run. Только затем team игнорирует inline шифротекст и переносит доказанные векторы.

Конфиденциальность различается. SQLite шифрует embeddings AES-256-GCM после ACL. PGVector работает с числами и не application-encrypt column. Требуйте TLS, encrypted PostgreSQL volumes/backups, least-privilege role, ограниченное администрирование и SQL logs без данных. Source text, persona memory, gallery metadata и descriptors остаются envelope-encrypted. Attributes открытый текст и не должны содержать секреты.

Ключи шифрования хранилища

При versioned keyring нужно задать стабильный 64-char ENCRYPTION_KEY, тот же под legacy в STORAGE_ENCRYPTION_KEYS и STORAGE_ENCRYPTION_ACTIVE_KEY_ID. Writes используют active; reads принимают все ID для staged rotation. Требование предотвращает отдельный ключ старой службы. Сохраняйте старые до rewrite/rewrap и проверки всех объектов.

Без карты адаптер принимает 64-char ENCRYPTION_KEY как legacy или читает ${DATA_DIR}/.encryption_key. Factory принимает только regular non-symlink private file; не создаёт и не меняет. Конфликт environment с persistent key закрывает старт. При rotation legacy key остаётся как legacy до rewrite/verify. Missing, malformed, mismatch, unknown fail closed.

Embedded queries применяют ACL/metadata в SQLite, затем суммируют число кандидатов, зашифрованные байты и объём вычислений по dimensions до возврата шифротекст в Node. Превышение бюджета закрывает запрос и требует сужения.

Координация

Контракт даёт events, expiring cache, fenced leases и fixed-window rate limits. Локальная реализация только для одной solo-реплики. Redis использует отдельные command/subscription clients, bounded payloads, health, namespace, atomic scripts, unique owner tokens, expiry и fencing. После Redis error нет local fallback.

Redis не источник истины. Authorization, durable jobs и replayable events остаются в БД; Redis — wake-up, invalidation, presence, quota, coordination. Критическая работа проверяет DB lease/fencing перед эффектом.

Tickets, caches, shared invalidation, connection limits, Work events и distributed locks используют границу. Один Redis не делает local persistence общей; нужен полный team.

Постоянные задания и события

SQLite migration v3 создаёт durable job, attempt, stream head и ordered event tables. Контракт поддерживает idempotent enqueue, bounded retry, cancel, progress, heartbeat/reclaim, dead-letter и replay by cursor. Encrypted JSON использует platform keyring с job/event AAD; references opaque bounded IDs.

SQLite v13 и PostgreSQL v12 добавляют индекс (stream_id, subject_id, global_cursor) для generation-scoped replay. Фильтры применяются до catch-up limit, поэтому старые generations не расходуют бюджет и не вызывают full scan.

Application и standalone worker регистрируют audited обработчики для ingestion, media continuation и cleanup. Admin предоставляет bounded inspect/cancel. Enqueue idempotent, relational create/delete вставляет job в той же транзакции. Cleanup удаляет vectors, private blobs, references, cache и queued work retry-safe.

Recovery считает states/outcomes, streams/cursors, блокирует active work и аутентифицирует payload. Отклоняет head mismatch и non-contiguous sequences.

Monotonic lease tokens ограждают stale workers. Это не выполнение ровно один раз: worker может сделать внешний эффект и не записать success. Нужны provider idempotency keys или transactional outbox/inbox и повторная actor authorization перед эффектом.

SQLite v4 добавляет unique keyed email lookup token рядом с randomized шифротекст. Это HMAC под application key, обеспечивающий duplicate enforcement без открытый текст или deterministic encryption. Старт аутентифицирует и backfill старые e-mail до трафика.

Состояние и восстановление

Пробы различают process liveness и dependency readiness:

  • /health и /health/live проверяют только процесс;
  • /health/ready проверяет БД, schema ledger, writable storage и required dependencies с редактированием. Не ждёт optional providers; и
  • /health/deep требует администратора и выполняет SQLite integrity/FK в bounded worker вне HTTP loop. Optional server providers, например Ollama, дают warnings без изменения readiness.

Запустите libre-webui recovery-check --json; из исходников используйте npm run recovery:check -- --json. Read-only инвентарь сообщает schema/key identities, blob root, legacy/platform vectors, аутентифицирует blobs/vectors, legacy application/voice и encrypted содержимого заданий/событий, размеры, плагины и их положение, медиа, Work resources/labels, checkpoints, active runs/jobs, blockers и exclusions. Это pre-backup gate, не backup. См. Готовность к восстановлению.

Сертифицированная работа нескольких реплик

Team сертифицирован для ≥3 app replicas и ≥1 external worker при полном наборе PostgreSQL, PGVector, Redis, S3, shared secrets и JOB_WORKER_MODE=external. Helm проверяет при render: replicas>1 без team не развёртываются. Миграции выбирают лидера PostgreSQL advisory lock, исключая гонку версий.

Сертификация выполняется: pipeline запускает three-replica drill (npm run test:team-platform) с реальным образом и проверяет cross-replica resume, смерть workera mid-write с replay, Redis outage с authoritative SQL, revocation при потере coordinator, shared rate limits, S3 delete retry и tenant isolation. Усиление: secrets.existingSecret и networkPolicy.enabled. Pod security: non-root, корневая файловая система только для чтения, dropped capabilities, seccomp RuntimeDefault, без повышения привилегий.

Известные оставшиеся переходы

Основа не означает, что каждое бинарное поле уже blob. Аудио сохранённых голосов, вложения и аватары и будущие plugin binaries требуют метаданные ссылок, dual-read/backfill, политики хранения и тесты удаления. Каждый embedding caller должен передавать model, dimensions, version, source revision, owner, resource scope и trusted grants через VectorStore; прямой доступ к таблице запрещён.

Новые долгие или внешние эффекты должны регистрировать постоянную цель ресурса, поддерживать отмену и повтор и транзакционную границу очереди/outbox с реляционным изменением. Добавляйте каждый ресурс в межрепличные проверки загрузки, чтения, поиска, удаления, резервного копирования и восстановления до включения в team.