fix: release QA hardening across processing, media, security, and CI gates (#649)

A release-readiness QA pass over the whole product. The commits split into
defects a user would hit and gates that were reporting green while measuring
nothing.

## Fixes that change behaviour

Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so
request.ip came from a client-set header and a forged X-Forwarded-For got past
the login limiter. The default is now a private-network trust list.

A transient Postgres outage stranded in-flight jobs, leaving finished output on
disk with no row pointing at it. A reconciler now resolves those rows and adopts
the bytes rather than dropping the work.

A Redis connection that moved to a new address wedged every read-blocked
consumer, so completions stopped signalling while health still answered 200.
Socket timeouts plus subscriber pings recover it.

Installing more than one AI bundle left the shared venv multi-versioned and
silently broke three tools. The installer now reconciles distributions to one
version each.

Converting an image to JXL at quality 1 through 4 returned a 500, because
libjxl 0.7 rejects the distance those values compute. The quality is floored at
what the encoder honours. A missing ffmpeg was also reported to the user as a
corrupt upload; it now says the engine is unavailable.

RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at
0.22.2, and the release scan was split so it can fail on an unfixed critical
instead of hiding it behind ignore-unfixed.

## Gates that could not fail

Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs
build; coverage discarded its whole report on any failing test; the lint gate
skipped root tests, scripts, and two workspaces; and several generated matrices
counted a host missing ffmpeg as a passing tool. Each now measures what it
claims.

Full evidence and the outstanding release items are tracked locally and are not
part of this branch.
This commit is contained in:
SnapOtter
2026-07-27 15:37:30 +08:00
committed by GitHub
parent bc32f86a07
commit d10d0f544f
855 changed files with 54564 additions and 13092 deletions
+4 -3
View File
@@ -1,8 +1,9 @@
---
description: "Структура монорепозиторію, архітектура застосунків і пакетів, життєвий цикл запиту та споживання ресурсів SnapOtter."
i18n_output_hash: 6a1de194bb8b
i18n_source_hash: a53946e760b0
i18n_source_hash: 50e076925c4b
i18n_provenance: human
i18n_output_hash: 4490df3a46a7
i18n_hash_version: 2
---
# Архітектура {#architecture}
@@ -52,7 +53,7 @@ snapotter/
### API (`apps/api`) {#api-apps-api}
Сервер Fastify v5, що надає 241 маршрут інструментів у п'яти модальностях (image, video, audio, PDF, file) і обробляє:
Сервер Fastify v5, що надає 243 маршрут інструментів у п'яти модальностях (image, video, audio, PDF, file) і обробляє:
- Завантаження файлів, керування тимчасовим робочим простором та постійне зберігання файлів
- Бібліотеку файлів користувача (таблиця `user_files`): за замовчуванням збережене редагування зберігається як незалежний новий файл, або як пов'язана з батьківською версія, коли ви перезаписуєте оригінал. Вона фіксує, які інструменти було застосовано (`toolChain`), і отримує автоматично згенеровану мініатюру для сторінки Files
- Виконання інструментів (маршрутизує кожен запит інструмента до рушія зображень або мосту AI)
+40 -17
View File
@@ -1,8 +1,9 @@
---
description: "Усі змінні середовища SnapOtter зі значеннями за замовчуванням. Налаштуйте автентифікацію, сховище, моделі AI, аналітику тощо."
i18n_source_hash: 8e9e9ca2840c
i18n_source_hash: 25970c776f7c
i18n_provenance: human
i18n_output_hash: 498d74ca4a81
i18n_output_hash: 52fb4064af72
i18n_hash_version: 2
---
# Конфігурація {#configuration}
@@ -19,28 +20,51 @@ i18n_output_hash: 498d74ca4a81
| `RATE_LIMIT_PER_MIN` | `1000` | Максимальна кількість запитів за хвилину на одну IP. Задайте 0, щоб вимкнути обмеження частоти запитів. |
| `CORS_ORIGIN` | (порожньо) | Розділені комами дозволені джерела для CORS або порожньо лише для того самого джерела. |
| `LOG_LEVEL` | `info` | Детальність журналювання. Одне з: `fatal`, `error`, `warn`, `info`, `debug`, `trace`. |
| `TRUST_PROXY` | `true` | Довіряти заголовкам `X-Forwarded-For` від зворотного проксі. Задайте `false`, якщо не за проксі. |
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Яким вузлам дозволено задавати IP клієнта через `X-Forwarded-For`. Значення за замовчуванням вірить лише вузлу з приватної мережі, тож зворотному проксі в мережі Docker або в локальній мережі довіра є, а підробленому заголовку від публічного клієнта немає. Задавайте `true`, лише якщо попереду стоїть підконтрольний вам проксі з публічною адресою. |
### Автентифікація {#authentication}
Дві булеві змінні нижче приймають лише `true` і `false`. Будь-що інше, чи то `1`, `yes` чи `on`, не проходить перевірку, і сервер завершує роботу, так і не почавши слухати.
| Змінна | За замовчуванням | Опис |
|---|---|---|
| `AUTH_ENABLED` | `false` | Задайте `true`, щоб вимагати входу. Образ Docker за замовчуванням має `true`. |
| `AUTH_ENABLED` | `true` | Вимагати вхід. Задайте `false`, щоб працювати взагалі без облікових записів: тоді будь-який запит отримує права адміністратора, тож лишайте так лише в довіреній мережі. |
| `DEFAULT_USERNAME` | `admin` | Ім'я користувача для початкового облікового запису адміністратора. Використовується лише під час першого запуску. |
| `DEFAULT_PASSWORD` | `admin` | Пароль для початкового облікового запису адміністратора. Змініть його після першого входу. |
| `MAX_USERS` | `0` (необмежено) | Максимальна кількість зареєстрованих облікових записів користувачів. Задайте 0 для необмеженої кількості. |
| `SESSION_DURATION_HOURS` | `168` | Тривалість сеансу входу в годинах (за замовчуванням 7 днів). |
| `SKIP_MUST_CHANGE_PASSWORD` | - | Задайте будь-яке непорожнє значення, щоб обійти примусовий запит на зміну пароля під час першого входу |
| `SKIP_MUST_CHANGE_PASSWORD` | `false` | Задайте `true`, щоб пропустити примусовий запит на зміну пароля під час першого входу. |
### Сховище {#storage}
| Змінна | За замовчуванням | Опис |
|---|---|---|
| `STORAGE_MODE` | `local` | `local` або `s3`. S3/MinIO вимагає ліцензії з функцією s3_storage. |
| `DATABASE_URL` | `postgres://snapotter:snapotter@postgres:5432/snapotter` | Рядок підключення до PostgreSQL. |
| `REDIS_URL` | `redis://redis:6379` | Рядок підключення до Redis (використовується для черг завдань BullMQ). |
| `WORKSPACE_PATH` | `./tmp/workspace` | Каталог для тимчасових файлів під час обробки. Очищується автоматично. |
| `FILES_STORAGE_PATH` | `./data/files` | Каталог для постійних файлів користувача (завантажені зображення, збережені результати). |
| `STORAGE_MODE` | `local` | `local` або `s3`. S3 і MinIO потребують ліцензії з функцією s3_storage, а також змінних `S3_*` нижче. |
| `DATABASE_URL` | `postgres://snapotter:snapotter@localhost:5432/snapotter` | Рядок підключення до PostgreSQL. Стек Compose спрямовує його на власний сервіс `postgres`; лишіть його незаданим (разом із `REDIS_URL`), щоб отримати вбудований режим. |
| `REDIS_URL` | `redis://localhost:6379` | Рядок підключення до Redis (використовується для черг завдань BullMQ). Compose спрямовує його на власний сервіс `redis`. |
| `WORKSPACE_PATH` | `./tmp/workspace` | Каталог для тимчасових файлів під час обробки. Очищується автоматично. Образ задає `/tmp/workspace`. |
| `FILES_STORAGE_PATH` | `./data/files` | Каталог для постійних файлів користувача (завантажені зображення, збережені результати). Образ задає `/data/files`. |
### Об'єктне сховище S3 {#s3-object-storage}
Читається лише тоді, коли задано `STORAGE_MODE=s3`. Пропустіть будь-яку з трьох обов'язкових змінних, і запуск завершиться помилкою з назвою тієї змінної, яку ви не задали.
| Змінна | За замовчуванням | Опис |
|---|---|---|
| `S3_BUCKET` | (порожньо) | Бакет, у якому зберігаються завантаження та результати. Обов'язкова. |
| `S3_ACCESS_KEY_ID` | (порожньо) | Ключ доступу. Обов'язкова. У контейнері його можна натомість змонтувати файлом через `S3_ACCESS_KEY_ID_FILE`. |
| `S3_SECRET_ACCESS_KEY` | (порожньо) | Секретний ключ. Обов'язкова. Та сама домовленість щодо файлів: `S3_SECRET_ACCESS_KEY_FILE`. |
| `S3_REGION` | `us-east-1` | Регіон бакета. |
| `S3_ENDPOINT` | (порожньо) | Власна кінцева точка для MinIO, R2, Backblaze та інших S3-сумісних сховищ. Порожньо означає AWS. |
| `S3_FORCE_PATH_STYLE` | `false` | Задайте `true` для MinIO та всього іншого, що очікує `endpoint/bucket/key` замість адресації за віртуальним хостом. |
| `S3_PREFIX` | (порожньо) | Префікс ключів, щоб один бакет міг обслуговувати кілька екземплярів. |
### Шифрування збережених даних {#encryption-at-rest}
| Змінна | За замовчуванням | Опис |
|---|---|---|
| `DATA_ENCRYPTION_KEY` | (порожньо) | 64 шістнадцяткові символи (32 байти). Шифрує чутливі налаштування, збережені в базі даних. Будь-що, що не є 64 шістнадцятковими символами, відхиляється під час запуску. |
| `DATA_ENCRYPTION_KEY_PREVIOUS` | (порожньо) | Ключ, від якого ви відходите під час ротації, у тому самому форматі. Задайте обидва на час ротації, щоб наявні рядки й далі розшифровувалися, а потім приберіть цей. |
### Вбудований режим {#embedded-mode}
@@ -59,16 +83,15 @@ i18n_output_hash: 498d74ca4a81
| Змінна | За замовчуванням | Опис |
|---|---|---|
| `MAX_UPLOAD_SIZE_MB` | `100` | Максимальний розмір файлу на одне завантаження в мегабайтах. Задайте 0 для необмеженого. |
| `MAX_BATCH_SIZE` | `100` | Максимальна кількість файлів в одному пакетному запиті. Задайте 0 для необмеженого. |
| `MAX_UPLOAD_SIZE_MB` | `0` (необмежено) | Максимальний розмір файлу на одне завантаження в мегабайтах. Задайте 0 для необмеженого. Опублікований образ постачається зі значенням `0`; збірка з вихідного коду починає зі 100. |
| `MAX_BATCH_SIZE` | `0` (необмежено) | Максимальна кількість файлів в одному пакетному запиті. Задайте 0 для необмеженого. Опублікований образ постачається зі значенням `0`; збірка з вихідного коду починає зі 100. |
| `CONCURRENT_JOBS` | `0` (авто) | Кількість пакетних завдань, що виконуються паралельно. Задайте 0 для автовизначення на основі доступних ядер CPU. |
| `MAX_MEGAPIXELS` | `0` (необмежено) | Максимальна дозволена роздільна здатність зображення в мегапікселях. Задайте 0 для необмеженого. |
| `MAX_WORKER_THREADS` | `0` (авто) | Максимальна кількість робочих потоків для обробки зображень. Задайте 0 для автовизначення на основі доступних ядер CPU. |
| `PROCESSING_TIMEOUT_S` | `0` (без обмеження) | Максимальний час обробки на один запит у секундах. Задайте 0 для відсутності таймауту. |
| `MAX_PIPELINE_STEPS` | `20` | Максимальна кількість кроків у конвеєрі. Задайте 0 для відсутності обмеження. |
| `MAX_CANVAS_PIXELS` | `0` (без обмеження) | Максимальний розмір полотна в пікселях для вихідних зображень. Задайте 0 для відсутності обмеження. |
| `MAX_SVG_SIZE_MB` | `0` (необмежено) | Максимальний розмір файлу SVG у мегабайтах. Задайте 0 для необмеженого. |
| `MAX_SPLIT_GRID` | `100` | Максимальний розмір сітки для інструмента розділення зображення. |
| `MAX_SVG_SIZE_MB` | `50` | Найбільший SVG, який приймається перед очищенням, у мегабайтах. Тут `0` поводиться інакше, ніж у сусідніх рядках: він повністю знімає обмеження розміру до розбору, а не піднімає його, тож цю змінну краще лишити заданою. |
| `MAX_PDF_PAGES` | `0` (необмежено) | Максимальна кількість сторінок PDF для конвертації PDF-у-зображення. Задайте 0 для необмеженого. |
### Очищення {#cleanup}
@@ -82,7 +105,7 @@ i18n_output_hash: 498d74ca4a81
| Змінна | За замовчуванням | Опис |
|---|---|---|
| `DEFAULT_THEME` | `light` | Типова тема для нових сеансів. `light` або `dark`. |
| `DEFAULT_THEME` | `light` | Типова тема для нових сеансів. `light`, `dark` або `system`. |
| `DEFAULT_LOCALE` | `en` | Типова мова інтерфейсу. |
| `DEFAULT_TOOL_VIEW` | `sidebar` | Типове компонування інструментів. `sidebar` або `fullscreen`. |
@@ -124,13 +147,13 @@ services:
image: postgres:17-alpine
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_PASSWORD: snapotter # Змініть це для нелокальних розгортань
POSTGRES_DB: snapotter
volumes:
- SnapOtter-pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
interval: 10s
timeout: 5s
retries: 12
+5 -4
View File
@@ -1,8 +1,9 @@
---
description: "Як зробити внесок у SnapOtter. Звіти про помилки, запити на функції, pull request-и та вимоги CLA."
i18n_source_hash: 528802503035
i18n_source_hash: 6c920a5f83e0
i18n_provenance: human
i18n_output_hash: 12a7eac534af
i18n_output_hash: 624e5a689563
i18n_hash_version: 2
---
# Внесок {#contributing}
@@ -53,7 +54,7 @@ i18n_output_hash: 12a7eac534af
### Передумови {#prerequisites}
- Node.js 22+
- Node.js 22.22+
- pnpm 9+
- Python 3.11+ (лише для інструментів ШІ)
- Docker (необов'язково, для повного інтеграційного тестування)
@@ -71,7 +72,7 @@ docker compose -f docker-compose.dev.yml up -d
# Install dependencies
pnpm install
# Start dev servers (web on :1349, API on :13490)
# Start dev servers (web on :1351, API on :13490)
pnpm dev
```
+34 -14
View File
@@ -1,8 +1,9 @@
---
description: "Схема бази даних PostgreSQL, таблиці, міграції та процедури резервного копіювання для SnapOtter."
i18n_source_hash: 50d5d4f220cf
i18n_provenance: human
i18n_output_hash: 8b7453bf7c82
i18n_source_hash: a68264552836
i18n_provenance: machine
i18n_output_hash: 5de59a4b91b6
i18n_hash_version: 2
---
# База даних {#database}
@@ -145,6 +146,17 @@ API-ключі для програмного доступу. Необробле
| `details` | jsonb | Дані, специфічні для дії |
| `createdAt` | timestamp | Час дії |
### user_preferences {#user-preferences}
Стан інтерфейсу для кожного користувача з ключем за назвою налаштування. Зберігає закріплені інструменти головної сторінки, які записуються через `PUT /api/v1/preferences`.
| Стовпець | Тип | Примітки |
|---|---|---|
| `userId` | text | Зовнішній ключ на users, каскадне видалення. Первинний ключ разом із `key` |
| `key` | text | Назва налаштування. Первинний ключ разом із `userId` |
| `value` | jsonb | Вміст налаштування |
| `updatedAt` | timestamp | Час останнього запису |
## Міграції {#migrations}
Drizzle відповідає за міграції схеми. Файли міграцій знаходяться у `apps/api/drizzle/`. Під час розробки:
@@ -159,27 +171,35 @@ npx drizzle-kit migrate # apply pending migrations
## Резервне копіювання та відновлення {#backup-and-restore}
Реляційна база даних знаходиться в томі `SnapOtter-pgdata` контейнера Postgres, а не в томі `/data` застосунку.
Реляційна база даних знаходиться в томі `SnapOtter-pgdata` контейнера Postgres, а не в томі `/data` програми.
**Варіант 1: pg_dump (рекомендовано)**
**Логічна резервна копія з перевіркою (рекомендовано)**
```bash
# Dump the database while the stack is running
docker exec SnapOtter-postgres pg_dump -U snapotter snapotter > backup.sql
# Dump into PostgreSQL's portable custom archive format
docker exec SnapOtter-postgres \
pg_dump --format=custom --no-owner -U snapotter snapotter > snapotter.dump
test -s snapotter.dump
docker exec -i SnapOtter-postgres pg_restore --list < snapotter.dump >/dev/null
# Restore into a fresh database
cat backup.sql | docker exec -i SnapOtter-postgres psql -U snapotter snapotter
# Restore into a fresh/disposable target first and fail on the first SQL error
docker exec -i SnapOtter-postgres \
pg_restore --exit-on-error --clean --if-exists --no-owner \
-U snapotter -d snapotter < snapotter.dump
```
**Варіант 2: Знімок тому**
Цей дамп бази даних не містить збережених об’єктів бібліотеки в `/data/files` або тривалому стані BullMQ у Redis. Створюйте резервні копії та відновлюйте їх за допомогою скоординованої процедури в [Безпека та посилення](/uk/guide/security#backup-and-recovery).
**Знімок холодного обсягу**
```bash
# Stop the stack, then snapshot the pgdata volume
docker compose down
docker run --rm -v SnapOtter-pgdata:/data -v $(pwd)/backup:/backup \
alpine tar czf /backup/snapotter-pgdata.tar.gz -C /data .
# Stop every service first, then use your storage platform to snapshot the
# PostgreSQL, app-data, and Redis volumes as one crash-consistent set.
docker compose -f docker/docker-compose.yml stop
```
Не копіюйте живий каталог даних PostgreSQL за допомогою `tar`. Створюйте префікси імен томів за проектом, тому вирішуйте ідентифікатори змонтованих томів із `docker inspect` або вашої платформи зберігання, а не припускайте буквальну мітку `SnapOtter-pgdata`.
### Міграція з 1.x (SQLite) {#migrating-from-1-x-sqlite}
Оновлення з SnapOtter 1.x має власний посібник: див. [Оновлення з 1.x до 2.0](./upgrading). Коротко: повторно використайте свій наявний том `/data`, і 2.0 автоматично виявить та імпортує `/data/snapotter.db` під час першого запуску (або встановіть `SQLITE_MIGRATE_PATH`, щоб явно вказати на нього). Спершу зробіть резервну копію всього тому `/data`, а не лише `snapotter.db`: 1.x використовує режим SQLite WAL, тож зупинений контейнер часто залишає більшість своїх даних у `snapotter.db-wal` поруч із майже порожнім `snapotter.db`.
+24 -13
View File
@@ -1,8 +1,9 @@
---
description: "Розгорніть SnapOtter у продакшені за допомогою Docker. Вимоги до апаратного забезпечення, налаштування GPU та конфігурації зворотного проксі для Nginx, Traefik і Cloudflare."
i18n_output_hash: 4d221e5eaf97
i18n_source_hash: 98172965118b
i18n_source_hash: 2a722f86da75
i18n_provenance: human
i18n_output_hash: a466f9f8a47f
i18n_hash_version: 2
---
# Розгортання {#deployment}
@@ -47,7 +48,7 @@ services:
# - MAX_USERS=0 # Max user accounts
# --- Networking ---
# - TRUST_PROXY=true # Trust X-Forwarded-For headers (set false if not behind a proxy)
# - TRUST_PROXY=loopback,linklocal,uniquelocal # Which peers may set the client IP via X-Forwarded-For (default shown)
# --- Bind mount permissions ---
# - PUID=1000 # Match your host user's UID (run: id -u)
@@ -82,7 +83,7 @@ services:
- SnapOtter-pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
interval: 10s
timeout: 5s
retries: 12
@@ -170,13 +171,13 @@ services:
container_name: SnapOtter-postgres
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_PASSWORD: snapotter # Змініть це для нелокальних розгортань
POSTGRES_DB: snapotter
volumes:
- SnapOtter-pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
interval: 10s
timeout: 5s
retries: 12
@@ -207,13 +208,17 @@ volumes:
docker compose -f docker-compose-gpu.yml up -d
```
Перевірте виявлення CUDA у логах:
### Перевірте прискорення GPU {#verify-gpu-acceleration}
Перевірте виявлення CUDA в журналах:
```bash
docker logs SnapOtter 2>&1 | head -20
# Look for: [gpu] CUDA available via torch
```
Якщо інструменти штучного інтелекту працюють на ЦП, навіть якщо `--gpus all` і NVIDIA Container Toolkit налаштовано правильно, переінсталюйте відповідний комплект (наприклад, Background Removal) із **Налаштування → Функції штучного інтелекту**. Інсталятор відновлює збірку графічного процесора ONNX Runtime, яку збірка лише для центрального процесора, залучена іншим пакетом (наприклад, транскрипцією), інакше може затіняти в спільному середовищі ШІ. Якщо перевстановлення з інтерфейсу користувача не відновлює GPU на старішому образі, перегляньте посібник із відновлення в [випуск №490](https://github.com/snapotter-hq/SnapOtter/issues/490).
## Вимоги до апаратного забезпечення {#hardware-requirements}
Ці цифри отримані з бенчмарків на низці систем, від сучасної робочої станції amd64 з NVIDIA RTX 4070 до Raspberry Pi, на яких запускали весь каталог інструментів і перебирали ліміти ресурсів Docker, щоб знайти реальний мінімум.
@@ -436,11 +441,11 @@ securityContext:
| `AUTH_ENABLED` | `true` | Увімкнути/вимкнути вимогу входу |
| `DEFAULT_USERNAME` | `admin` | Початкове ім'я адміністратора |
| `DEFAULT_PASSWORD` | `admin` | Початковий пароль адміністратора (примусова зміна при першому вході) |
| `MAX_UPLOAD_SIZE_MB` | `100` | Ліміт завантаження на файл |
| `MAX_BATCH_SIZE` | `100` | Максимум файлів на пакетний запит |
| `MAX_UPLOAD_SIZE_MB` | `0` (необмежено) | Ліміт завантаження на файл у МБ. Образ постачається зі значенням `0`; збірка з вихідного коду стартує зі 100 |
| `MAX_BATCH_SIZE` | `0` (необмежено) | Максимум файлів на пакетний запит. Образ постачається зі значенням `0`; збірка з вихідного коду стартує зі 100 |
| `RATE_LIMIT_PER_MIN` | `1000` | Запити API за хвилину на IP (встановіть 0, щоб вимкнути) |
| `MAX_USERS` | `0` (необмежено) | Максимальна кількість облікових записів користувачів |
| `TRUST_PROXY` | `true` | Довіряти заголовкам X-Forwarded-For від зворотного проксі |
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Яким вузлам дозволено задавати IP клієнта через `X-Forwarded-For`. Типово лише приватні мережі |
| `PUID` | `999` | Запускати з цим UID (для прав bind mount) |
| `PGID` | `999` | Запускати з цим GID (для прав bind mount) |
| `LOG_LEVEL` | `info` | Рівень деталізації логів: fatal, error, warn, info, debug, trace |
@@ -483,7 +488,13 @@ curl http://localhost:1349/api/v1/health
## Зворотний проксі {#reverse-proxy}
SnapOtter встановлює `TRUST_PROXY=true` за замовчуванням, тож обмеження швидкості й логування використовують реальний IP клієнта із заголовків `X-Forwarded-For`.
Типово `TRUST_PROXY` має значення `loopback,linklocal,uniquelocal`, тож SnapOtter вірить заголовку `X-Forwarded-For` лише від вузла з приватної мережі. Зворотному проксі на тому самому хості, у мережі Docker або у вашій локальній мережі довіра є одразу, а отже обмеження швидкості, захист входу від перебору паролів, журнал аудиту та список дозволених IP в enterprise-версії бачать справжній IP клієнта без жодного налаштування.
Задавайте `TRUST_PROXY=true` лише тоді, коли проксі попереду звертається до SnapOtter з **публічної** адреси, наприклад хмарний балансувальник навантаження в іншій мережі. На безпосередньо відкритому екземплярі це значення віддає `request.ip` під контроль зловмисника: той, хто змінює заголовок, отримує свіжий лічильник обмеження швидкості на кожен запит.
Дві речі варто знати, перш ніж вимірювати IP клієнтів. Docker Desktop на macOS і Windows обслуговує опублікований порт через проксі в просторі користувача, який переписує будь-яку адресу джерела на шлюз віртуальної машини `192.168.65.1`, тож там жодне значення `TRUST_PROXY` не поверне справжнього клієнта; усе, що дивиться в інтернет, розгортайте на Linux. І на будь-якій платформі звернення до опублікованого порту через `localhost` бачиться як шлюз мосту, а не як ваш клієнт, тому перевірка через localhost нічого не каже про те, як визначається справжній клієнт. Повну таблицю значень `TRUST_PROXY` та застереження щодо Docker Desktop наведено в [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md#client-ip-resolution-trust_proxy).
Дві речі важливі для кожного проксі нижче: дозволити великі тіла запитів (завантаження) і не буферизувати відповіді. Проксі-сервер із буферизацією відповідей порушує прогрес SSE і, що більш помітно, робить завантаження великого файлу «початком, але ніколи не завершенням», оскільки проксі-сервер зберігає весь файл перед його передачею. SnapOtter надсилає `X-Accel-Buffering: no` під час завантажень, тому nginx транслює їх, навіть якщо буферизація залишилася в іншому місці, але проксі, крім nginx, потребують явного вимкнення буферизації відповіді (показано в кожній конфігурації нижче). Якщо завантаження частково зупиняється, перше, що потрібно перевірити — буферизаційний проксі-сервер.
### Nginx {#nginx}
@@ -505,7 +516,7 @@ server {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE support (batch progress, feature install progress)
# Потокові відповіді замість буферизації: необхідні для прогресу SSE (пакети, штучний інтелект, встановлення функцій) і для завантаження великих файлів.
proxy_buffering off;
proxy_read_timeout 300s;
}
@@ -549,7 +560,7 @@ images.example.com {
}
```
`flush_interval -1` вимикає буферизацію відповідей, що потрібно для подій прогресу SSE (пакетна обробка, AI-інструменти, встановлення функцій). Продовжені таймаути дозволяють завершити завантаження великих файлів без того, щоб Caddy передчасно закривав з'єднання.
`flush_interval -1` вимикає буферизацію відповіді, необхідну для подій прогресу SSE (пакетна обробка, інструменти штучного інтелекту, встановлення функцій) і для потокового передавання великих файлів замість зупинки. Розширені тайм-аути дозволяють завершити завантаження великих файлів без передчасного закриття зєднання Caddy.
### Cloudflare Tunnels {#cloudflare-tunnels}
+19 -7
View File
@@ -1,8 +1,9 @@
---
description: "Локальне налаштування розробки, команди, конвенції коду та як додати новий інструмент до SnapOtter."
i18n_source_hash: cb03724d2829
i18n_provenance: human
i18n_output_hash: 4a74c237b20d
i18n_source_hash: 56acc1bf9a9b
i18n_provenance: machine
i18n_output_hash: 7be8e7403bd6
i18n_hash_version: 2
---
# Посібник для розробників {#developer-guide}
@@ -11,12 +12,12 @@ i18n_output_hash: 4a74c237b20d
## Передумови {#prerequisites}
- [Node.js](https://nodejs.org/) 22+
- [Node.js](https://nodejs.org/) 22.22+
- [pnpm](https://pnpm.io/) 9+ (`corepack enable && corepack prepare pnpm@latest --activate`)
- [Docker](https://www.docker.com/) (потрібен для локальних Postgres + Redis, збірок контейнерів та функцій ШІ)
- Git
Python 3.10+ потрібен лише якщо ви працюєте над сайдкаром ШІ/ML (видалення фону, апскейлінг, OCR).
Python 3.11+ потрібен лише якщо ви працюєте над сайдкаром ШІ/ML (видалення фону, апскейлінг, OCR).
## Налаштування {#setup}
@@ -32,10 +33,10 @@ pnpm dev
| Сервіс | URL | Примітки |
|----------|--------------------------|------------------------------------|
| Фронтенд | http://localhost:1349 | Сервер розробки Vite, проксіює /api |
| Фронтенд | http://localhost:1351 | Сервер розробки Vite, проксіює /api |
| Бекенд | http://localhost:13490 | Fastify API (доступ через проксі) |
Відкрийте http://localhost:1349 у вашому браузері. Увійдіть з `admin` / `admin`. Вам буде запропоновано змінити пароль під час першого входу.
Відкрийте http://localhost:1351 у вашому браузері. Увійдіть з `admin` / `admin`. Вам буде запропоновано змінити пароль під час першого входу.
## Структура проєкту {#project-structure}
@@ -220,6 +221,17 @@ docker build -f docker/Dockerfile -t snapotter:latest .
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile -t snapotter:latest .
```
## Домени версій випуску {#release-version-domains}
SnapOtter навмисно має три домени версій. Не копіюйте один домен в інший під час випуску:
- Версія випуску програми охоплює кореневий маніфест, усі пакети приватної робочої області та `APP_VERSION`. Semantic-release надає це значення, а `pnpm version:sync <version>` оновлює кожну робочу область перед випуском програми.
- OpenAPI `info.version` — це стабільний публічний великий контракт API. Усі локалізовані специфікації залишаються на `<major>.0.0` для випусків сумісних програм і змінюються лише тоді, коли контракт API переходить на нову основну версію.
- `docker/feature-manifest.json` зберігає `imageVersion: 2.0.0` як незмінну епоху зберігання наборів функцій. Ці шляхи архіву v2 не є версіями пакетів програм. Точне оптичне розпізнавання символів використовує формат виконання версії 3 і окремо записує походження випуску програми.
`tests/unit/infra/release-version-policy.test.ts` забезпечує дотримання цих меж. Домен нової версії або міграція повинні разом оновити цей контракт і відповідний дизайн міграції артефакту.
Незалежні значення API і застарілого пакета знаходяться в `config/release-version-policy.json`; синхронізація версії програми ніколи не повинна неявно переписувати цей файл політики.
## Змінні середовища {#environment-variables}
Повний список дивіться у [Посібнику з конфігурації](/uk/guide/configuration). Ключові для розробки:
+8 -7
View File
@@ -1,8 +1,9 @@
---
description: "Теги Docker-образу SnapOtter, тести продуктивності GPU, закріплення версій і мультиплатформна підтримка для AMD64 та ARM64."
i18n_output_hash: 8d481651c9cb
i18n_source_hash: fda322e78b4b
i18n_source_hash: 566e20ca07fc
i18n_provenance: human
i18n_output_hash: b3d630e0aebf
i18n_hash_version: 2
---
# Docker-образ {#docker-image}
@@ -93,13 +94,13 @@ services:
image: postgres:17-alpine
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_PASSWORD: snapotter # Змініть це для нелокальних розгортань
POSTGRES_DB: snapotter
volumes:
- SnapOtter-pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
interval: 10s
timeout: 5s
retries: 12
@@ -140,9 +141,9 @@ volumes:
| Тег | Опис |
|-----|------------|
| `latest` | Останній випуск |
| `1.11.0` | Точна версія |
| `1.11` | Останній патч у 1.11.x |
| `1` | Останній мінорний у 1.x |
| `2.1.0` | Точна версія |
| `2.1` | Останній патч у 2.1.x |
| `2` | Останній мінорний у 2.x |
## Платформи {#platforms}
+27 -60
View File
@@ -1,8 +1,9 @@
---
description: "Встановіть SnapOtter за допомогою Docker однією командою. Включає налаштування Docker Compose, збирання з вихідного коду й повний огляд функцій."
i18n_output_hash: a97c15d102b5
i18n_source_hash: 68bf7f60b68d
i18n_provenance: human
i18n_source_hash: 8040133a6982
i18n_provenance: machine
i18n_output_hash: 5859f9d6859f
i18n_hash_version: 2
---
# Початок роботи {#getting-started}
@@ -17,7 +18,7 @@ i18n_provenance: human
docker run -d --name SnapOtter -p 1349:1349 -v SnapOtter-data:/data snapotter/snapotter:latest
```
Цей єдиний контейнер запускає все, що йому потрібно: без встановленого `DATABASE_URL` він запускає власні PostgreSQL і Redis на інтерфейсі loopback (вбудований режим) і зберігає всі дані в томі `SnapOtter-data`. Це найшвидший спосіб спробувати SnapOtter або самостійно розмістити його в homelab. Для продакшену запустіть стек [Docker Compose](#docker-compose) нижче, який тримає PostgreSQL і Redis у власних контейнерах. Вбудований режим працює від root (за замовчуванням) і автоматично вимикається, щойно ви встановлюєте `DATABASE_URL`.
Цей єдиний контейнер запускає все, що йому потрібно: без встановлення `DATABASE_URL` він запускає власний PostgreSQL і Redis на інтерфейсі петлі (вбудований режим) і зберігає всі дані в тому `SnapOtter-data`. Це найшвидший спосіб спробувати SnapOtter або самостійне розміщення в домашній лабораторії. Для виробництва використовуйте [канонічний стек Docker Compose](#docker-compose), який зберігає PostgreSQL і Redis у власних контейнерах. Вбудований режим працює як root (за замовчуванням) і вимикається автоматично, щойно ви встановите `DATABASE_URL`.
Встановлюєте на Raspberry Pi, старому ноутбуці чи невеликому VPS? Див. [Робота на слабкому обладнанні](/uk/guide/low-resource): там є покроковий посібник із підібраними налаштуваннями й пояснення, чого очікувати від обмеженого обладнання.
@@ -40,7 +41,7 @@ SnapOtter містить анонімну продуктову аналітик
docker run -d --name SnapOtter -p 1349:1349 --gpus all -v SnapOtter-data:/data snapotter/snapotter:latest
```
Потребує [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html). Автоматично переходить на CPU, коли CUDA недоступний. Прискорення на iGPU Intel/AMD через VA-API, Quick Sync або OpenCL наразі не підтримується для AI-інференсу. Див. [Docker Tags](/uk/guide/docker-tags) щодо бенчмарків.
Потрібен [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html). Автоматично повертається до ЦП, коли CUDA недоступна. Прискорення Intel/AMD iGPU через VA-API, Quick Sync або OpenCL на сьогодні не підтримується для висновків ШІ. Див. [Теги Docker](/uk/guide/docker-tags) для тестів. Якщо інструменти штучного інтелекту працюють на ЦП, незважаючи на `--gpus all`, див. [Перевірте прискорення GPU](/uk/guide/deployment#verify-gpu-acceleration).
:::
::: details Також на GHCR
@@ -53,65 +54,31 @@ docker run -d --name SnapOtter -p 1349:1349 -v SnapOtter-data:/data ghcr.io/snap
## Docker Compose {#docker-compose}
```yaml
services:
SnapOtter:
image: snapotter/snapotter:latest # or ghcr.io/snapotter-hq/snapotter:latest
ports:
- "1349:1349"
volumes:
- SnapOtter-data:/data
environment:
- AUTH_ENABLED=true
- DEFAULT_USERNAME=admin
- DEFAULT_PASSWORD=admin
- DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter
- REDIS_URL=redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
Використовуйте робочий файл, який підтримується та перевіряється з кожним випуском, замість копіювання скороченого прикладу Compose із цієї сторінки:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_DB: snapotter
volumes:
- SnapOtter-pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
interval: 10s
timeout: 5s
retries: 12
```bash
install -d -m 700 snapotter && cd snapotter
curl --proto '=https' --tlsv1.2 -fsSLo docker-compose.yml \
https://raw.githubusercontent.com/snapotter-hq/SnapOtter/v2.1.0/docker/docker-compose.yml
redis:
image: redis:8-alpine
command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"]
volumes:
- SnapOtter-redisdata:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 12
# Keep generated service credentials out of shell history and world-readable files.
umask 077
POSTGRES_PASSWORD="$(openssl rand -hex 32)"
REDIS_PASSWORD="$(openssl rand -hex 32)"
printf 'POSTGRES_PASSWORD=%s\nREDIS_PASSWORD=%s\n' \
"$POSTGRES_PASSWORD" "$REDIS_PASSWORD" > .env
volumes:
SnapOtter-data:
SnapOtter-pgdata:
SnapOtter-redisdata:
docker compose -f docker-compose.yml pull
docker compose -f docker-compose.yml up -d --no-build
```
Див. [Конфігурація](/uk/guide/configuration) щодо всіх змінних середовища.
Канонічний [`docker/docker-compose.yml`](https://github.com/snapotter-hq/SnapOtter/blob/v2.1.0/docker/docker-compose.yml) включає всі чотири томи часу виконання, перевірки працездатності, обмеження ресурсів, надійну конфігурацію Redis, закріплені зображення бази даних/кешу та поточний захист контейнера. Змініть стандартний пароль адміністратора одразу після першого входу. Для відтворюваного розгортання прикріпіть зображення програми SnapOtter до тегу випуску або перевіреного дайджесту замість `latest`.
Перегляньте [Конфігурація](/uk/guide/configuration) для всіх змінних середовища та [Безпека та зміцнення](/uk/guide/security) для секретів, мережевої політики та вказівок щодо резервного копіювання.
## Збирання з вихідного коду {#build-from-source}
**Передумови:** Node.js 22+, pnpm 9+, Docker (для Postgres + Redis), Python 3.10+ (для функцій AI), Git.
**Передумови:** Node.js 22.22+, pnpm 9+, Docker (для Postgres + Redis), Python 3.11+ (для функцій AI), Git.
```bash
git clone https://github.com/snapotter-hq/SnapOtter.git
@@ -121,7 +88,7 @@ pnpm install
pnpm dev
```
- Фронтенд: [http://localhost:1349](http://localhost:1349)
- Фронтенд: [http://localhost:1351](http://localhost:1351)
- Бекенд: [http://localhost:13490](http://localhost:13490)
## Що ви можете робити {#what-you-can-do}
@@ -130,11 +97,11 @@ pnpm dev
| Модальність | Кількість | Приклади інструментів |
|----------|-------|---------------|
| **Зображення** | 105 | Зміна розміру, Обрізання, Стиснення, Конвертація, Видалення фону, Upscale, OCR, Водяний знак, Колаж, Colorize, Інструменти GIF, пресети форматів |
| **Зображення** | 107 | Зміна розміру, Обрізання, Стиснення, Конвертація, Видалення фону, Upscale, OCR, Водяний знак, Колаж, Colorize, Інструменти GIF, пресети форматів |
| **Відео** | 57 | Обрізання, Обрізання за краями, Стиснення, Конвертація, Об'єднання, Витягнення аудіо, Автосубтитри, Відео у GIF, Зміна розміру, Стабілізація, пресети форматів |
| **Аудіо** | 27 | Обрізання, Об'єднання, Конвертація, Нормалізація, Зменшення шуму, Транскрипція, Зсув висоти тону, Затухання, Створення рингтонів, пресети форматів |
| **PDF / Документ** | 42 | Об'єднання, Розділення, Стиснення, OCR, Водяний знак, Редагування (redact), Word у PDF, Excel у PDF, Обертання, Захист, Відновлення |
| **Файли** | 10 | CSV у JSON, JSON у XML, Об'єднання CSV, Розділення CSV, Створення ZIP, Витягнення ZIP, Створення діаграм, YAML/JSON |
| **PDF / Документ** | 29 | Об'єднання, Розділення, Стиснення, OCR, Водяний знак, Редагування (redact), Word у PDF, Excel у PDF, Обертання, Захист, Відновлення |
| **Файли** | 23 | CSV у JSON, JSON у XML, Об'єднання CSV, Розділення CSV, Створення ZIP, Витягнення ZIP, Створення діаграм, YAML/JSON |
### Конвеєри {#pipelines}
+4 -3
View File
@@ -1,7 +1,8 @@
---
i18n_source_hash: f5de74aee1b9
i18n_source_hash: 521c03a6416c
i18n_provenance: machine
i18n_output_hash: 27b701493717
i18n_output_hash: e8532c6c8a52
i18n_hash_version: 2
---
# Робота на слабкому обладнанні {#low-resource-setups}
@@ -59,7 +60,7 @@ services:
image: postgres:17-alpine
environment:
- POSTGRES_USER=snapotter
- POSTGRES_PASSWORD=snapotter
- POSTGRES_PASSWORD=snapotter # Змініть це для нелокальних розгортань
- POSTGRES_DB=snapotter
volumes:
- ./postgres-data:/var/lib/postgresql/data
+12 -7
View File
@@ -1,8 +1,9 @@
---
description: "Налаштуйте провізіонінг SCIM 2.0 для синхронізації користувачів і груп з вашого постачальника ідентифікації до SnapOtter. Охоплює Okta, Azure AD / Entra ID та власні інтеграції."
i18n_source_hash: bbd50119ec12
i18n_source_hash: 06ee702b386e
i18n_provenance: human
i18n_output_hash: 8b95a7ec2ac6
i18n_output_hash: 40fd5043d957
i18n_hash_version: 2
---
# Провізіонінг SCIM {#scim-provisioning}
@@ -17,7 +18,7 @@ SnapOtter реалізує SCIM 2.0 (System for Cross-domain Identity Management
- Запущений екземпляр SnapOtter, доступний за публічним URL
- Ключ ліцензії enterprise з функцією `scim`
- Доступ адміністратора до SnapOtter (для створення чи відкликання токена SCIM потрібен дозвіл `users:manage`)
- Вбудований обліковий запис SnapOtter `admin` із повним ефективним набором дозволів. Делегована спеціальна роль або ключ API адміністратора, у якому відсутні будь-які дозволи адміністратора, не можуть створити або відкликати глобальний маркер SCIM.
- Доступ адміністратора до налаштувань провізіонінгу вашого постачальника ідентифікації
## Швидкий старт {#quick-start}
@@ -34,7 +35,7 @@ curl -X POST https://photos.example.com/api/v1/enterprise/scim/token \
```json
{
"token": "a1b2c3d4e5f6...",
"token": "so_scim_v2_a1b2c3d4e5f6...",
"message": "Save this token - it cannot be retrieved again"
}
```
@@ -49,15 +50,19 @@ curl -X POST https://photos.example.com/api/v1/enterprise/scim/token \
### Створення токена {#generating-a-token}
`POST /api/v1/enterprise/scim/token` створює новий токен SCIM. Ця кінцева точка потребує дійсної сесії з дозволом `users:manage`.
`POST /api/v1/enterprise/scim/token` генерує новий маркер SCIM. Оскільки маркер може надавати та змінювати користувачів у примірнику, для цієї кінцевої точки потрібна вбудована роль `admin` із повним ефективним набором дозволів адміністратора. Утримання `users:manage` у спеціальній ролі недостатньо.
Токен повертається у відкритому вигляді рівно один раз. SnapOtter зберігає лише хеш scrypt. Якщо ви втратите токен, відкличте його та створіть новий.
Одночасно активний лише один токен SCIM. Створення нового токена замінює попередній.
::: warning Перевипуск токена після оновлення
Застарілі неверсійовані маркери SCIM відхилено. Після оновлення до випуску, який видає токени `so_scim_v2_...`, згенеруйте новий токен і оновіть постачальника ідентифікаційної інформації, перш ніж відновити надання.
:::
### Відкликання токена {#revoking-a-token}
`DELETE /api/v1/enterprise/scim/token` відкликає поточний токен SCIM. Ця кінцева точка також потребує `users:manage`.
`DELETE /api/v1/enterprise/scim/token` скасовує поточний маркер SCIM. Він має ті ж повні вбудовані вимоги адміністратора, що й генерація маркерів.
### Обмеження частоти запитів {#rate-limiting}
@@ -279,7 +284,7 @@ Azure провізіонує користувачів і групи за фік
### 401 "Invalid token" {#_401-invalid-token}
Токен не відповідає збереженому хешу. Це трапляється, якщо токен було відкликано та створено заново. Оновіть токен у налаштуваннях провізіонінгу свого IdP.
Маркер має неправильний формат, використовує вилучений неверсійний формат або не відповідає збереженому хешу. Згенеруйте поточний маркер `so_scim_v2_...` і оновіть його в налаштуваннях ініціалізації IdP.
### 401 "SCIM not configured" {#_401-scim-not-configured}
+88 -157
View File
@@ -1,8 +1,9 @@
---
description: "Посібник із посилення безпеки для SnapOtter. Безпека контейнерів, мережева ізоляція, секрети Docker, розгортання в Kubernetes та артефакти відповідності."
i18n_source_hash: 986f7658430c
i18n_provenance: human
i18n_output_hash: 6b9158af7666
i18n_source_hash: 9ff337fa0417
i18n_provenance: machine
i18n_output_hash: 09a77c679598
i18n_hash_version: 2
---
# Безпека та посилення {#security-hardening}
@@ -11,133 +12,45 @@ SnapOtter обробляє файли повністю на вашій інфр
Контейнер працює від імені виділеного не-root користувача (`snapotter`) з усіма скиненими можливостями Linux, окрім мінімального необхідного набору. Щодо повної політики розкриття вразливостей і архітектури безпеки див. [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) на GitHub.
## Посилення контейнера {#container-hardening}
## Зміцнення контейнера {#container-hardening}
[Стандартний docker-compose.yml](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) містить посилення безпеки для продакшену. Ось розбір кожного параметра й чому він важливий:
Канонічні файли Compose [CPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) і [GPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose-gpu.yml) є джерелом правди. Не копіюйте скорочений приклад у виробництво; розгорнути файл із тегу випуску, який ви перевірили.
```yaml
services:
SnapOtter:
image: snapotter/snapotter:latest
ports:
# Bind to localhost only for internet-facing deployments:
- "127.0.0.1:1349:1349"
volumes:
- SnapOtter-data:/data
- SnapOtter-workspace:/tmp/workspace
environment:
- AUTH_ENABLED=true
- DEFAULT_PASSWORD=change-me-immediately
- RATE_LIMIT_PER_MIN=1000
- DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter
- REDIS_URL=redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
Обидва стеки застосовують такі елементи керування:
# --- Resource limits ---
mem_limit: 6g # Prevents runaway memory from crashing the host
memswap_limit: 6g # No swap - fail fast instead of degrading the host
cpus: 4 # Cap CPU usage to 4 cores
pids_limit: 512 # Prevents fork bombs
- Обмеження пам'яті, підкачки, процесора та PID містять нестандартну власну обробку.
- Кожна служба втрачає всі можливості Linux. Додаток додає лише `CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILL` для володіння томом, одностороннє видалення ідентифікаційної інформації `gosu` і витончене пересилання сигналу. PostgreSQL і Redis отримують лише ту підмножину, яку потребують їхні офіційні точки входу.
# --- Capability restrictions ---
cap_drop:
- ALL # Drop ALL Linux capabilities first
cap_add:
- CHOWN # Needed for volume permission setup
- SETUID # Needed for gosu privilege drop (root -> snapotter)
- SETGID # Needed for gosu privilege drop
- DAC_OVERRIDE # Needed for volume permission setup
- FOWNER # Needed for volume permission setup
`security_opt: [no-new-privileges:true]` не дозволяє процесам у контейнерах програми, PostgreSQL і Redis отримати додаткові привілеї. Це залишається сумісним із `gosu`: точка входу починається від імені root, готує томи та передається лише виділеному користувачеві `snapotter`.
# --- Logging ---
logging:
driver: json-file
options:
max-size: "50m" # Rotate logs at 50 MB
max-file: "5" # Keep 5 rotated log files
— Вхідні дані зображень PostgreSQL і Redis закріплені дайджестом. Програму також слід прикріпити до тегу перевіреного випуску або дайджесту, а не до `latest`.
# --- Health check ---
healthcheck:
test: ["CMD", "curl", "-sf", "--max-time", "5", "http://localhost:1349/api/v1/health"]
interval: 30s
timeout: 5s
start_period: 60s
retries: 3
— Перевірки працездатності, обмежена ротація журналів JSON, надійний Redis AOF і політика перезапуску визначаються централізовано в канонічних файлах.
shm_size: "2gb" # Required for Python ML shared memory
restart: unless-stopped
postgres:
image: postgres:17-alpine
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_DB: snapotter
volumes:
- SnapOtter-pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
interval: 10s
timeout: 5s
retries: 12
start_period: 15s
redis:
image: redis:8-alpine
command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"]
volumes:
- SnapOtter-redisdata:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 12
start_period: 10s
volumes:
SnapOtter-data:
SnapOtter-workspace:
SnapOtter-pgdata:
SnapOtter-redisdata:
```
### Чому `no-new-privileges` не встановлено {#why-no-new-privileges-is-not-set}
`security_opt: [no-new-privileges:true]` навмисно пропущено. Точка входу стартує від root, щоб виправити власника томів, потім скидається до користувача `snapotter` через [gosu](https://github.com/tianon/gosu), що потребує setuid. Щойно скидання привілеїв завершується, процес працює від імені `snapotter` з усіма можливостями, окрім п'яти перелічених вище, видаленими.
Якщо ви використовуєте Kubernetes або прапорець `--user` Docker для запуску безпосередньо від імені не-root (в обхід gosu), `no-new-privileges` безпечно вмикати.
Для розгортання з підключенням до Інтернету прив’яжіть порт 1349 до loopback і завершіть TLS на підтримуваному зворотному проксі-сервері. Створюйте унікальні облікові дані PostgreSQL і Redis, зберігайте секрети в захищених файлах або в менеджері секретів і негайно змінюйте початковий пароль адміністратора.
### Чому `read_only` не встановлено {#why-read-only-is-not-set}
`read_only: true` не встановлено, оскільки перепризначення PUID/PGID записує в `/etc/passwd` і `/etc/group` під час запуску. Якщо ви використовуєте прапорець `--user` Docker або `runAsUser` Kubernetes замість PUID/PGID, ви можете безпечно увімкнути кореневу файлову систему тільки для читання.
`read_only: true` не встановлено, оскільки перевідповідання PUID/PGID записує в `/etc/passwd` і `/etc/group` під час запуску. Якщо ви використовуєте прапор Docker `--user` або Kubernetes `runAsUser` замість PUID/PGID, ви можете безпечно ввімкнути кореневу файлову систему лише для читання.
## Мережева ізоляція {#network-isolation}
## Ізоляція мережі {#network-isolation}
Під час звичайної роботи контейнер робить **нуль вихідних мережевих з'єднань**. Уся обробка файлів відбувається локально з використанням вбудованих бібліотек.
Обробка файлів є локальною, але інсталяція за замовчуванням **не є системою без виходу**. Анонімна аналітика продуктів використовує PostHog, а звіти про збої використовують Sentry, якщо ввімкнено телеметрію. Встановіть `SNAPOTTER_TELEMETRY=0` (або вимкніть аналітику в меню «Налаштування» > «Система» > «Конфіденційність»), щоб вимкнути обидва параметри. SnapOtter ніколи не включає в ці події завантажені файли, імена файлів, вихід OCR, текст документа чи інший вміст файлу.
```
Browser --> Reverse Proxy (TLS) --> SnapOtter container --> (nothing)
```
Інший вихідний трафік керується функціями: інсталяція комплекту/моделі штучного інтелекту завантажує підписані вхідні дані випуску; Імпорт URL-адреси отримує загальнодоступну URL-адресу, яку запитує користувач; і явно налаштовані OIDC, SAML, OpenTelemetry, веб-хуки, S3-сумісне сховище або подібні інтеграції зв’язуються з пунктами призначення, вибраними адміністратором. Завантаження моделей під час виконання за замовчуванням вимкнено. Установіть `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1` лише для явного ввімкнення автоматичних резервних завантажень. [Офлайн-пакет імпорту](/uk/guide/deployment) може надавати функції AI без виходу моделі середовища виконання.
Єдиний виняток — це **завантаження моделей AI**: коли користувач встановлює бандл функцій AI через UI, контейнер завантажує заздалегідь зібраний архів бандла з Hugging Face, плюс кілька окремих файлів моделей із GitHub Releases, Google Storage і PyPI. Ці завантаження відбуваються один раз на бандл і зберігаються в томі `/data`.
**Рекомендації щодо брандмауера:**
**Рекомендації щодо фаєрволу:**
| Сценарій | Правило для вихідного трафіку |
|Сценарій|Вихідне правило|
|---|---|
| Ізольований (без AI) | Заблокувати весь вихідний трафік від контейнера |
| Потрібні бандли AI | Дозволити HTTPS до `huggingface.co`, `*.xethub.hf.co`, `cdn-lfs.huggingface.co`, `github.com`, `objects.githubusercontent.com`, `storage.googleapis.com`, `pypi.org`, `files.pythonhosted.org` під час встановлення, потім заблокувати |
| Після встановлення AI | Заблокувати весь вихідний трафік — моделі кешуються локально |
|З повітряним проміжком|Встановіть `SNAPOTTER_TELEMETRY=0` і `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0`, використовуйте офлайн-імпорт пакетів AI, вимкніть імпорт URL-адрес і зовнішню інтеграцію, а потім заблокуйте вихід|
|Телеметрія за замовчуванням|Дозволити кінцеві точки PostHog і Sentry, указані в журналах вашого браузера/мережі; вимкнути телеметрію, якщо політика не дозволяє їх|
|Потрібні пакети AI|Під час встановлення дозвольте HTTPS до `huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org`; потім заблокуйте ці хости|
|Зовнішні інтеграції|Дозволити лише точні призначення OIDC/SAML/OTLP/webhook/object-storage, налаштовані адміністратором|
Архіви бандлів обслуговуються зі сховища Xet від Hugging Face, яке передає через кінцеві точки `*.xethub.hf.co` паралельно і завдяки чому завантаження багатогігабайтних бандлів швидке. Якщо ваш фаєрвол дозволяє `huggingface.co`, але блокує `*.xethub.hf.co`, встановлення все одно вдаються, але переходять на повільніше однопотокове завантаження, тож додайте хости Xet до дозволеного списку, щоб залишатися на швидкому шляху. Повністю офлайн-встановлення можуть пропустити все це й натомість використати [Імпорт офлайн-бандлів](/uk/guide/deployment).
Архіви пакетів обслуговуються зі сховища Xet Hugging Face, яке паралельно передається через кінцеві точки `*.xethub.hf.co` і завдяки чому швидко завантажуються пакети на кілька ГБ. Якщо ваш брандмауер дозволяє `huggingface.co`, але блокує `*.xethub.hf.co`, встановлення все одно вдасться, але повернеться до повільнішого однопотокового завантаження, тому внесіть хости Xet у білий список, щоб залишатися на швидкому шляху. Повністю автономна інсталяція може пропустити все це та використовувати [Offline Bundle Import](/uk/guide/deployment).
Щодо конфігурації зворотного проксі (Nginx, Traefik, Caddy, Cloudflare Tunnels) див. [посібник із розгортання](/uk/guide/deployment#reverse-proxy).
Для конфігурації зворотного проксі (Nginx, Traefik, Caddy, Cloudflare Tunnels) див. [Посібник із розгортання](/uk/guide/deployment#reverse-proxy).
## Секрети Docker {#docker-secrets}
@@ -257,83 +170,101 @@ spec:
## Резервне копіювання та відновлення {#backup-and-recovery}
Постійний стан розділений між двома томами:
Виробничий стек Compose визначає чотири томи. Зупиніть вхід і дайте активним завданням завершитися, перш ніж виконувати координоване резервне копіювання, щоб PostgreSQL, Redis і стан файлу описували той самий момент часу.
| Том | Вміст | Критичний? |
|Обсяг|Зміст|Відновлювальне лікування|
|---|---|---|
| `SnapOtter-pgdata` | База даних PostgreSQL (користувачі, налаштування, конвеєри, завдання, журнал аудиту) | Так |
| `/data` (том застосунку) | Завантажені користувачами файли, моделі AI, Python venv | Частково (див. нижче) |
|`SnapOtter-pgdata`|Користувачі PostgreSQL, налаштування, конвеєри, завдання, метадані файлів і журнал аудиту|Критичний; використовуйте швидкий логічний дамп для портативного відновлення|
|`SnapOtter-data`|Збережені бібліотечні об’єкти, журнали та стан AI (`/data/files, /data/logs, /data/ai, /data/ai/venv`)|Створіть резервну копію всього тому; щоб заощадити місце, навмисно пропустіть усі стани ШІ та перевстановіть його комплекти|
|`SnapOtter-redisdata`|Redis AOF для тривалого стану черги BullMQ|Резервне копіювання після призупинення програми та примусового запуску `SAVE`; необхідні для точного відновлення роботи в черзі|
|`SnapOtter-workspace`|Тимчасові ключі зберігання об’єктів (`/tmp/workspace/uploads, /tmp/workspace/outputs`)|Не створюйте резервну копію після того, як усі завдання вичерпано або скасовано; ніколи не викидайте його, поки завдання активні|
У межах тому `/data`:
| Шлях | Вміст | Критичний? |
|---|---|---|
| `/data/uploads/`, `/data/outputs/` | Файли користувачів і результати обробки | Так |
| `/data/ai/` | Завантажені файли моделей AI | Ні (можна завантажити повторно) |
| `/data/venv/` | Віртуальне середовище Python | Ні (перезбирається під час запуску) |
Компонувати зазвичай префікси імен томів із назвою проекту. Розділіть реальний вихідний том із підключеного контейнера замість того, щоб припускати, що відображуване ім’я, наприклад `SnapOtter-data`, є ім’ям тому Docker.
### Резервне копіювання бази даних {#database-backup}
Використовуйте `pg_dump`, щоб зробити резервну копію бази даних, поки стек працює:
Використовуйте спеціальний формат архіву PostgreSQL і перевірте архів, перш ніж розглядати резервну копію як завершену:
```bash
# Dump the database
docker exec SnapOtter-postgres pg_dump -U snapotter snapotter > backup.sql
docker exec SnapOtter-postgres \
pg_dump --format=custom --no-owner -U snapotter snapotter > snapotter.dump
test -s snapotter.dump
docker exec -i SnapOtter-postgres pg_restore --list < snapotter.dump >/dev/null
# Restore into a fresh database
cat backup.sql | docker exec -i SnapOtter-postgres psql -U snapotter snapotter
# Restore only into a fresh/disposable target first; any SQL error fails the command.
docker exec -i SnapOtter-postgres \
pg_restore --exit-on-error --clean --if-exists --no-owner \
-U snapotter -d snapotter < snapotter.dump
```
Як альтернатива, зупиніть стек і зробіть знімок тому `SnapOtter-pgdata`:
Перевірте кожну резервну копію, відновивши її в ізольований стек, перевіривши записи бази даних і контрольні суми файлів і запустивши програму. `tests/qa/backup-restore-drill.sh` репозиторію автоматизує цей шлюз випуску проти явного `QA_IMAGE`.
Якщо натомість ваша платформа робить миттєві знімки томів, що відповідають збоям, спочатку зупиніть увесь стек і зробіть миттєві знімки всіх критичних томів як один набір. Необроблена копія каталогу даних PostgreSQL із запущеного контейнера не є підтримуваною логічною резервною копією.
### Резервне копіювання файлів і черги {#file-and-queue-backup}
Призупиніть програму перед захопленням томів файлів і черги. Використовуйте `docker inspect`, щоб розпізнати фактичну назву тому, змусити Redis зберегти поточний стан і архівувати зі збереженням права власності та дозволів:
```bash
docker compose down
docker run --rm -v SnapOtter-pgdata:/data -v $(pwd)/backup:/backup \
alpine tar czf /backup/snapotter-pgdata.tar.gz -C /data .
docker stop SnapOtter
docker exec SnapOtter-redis redis-cli -a "$REDIS_PASSWORD" --no-auth-warning SAVE
docker stop SnapOtter-redis
DATA_VOLUME="$(docker inspect SnapOtter --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
REDIS_VOLUME="$(docker inspect SnapOtter-redis --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
install -d -m 700 backup
docker run --rm -v "$DATA_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-data.tar.gz -C /source .
docker run --rm -v "$REDIS_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-redis.tar.gz -C /source .
sha256sum backup/snapotter-*.tar.gz > backup/SHA256SUMS
```
### Резервне копіювання файлів користувачів {#user-files-backup}
```bash
# Snapshot the app data volume (excluding re-downloadable AI models)
docker run --rm -v SnapOtter-data:/data -v $(pwd)/backup:/backup \
alpine tar czf /backup/snapotter-files.tar.gz \
--exclude='ai' --exclude='venv' -C /data .
```
Моделі AI разом сягають близько 24 GB для всіх бандлів. Оскільки їх можна завантажити повторно, виключіть `/data/ai/` і `/data/venv/` з резервних копій, щоб заощадити місце. Критичними є лише база даних і файли користувачів.
Перезапустіть Redis перед програмою. Якщо ви навмисно виключаєте `/data/ai`, видаліть усе піддерево AI, а не зберігайте запис `installed.json` без його моделей або віртуального середовища. Зберігайте файли резервних копій у зашифрованому вигляді, з контрольованим доступом і окремо від хоста, на якому запущено SnapOtter.
## Артефакти відповідності {#compliance-artifacts}
Кожен реліз SnapOtter містить такі артефакти безпеки:
Кожен випуск SnapOtter містить такі артефакти безпеки:
| Артефакт | Формат | Де його знайти |
|---|---|---|
| SBOM (CycloneDX) | JSON | Ресурс [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.cdx.json` |
| SBOM (SPDX) | JSON | Ресурс [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.spdx.json` |
| Сканування вразливостей | Trivy JSON | Ресурс [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-trivy.json` |
| Сканування вразливостей | SARIF | Вкладка [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security) |
| Статичний аналіз | CodeQL (JS/TS + Python) | Вкладка [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security), запускається щотижня + на кожен PR |
| Огляд залежностей | Нативний GitHub | Перевірка на кожен PR, дає збій при додаваннях високої серйозності |
| Аудит залежностей Python | pip-audit | Журнал запуску CI при кожному push |
| Політика безпеки | Markdown | [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) у репозиторії |
| Звільнити предметну прив'язку | Канонічна атестація JSON + GitHub | [Випуск GitHub](https://github.com/snapotter-hq/SnapOtter/releases) актив: `snapotter-v{version}-release-subjects.json` |
| Архів SBOM | CycloneDX і SPDX JSON | Випустити активи: `snapotter-v{version}-archive-linux-{arch}-sbom.{cdx,spdx}.json` |
| Зображення SBOM | CycloneDX і SPDX JSON | Випустити активи: `snapotter-v{version}-image-linux-{arch}-sbom.{cdx,spdx}.json` |
| Сканування вразливостей | Trivy JSON | Випустіть активи з відповідними префіксами `archive-linux-{arch}` або `image-linux-{arch}` |
| Сканування вразливостей | SARIF | Вкладка [Безпека GitHub](https://github.com/snapotter-hq/SnapOtter/security). |
| Статичний аналіз | CodeQL (JS/TS + Python) | Вкладка [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security), запускається щотижня + за PR |
| Огляд залежності | GitHub рідний | Перевірка за PR, не вдається виконати додавання високої серйозності |
| Аудит залежностей Python | pip-audit | Журнал запуску CI під час кожного натискання |
| Політика безпеки | Markdown | [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) у сховищі |
| Оновлення залежностей | Dependabot | Автоматизовані щотижневі PR для npm, pip, Docker, Actions |
**Запуск власного сканування:**
Завантажте SBOM з релізу й проскануйте його за допомогою бажаного інструмента:
Завантажте маніфест теми випуску та переконайтеся, що він підтверджений робочим процесом випуску:
```bash
gh attestation verify snapotter-v2.1.0-release-subjects.json \
--repo snapotter-hq/SnapOtter \
--signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml
```
У маніфесті окремо записуються `releaseTag`, `releaseCommit` і `workflowTriggerCommit`. Переконайтеся, що `releaseCommit` є комітом, видаленим із незмінного тегу, а потім перевірте дайджест SHA-256 архіву, зображення, SBOM або сканування, який ви використовуєте, на його запис у `subjects`. Ця відмінність є навмисною: перевірка щойно створеного коміту випуску не змінює ідентифікатор коміту в облікових даних OIDC робочого циклу.
Ви також можете сканувати завантажений SBOM або безпосередньо зображення:
```bash
# Scan with Grype using the CycloneDX SBOM
grype sbom:snapotter-v1.17.2-sbom.cdx.json
grype sbom:snapotter-v2.1.0-image-linux-amd64-sbom.cdx.json
# Scan with Trivy using the SPDX SBOM
trivy sbom snapotter-v1.17.2-sbom.spdx.json
trivy sbom snapotter-v2.1.0-image-linux-amd64-sbom.spdx.json
# Scan the Docker image directly
trivy image snapotter/snapotter:1.17.2
trivy image snapotter/snapotter:2.1.0
```
::: info
SBOM і сканування вразливостей відображають точний образ, опублікований для того релізу. Бандли моделей AI, встановлені після розгортання, не включені до SBOM, оскільки вони завантажуються під час виконання.
::: info
Зображення SBOMs і скановані зображення відображають точне зображення конкретної архітектури, опубліковане для цього випуску. Архів SBOMs і скани описують попередньо зібраний архів окремо. Комплекти моделей AI, встановлені після розгортання, не включені в ці SBOMs, оскільки вони завантажуються під час виконання.
:::
+6 -2
View File
@@ -11,7 +11,7 @@ SnapOtter обробляє файли у п'яти модальностях: з
## Формати зображень {#image-formats}
SnapOtter підтримує понад 55 форматів зображень для введення та 13 форматів для виведення.
SnapOtter підтримує понад 55 форматів зображень для введення та 17 форматів для виведення.
## Формати введення {#input-formats}
@@ -104,7 +104,7 @@ SnapOtter підтримує понад 55 форматів зображень
| PAM | .pam | Sharp (native) | Довільна карта |
| PFM | .pfm | Sharp (native) | Карта з плаваючою комою |
## Формати виведення (13) {#output-formats-13}
## Формати виведення (17) {#output-formats-13}
| Формат | Кодувальник | Контроль якості | Доступний у |
|--------|---------|----------------|-------------|
@@ -121,6 +121,10 @@ SnapOtter підтримує понад 55 форматів зображень
| ICO | ImageMagick CLI | Без втрат | Інструмент Convert |
| JP2 | opj_compress CLI | Коефіцієнт стиснення | Інструмент Convert |
| QOI | Вбудований кодек | Без втрат | Інструмент Convert |
| PSD | ImageMagick CLI | Без втрат | Інструмент Convert |
| PPM | ImageMagick CLI | Без втрат | Інструмент Convert |
| EPS | ImageMagick CLI | Без втрат | Інструмент Convert |
| TGA | ImageMagick CLI | Без втрат | Інструмент Convert |
## Формати відео {#video-formats}
+13 -10
View File
@@ -1,8 +1,9 @@
---
description: "Керуйте користувачами, вбудованими та власними ролями, дозволами, ключами API, командами, сеансами й журналом аудиту в SnapOtter."
i18n_source_hash: 5e28af686c96
i18n_source_hash: bea8955f3aff
i18n_provenance: human
i18n_output_hash: 3ef564692abf
i18n_output_hash: 802ed8983042
i18n_hash_version: 2
---
# Користувачі, ролі та дозволи {#users-roles-permissions}
@@ -82,12 +83,12 @@ SnapOtter містить три вбудовані ролі. Їх не можн
| `pipelines:all` | Переглядати конвеєри всіх користувачів та керувати ними |
| `settings:read` | Переглядати налаштування екземпляра |
| `settings:write` | Змінювати налаштування екземпляра |
| `users:manage` | Створювати, оновлювати та видаляти облікові записи користувачів |
| `users:manage` | Створюйте облікові записи користувачів і керуйте ними в межах повноважень актора |
| `teams:manage` | Створювати, оновлювати та видаляти команди |
| `features:manage` | Встановлювати комплекти AI-функцій та керувати ними |
| `system:health` | Доступ до кінцевих точок стану та готовності |
| `audit:read` | Переглядати журнал аудиту та перелічувати ролі |
| `compliance:manage` | Керувати життєвим циклом GDPR та функціями відповідності |
| `compliance:manage` | Керуйте життєвим циклом GDPR і функціями відповідності; деструктивні операції користувача залишаються обмеженими повноваженнями |
| `webhooks:manage` | Налаштовувати вихідні вебхуки |
| `security:manage` | Керувати налаштуваннями безпеки (список дозволених IP, примусове застосування SSO) |
@@ -110,15 +111,17 @@ curl -X POST http://localhost:1349/api/v1/roles \
Назви ролей мають бути 2-30 символів, малими літерами, буквено-цифровими, з дефісами й підкресленнями.
### Дозволи, зарезервовані для адміністратора {#admin-reserved-permissions}
### Межі делегованого адміністрування {#delegated-administration-boundaries}
Три дозволи зарезервовані для вбудованих ролей і не можуть бути призначені власним ролям:
Усі 17 дозволів можна делегувати за допомогою спеціальних ролей, але адміністративний дозвіл не робить цю роль еквівалентною вбудованій ролі `admin`. Мутації користувача, дозволені `users:manage`, деструктивні операції, дозволені `compliance:manage`, і керування нестандартними ролями, дозволені `security:manage`, обмежені поточними повноваженнями актора:
- `compliance:manage`
- `webhooks:manage`
- `security:manage`
- Вбудовані ролі слідують за `admin` > `editor` > `user`; настроювані ролі знаходяться нижче вбудованих ролей.
- Дозволи цілі повинні міститися в **ефективних** дозволах актора. Таким чином, ключ API із обмеженою областю не може використовувати дозволи, випущені з його області.
- Доступ до інструментів цільової ролі має бути обмежений власним доступом до інструментів актора.
- Вимкнений обліковий запис перевіряється на його початкову роль, якщо ця роль записана як `disabled:<original-role>`.
- Для видалення настроюваної ролі також потрібні повноваження для призначення вбудованого резервного варіанта `user`; недієздатні учасники залишаються недієздатними як `disabled:user`.
API ролей відхиляє будь-який запит, що містить ці дозволи. Лише вбудована роль `admin` має до них доступ.
Глобальні облікові дані та конфігурація суворіші: для видачі або відкликання маркера SCIM та імпорту конфігурації екземпляра потрібна вбудована роль `admin` із повними ефективними повноваженнями адміністратора.
### Дозволи на рівні інструментів {#tool-level-permissions}