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: "Estrutura do monorepo, arquitetura de apps e pacotes, ciclo de vida das requisições e uso de recursos do SnapOtter."
i18n_output_hash: 93555b8e15c0
i18n_source_hash: a53946e760b0
i18n_source_hash: 50e076925c4b
i18n_provenance: human
i18n_output_hash: e541eef7cbf1
i18n_hash_version: 2
---
# Arquitetura {#architecture}
@@ -52,7 +53,7 @@ Tipos TypeScript compartilhados, constantes (como `APP_VERSION` e as definiçõe
### API (`apps/api`) {#api-apps-api}
Um servidor Fastify v5 que expõe 241 rotas de ferramentas em cinco modalidades (image, video, audio, PDF, file) e lida com:
Um servidor Fastify v5 que expõe 243 rotas de ferramentas em cinco modalidades (image, video, audio, PDF, file) e lida com:
- Uploads de arquivos, gerenciamento de espaço de trabalho temporário e armazenamento persistente de arquivos
- Biblioteca de arquivos do usuário (tabela `user_files`): uma edição salva é armazenada por padrão como um novo arquivo independente, ou como uma versão vinculada ao pai quando você sobrescreve o original. Ela registra quais ferramentas foram aplicadas (`toolChain`) e recebe uma miniatura gerada automaticamente para a página Files
- Execução de ferramentas (roteia cada requisição de ferramenta para o motor de imagem ou para a ponte de IA)
+40 -17
View File
@@ -1,8 +1,9 @@
---
description: "Todas as variáveis de ambiente do SnapOtter com valores padrão. Configure autenticação, armazenamento, modelos de IA, análise de dados e muito mais."
i18n_source_hash: 8e9e9ca2840c
i18n_source_hash: 25970c776f7c
i18n_provenance: human
i18n_output_hash: 9c568fbd1d33
i18n_output_hash: bc726078e4af
i18n_hash_version: 2
---
# Configuração {#configuration}
@@ -19,28 +20,51 @@ Toda a configuração é feita por meio de variáveis de ambiente. Cada variáve
| `RATE_LIMIT_PER_MIN` | `1000` | Máximo de requisições por minuto por IP. Defina como 0 para desativar a limitação de taxa. |
| `CORS_ORIGIN` | (vazio) | Origens permitidas para CORS, separadas por vírgula, ou vazio para apenas a mesma origem. |
| `LOG_LEVEL` | `info` | Verbosidade do log. Um de: `fatal`, `error`, `warn`, `info`, `debug`, `trace`. |
| `TRUST_PROXY` | `true` | Confia nos cabeçalhos `X-Forwarded-For` de um proxy reverso. Defina como `false` se não estiver atrás de um proxy. |
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Quais pares podem definir o IP do cliente por meio de `X-Forwarded-For`. O padrão acredita apenas em um par de rede privada, então um proxy reverso em uma rede Docker ou em uma LAN é confiável e o cabeçalho forjado de um cliente público não é. Defina `true` só quando um proxy sob seu controle estiver na frente, em um endereço público. |
### Autenticação {#authentication}
Os dois booleanos abaixo aceitam apenas `true` e `false`. Qualquer outra coisa, `1` ou `yes` ou `on`, falha na validação e o servidor encerra antes de começar a escutar.
| Variável | Padrão | Descrição |
|---|---|---|
| `AUTH_ENABLED` | `false` | Defina como `true` para exigir login. A imagem Docker usa `true` por padrão. |
| `AUTH_ENABLED` | `true` | Exige login. Defina como `false` para rodar sem conta nenhuma, o que dá direitos de admin a toda requisição, então mantenha isso em uma rede confiável. |
| `DEFAULT_USERNAME` | `admin` | Nome de usuário da conta de admin inicial. Usado apenas na primeira execução. |
| `DEFAULT_PASSWORD` | `admin` | Senha da conta de admin inicial. Altere-a após o primeiro login. |
| `MAX_USERS` | `0` (ilimitado) | Número máximo de contas de usuário registradas. Defina como 0 para ilimitado. |
| `SESSION_DURATION_HOURS` | `168` | Duração da sessão de login em horas (o padrão é 7 dias). |
| `SKIP_MUST_CHANGE_PASSWORD` | - | Defina como qualquer valor não vazio para ignorar o prompt de troca de senha forçada no primeiro login |
| `SKIP_MUST_CHANGE_PASSWORD` | `false` | Defina como `true` para pular o prompt de troca de senha forçada no primeiro login. |
### Armazenamento {#storage}
| Variável | Padrão | Descrição |
|---|---|---|
| `STORAGE_MODE` | `local` | `local` ou `s3`. S3/MinIO requer uma licença com o recurso s3_storage. |
| `DATABASE_URL` | `postgres://snapotter:snapotter@postgres:5432/snapotter` | String de conexão do PostgreSQL. |
| `REDIS_URL` | `redis://redis:6379` | String de conexão do Redis (usada para as filas de jobs do BullMQ). |
| `WORKSPACE_PATH` | `./tmp/workspace` | Diretório para arquivos temporários durante o processamento. Limpo automaticamente. |
| `FILES_STORAGE_PATH` | `./data/files` | Diretório para arquivos persistentes do usuário (imagens enviadas, resultados salvos). |
| `STORAGE_MODE` | `local` | `local` ou `s3`. S3 e MinIO precisam de uma licença com o recurso s3_storage, além das variáveis `S3_*` abaixo. |
| `DATABASE_URL` | `postgres://snapotter:snapotter@localhost:5432/snapotter` | String de conexão do PostgreSQL. A pilha Compose aponta isso para o serviço `postgres` dela; deixe indefinido (junto com `REDIS_URL`) para obter o modo embutido. |
| `REDIS_URL` | `redis://localhost:6379` | String de conexão do Redis (usada para as filas de jobs do BullMQ). O Compose aponta isso para o serviço `redis` dele. |
| `WORKSPACE_PATH` | `./tmp/workspace` | Diretório para arquivos temporários durante o processamento. Limpo automaticamente. A imagem define `/tmp/workspace`. |
| `FILES_STORAGE_PATH` | `./data/files` | Diretório para arquivos persistentes do usuário (imagens enviadas, resultados salvos). A imagem define `/data/files`. |
### Armazenamento de objetos S3 {#s3-object-storage}
Lido apenas quando `STORAGE_MODE=s3`. Se faltar qualquer uma das três obrigatórias, a inicialização falha informando o nome da variável que você deixou de fora.
| Variável | Padrão | Descrição |
|---|---|---|
| `S3_BUCKET` | (vazio) | Bucket que guarda os uploads e as saídas. Obrigatória. |
| `S3_ACCESS_KEY_ID` | (vazio) | Chave de acesso. Obrigatória. No contêiner, você pode montá-la em vez disso, via `S3_ACCESS_KEY_ID_FILE`. |
| `S3_SECRET_ACCESS_KEY` | (vazio) | Chave secreta. Obrigatória. Mesma convenção de arquivo: `S3_SECRET_ACCESS_KEY_FILE`. |
| `S3_REGION` | `us-east-1` | Região do bucket. |
| `S3_ENDPOINT` | (vazio) | Endpoint personalizado para MinIO, R2, Backblaze e outros armazenamentos compatíveis com S3. Vazio significa AWS. |
| `S3_FORCE_PATH_STYLE` | `false` | Defina como `true` para o MinIO e qualquer outro que espere `endpoint/bucket/key` em vez de endereçamento por virtual host. |
| `S3_PREFIX` | (vazio) | Prefixo de chave, para que um bucket possa guardar várias instâncias. |
### Criptografia em repouso {#encryption-at-rest}
| Variável | Padrão | Descrição |
|---|---|---|
| `DATA_ENCRYPTION_KEY` | (vazio) | 64 caracteres hexadecimais (32 bytes). Criptografa as configurações sensíveis armazenadas no banco de dados. Qualquer coisa que não tenha 64 caracteres hexadecimais é rejeitada na inicialização. |
| `DATA_ENCRYPTION_KEY_PREVIOUS` | (vazio) | A chave da qual você está saindo na rotação, no mesmo formato. Defina as duas durante uma rotação para que as linhas existentes ainda sejam descriptografadas e depois remova esta. |
### Modo embutido {#embedded-mode}
@@ -59,16 +83,15 @@ Observação sobre telemetria: o modo embutido herda o padrão de análise de da
| Variável | Padrão | Descrição |
|---|---|---|
| `MAX_UPLOAD_SIZE_MB` | `100` | Tamanho máximo de arquivo por upload em megabytes. Defina como 0 para ilimitado. |
| `MAX_BATCH_SIZE` | `100` | Número máximo de arquivos em uma única requisição em lote. Defina como 0 para ilimitado. |
| `MAX_UPLOAD_SIZE_MB` | `0` (ilimitado) | Tamanho máximo de arquivo por upload em megabytes. Defina como 0 para ilimitado. A imagem publicada vem com `0`; uma compilação a partir do código-fonte começa em 100. |
| `MAX_BATCH_SIZE` | `0` (ilimitado) | Número máximo de arquivos em uma única requisição em lote. Defina como 0 para ilimitado. A imagem publicada vem com `0`; uma compilação a partir do código-fonte começa em 100. |
| `CONCURRENT_JOBS` | `0` (auto) | Número de jobs em lote que rodam em paralelo. Defina como 0 para detecção automática com base nos núcleos de CPU disponíveis. |
| `MAX_MEGAPIXELS` | `0` (ilimitado) | Resolução máxima de imagem permitida em megapixels. Defina como 0 para ilimitado. |
| `MAX_WORKER_THREADS` | `0` (auto) | Máximo de threads de trabalho para o processamento de imagem. Defina como 0 para detecção automática com base nos núcleos de CPU disponíveis. |
| `PROCESSING_TIMEOUT_S` | `0` (sem limite) | Tempo máximo de processamento por requisição em segundos. Defina como 0 para sem tempo limite. |
| `MAX_PIPELINE_STEPS` | `20` | Número máximo de etapas em um pipeline. Defina como 0 para sem limite. |
| `MAX_CANVAS_PIXELS` | `0` (sem limite) | Tamanho máximo de canvas em pixels para as imagens de saída. Defina como 0 para sem limite. |
| `MAX_SVG_SIZE_MB` | `0` (ilimitado) | Tamanho máximo de arquivo SVG em megabytes. Defina como 0 para ilimitado. |
| `MAX_SPLIT_GRID` | `100` | Dimensão máxima da grade para a ferramenta de divisão de imagem. |
| `MAX_SVG_SIZE_MB` | `50` | Maior SVG aceito antes da sanitização, em megabytes. `0` se comporta de forma diferente aqui em relação às linhas ao redor. Ele remove por completo o limite de tamanho aplicado antes da análise, em vez de aumentá-lo, então deixe esta definida. |
| `MAX_PDF_PAGES` | `0` (ilimitado) | Número máximo de páginas de PDF para a conversão de PDF para imagem. Defina como 0 para ilimitado. |
### Limpeza {#cleanup}
@@ -82,7 +105,7 @@ Observação sobre telemetria: o modo embutido herda o padrão de análise de da
| Variável | Padrão | Descrição |
|---|---|---|
| `DEFAULT_THEME` | `light` | Tema padrão para novas sessões. `light` ou `dark`. |
| `DEFAULT_THEME` | `light` | Tema padrão para novas sessões. `light`, `dark` ou `system`. |
| `DEFAULT_LOCALE` | `en` | Idioma padrão da interface. |
| `DEFAULT_TOOL_VIEW` | `sidebar` | Layout padrão das ferramentas. `sidebar` ou `fullscreen`. |
@@ -124,13 +147,13 @@ services:
image: postgres:17-alpine
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_PASSWORD: snapotter # Altere isso para implantações não locais
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: "Como contribuir com o SnapOtter. Relatórios de bugs, solicitações de recursos, pull requests e requisitos do CLA."
i18n_source_hash: 528802503035
i18n_source_hash: 6c920a5f83e0
i18n_provenance: human
i18n_output_hash: a6f10e198b99
i18n_output_hash: 4ef406ec11e7
i18n_hash_version: 2
---
# Contribuindo {#contributing}
@@ -53,7 +54,7 @@ Se você está contribuindo em nome do seu empregador e ele detém os direitos d
### Pré-requisitos {#prerequisites}
- Node.js 22+
- Node.js 22.22+
- pnpm 9+
- Python 3.11+ (apenas para ferramentas de IA)
- Docker (opcional, para testes de integração completos)
@@ -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: "Esquema do banco de dados PostgreSQL, tabelas, migrações e procedimentos de backup do SnapOtter."
i18n_source_hash: 50d5d4f220cf
i18n_provenance: human
i18n_output_hash: dd1ed517c252
i18n_source_hash: a68264552836
i18n_provenance: machine
i18n_output_hash: bd8d8459b7e0
i18n_hash_version: 2
---
# Banco de dados {#database}
@@ -145,6 +146,17 @@ Registro de ações relevantes para segurança.
| `details` | jsonb | Dados específicos da ação |
| `createdAt` | timestamp | Horário da ação |
### user_preferences {#user-preferences}
Estado da interface por usuário, indexado pelo nome da preferência. Guarda as ferramentas fixadas da página inicial, gravadas por meio de `PUT /api/v1/preferences`.
| Coluna | Tipo | Observações |
|---|---|---|
| `userId` | text | FK para users, com exclusão em cascata. Chave primária junto com `key` |
| `key` | text | Nome da preferência. Chave primária junto com `userId` |
| `value` | jsonb | Conteúdo da preferência |
| `updatedAt` | timestamp | Última gravação |
## Migrações {#migrations}
O Drizzle cuida das migrações de esquema. Os arquivos de migração ficam em `apps/api/drizzle/`. Durante o desenvolvimento:
@@ -159,27 +171,35 @@ Em produção, as migrações pendentes são aplicadas automaticamente na inicia
## Backup e restauração {#backup-and-restore}
O banco de dados relacional fica no volume `SnapOtter-pgdata` do contêiner do Postgres, não no volume `/data` do aplicativo.
O banco de dados relacional reside no volume `SnapOtter-pgdata` do contêiner Postgres, não no volume `/data` do aplicativo.
**Opção 1: pg_dump (recomendada)**
**Backup lógico com validação (recomendado)**
```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
```
**Opção 2: Snapshot de volume**
Este dump do banco de dados não contém objetos de biblioteca salvos em `/data/files` ou estado BullMQ durável no Redis. Faça backup e restaure-os com o procedimento coordenado em [Segurança e Proteção](/pt-BR/guide/security#backup-and-recovery).
**Instantâneo de volume frio**
```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
```
Não copie um diretório de dados PostgreSQL ativo com `tar`. Componha nomes de volume de prefixos por projeto, portanto resolva os IDs de volume montados de `docker inspect` ou de sua plataforma de armazenamento em vez de assumir o rótulo literal `SnapOtter-pgdata`.
### Migrando da versão 1.x (SQLite) {#migrating-from-1-x-sqlite}
Atualizar do SnapOtter 1.x tem seu próprio guia: veja [Atualizando da 1.x para a 2.0](./upgrading). Em resumo, reutilize seu volume `/data` existente e a 2.0 detecta e importa automaticamente o `/data/snapotter.db` na primeira inicialização (ou defina `SQLITE_MIGRATE_PATH` para apontar para ele explicitamente). Faça backup de todo o volume `/data` primeiro, não apenas de `snapotter.db`: a 1.x usa o modo WAL do SQLite, então um contêiner parado frequentemente deixa a maior parte de seus dados em `snapotter.db-wal` ao lado de um `snapotter.db` quase vazio.
+24 -13
View File
@@ -1,8 +1,9 @@
---
description: "Implante o SnapOtter em produção com Docker. Requisitos de hardware, configuração de GPU e configs de proxy reverso para Nginx, Traefik e Cloudflare."
i18n_output_hash: b3176447b423
i18n_source_hash: 98172965118b
i18n_source_hash: 2a722f86da75
i18n_provenance: human
i18n_output_hash: 7829ae611800
i18n_hash_version: 2
---
# Implantação {#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 # Altere isso para implantações não locais
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
```
Verifique a detecção do CUDA nos logs:
### Verifique a aceleração da GPU {#verify-gpu-acceleration}
Verifique a detecção de CUDA nos logs:
```bash
docker logs SnapOtter 2>&1 | head -20
# Look for: [gpu] CUDA available via torch
```
Se as ferramentas de IA forem executadas na CPU mesmo que o `--gpus all` e o NVIDIA Container Toolkit estejam configurados corretamente, reinstale o pacote afetado (por exemplo, Remoção de segundo plano) em **Configurações → Recursos de IA**. O instalador restaura a compilação de GPU do ONNX Runtime, que uma compilação somente de CPU extraída por outro pacote (como transcrição) pode, de outra forma, ocultar no ambiente de IA compartilhado. Se a reinstalação a partir da UI não restaurar a GPU em uma imagem mais antiga, consulte o reparo manual no [problema nº 490](https://github.com/snapotter-hq/SnapOtter/issues/490).
## Requisitos de Hardware {#hardware-requirements}
Estes números vêm de benchmarks em uma variedade de sistemas, de uma workstation amd64 moderna com uma NVIDIA RTX 4070 até um Raspberry Pi, rodando todo o catálogo de ferramentas em cada um e variando os limites de recursos do Docker para encontrar o piso real.
@@ -436,11 +441,11 @@ O erro de inicialização nomeia o UID exato a usar, então o caminho mais rápi
| `AUTH_ENABLED` | `true` | Habilita/desabilita a exigência de login |
| `DEFAULT_USERNAME` | `admin` | Nome de usuário administrador inicial |
| `DEFAULT_PASSWORD` | `admin` | Senha de administrador inicial (troca forçada no primeiro login) |
| `MAX_UPLOAD_SIZE_MB` | `100` | Limite de upload por arquivo |
| `MAX_BATCH_SIZE` | `100` | Máximo de arquivos por requisição em lote |
| `MAX_UPLOAD_SIZE_MB` | `0` (ilimitado) | Limite de upload por arquivo em MB. A imagem vem com `0`; uma build a partir do código-fonte começa em 100 |
| `MAX_BATCH_SIZE` | `0` (ilimitado) | Máximo de arquivos por requisição em lote. A imagem vem com `0`; uma build a partir do código-fonte começa em 100 |
| `RATE_LIMIT_PER_MIN` | `1000` | Requisições à API por minuto por IP (defina 0 para desabilitar) |
| `MAX_USERS` | `0` (ilimitado) | Número máximo de contas de usuário |
| `TRUST_PROXY` | `true` | Confiar nos cabeçalhos X-Forwarded-For do proxy reverso |
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Quais pares podem definir o IP do cliente por meio de `X-Forwarded-For`. Apenas redes privadas por padrão |
| `PUID` | `999` | Rodar com este UID (para permissões de bind mount) |
| `PGID` | `999` | Rodar com este GID (para permissões de bind mount) |
| `LOG_LEVEL` | `info` | Verbosidade do log: fatal, error, warn, info, debug, trace |
@@ -483,7 +488,13 @@ curl http://localhost:1349/api/v1/health
## Proxy Reverso {#reverse-proxy}
O SnapOtter define `TRUST_PROXY=true` por padrão para que a limitação de taxa e o logging usem o IP real do cliente a partir dos cabeçalhos `X-Forwarded-For`.
`TRUST_PROXY` vem como `loopback,linklocal,uniquelocal` por padrão, então o SnapOtter só acredita no `X-Forwarded-For` vindo de um par em uma rede privada. Um proxy reverso no mesmo host, em uma rede Docker ou na sua LAN já é confiável de saída, o que faz a limitação de taxa, o limitador de força bruta do login, o log de auditoria e a lista de IPs permitidos da edição enterprise enxergarem o IP real do cliente sem nenhuma configuração.
Defina `TRUST_PROXY=true` só quando o proxy à frente alcançar o SnapOtter a partir de um endereço **público**, um balanceador de carga na nuvem em outra rede, por exemplo. Em uma instância exposta diretamente, esse valor deixa `request.ip` sob controle do atacante, porque quem fica trocando o cabeçalho ganha um contador de limite de taxa novo a cada requisição.
Duas coisas para saber antes de sair medindo IPs de cliente. O Docker Desktop no macOS e no Windows serve uma porta publicada por meio de um proxy em espaço de usuário que reescreve todo endereço de origem para o gateway da VM `192.168.65.1`, então ali nenhum valor de `TRUST_PROXY` recupera o cliente real; implante no Linux qualquer coisa voltada para a internet. E em qualquer plataforma, chegar a uma porta publicada por `localhost` é observado como o gateway da bridge e não como o seu cliente, de modo que um teste em localhost não diz nada sobre como um cliente real é atribuído. A tabela completa dos valores de `TRUST_PROXY` e a ressalva sobre o Docker Desktop estão em [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md#client-ip-resolution-trust_proxy).
Duas coisas são importantes para cada proxy abaixo: permitir grandes corpos de solicitação (uploads) e não armazenar respostas em buffer. Um proxy de buffer de resposta interrompe o progresso do SSE e, mais visivelmente, faz um download de arquivo grande "iniciar, mas nunca terminar", porque o proxy mantém o arquivo inteiro antes de transmiti-lo. SnapOtter envia `X-Accel-Buffering: no` em downloads, então nginx os transmite mesmo se o buffer for deixado em outro lugar, mas proxies diferentes de nginx precisam de buffer de resposta desativado explicitamente (mostrado em cada configuração abaixo). Se um download parar no meio, um proxy de buffer na frente é a primeira coisa a verificar.
### 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)
# Transmita respostas em vez de buffer: necessário para o progresso do SSE (lote, IA, instalações de recursos) e para downloads de arquivos grandes.
proxy_buffering off;
proxy_read_timeout 300s;
}
@@ -549,7 +560,7 @@ images.example.com {
}
```
`flush_interval -1` desabilita o buffering de resposta, que é necessário para eventos de progresso SSE (processamento em lote, ferramentas de IA, instalações de features). Os timeouts estendidos permitem que uploads de arquivos grandes sejam concluídos sem que o Caddy encerre a conexão cedo demais.
`flush_interval -1` desativa o buffer de resposta, que é necessário para eventos de progresso SSE (processamento em lote, ferramentas de IA, instalações de recursos) e para downloads de arquivos grandes para serem transmitidos em vez de paralisados. Os tempos limite estendidos permitem que uploads de arquivos grandes sejam concluídos sem que Caddy feche a conexão antecipadamente.
### Cloudflare Tunnels {#cloudflare-tunnels}
+19 -7
View File
@@ -1,8 +1,9 @@
---
description: "Configuração de ambiente de desenvolvimento local, comandos, convenções de código e como adicionar uma nova ferramenta ao SnapOtter."
i18n_source_hash: cb03724d2829
i18n_provenance: human
i18n_output_hash: 3003ce78a10f
i18n_source_hash: 56acc1bf9a9b
i18n_provenance: machine
i18n_output_hash: 3c036b74e396
i18n_hash_version: 2
---
# Guia do desenvolvedor {#developer-guide}
@@ -11,12 +12,12 @@ Como configurar um ambiente de desenvolvimento local e contribuir com código pa
## Pré-requisitos {#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/) (necessário para Postgres + Redis locais, builds de contêiner e recursos de IA)
- Git
Python 3.10+ só é necessário se você estiver trabalhando no sidecar de IA/ML (remoção de fundo, upscaling, OCR).
Python 3.11+ só é necessário se você estiver trabalhando no sidecar de IA/ML (remoção de fundo, upscaling, OCR).
## Configuração {#setup}
@@ -32,10 +33,10 @@ Isso inicia dois servidores de desenvolvimento:
| Serviço | URL | Observações |
|----------|--------------------------|------------------------------------|
| Frontend | http://localhost:1349 | Servidor de dev Vite, faz proxy de /api |
| Frontend | http://localhost:1351 | Servidor de dev Vite, faz proxy de /api |
| Backend | http://localhost:13490 | API Fastify (acessada via proxy) |
Abra http://localhost:1349 no seu navegador. Faça login com `admin` / `admin`. Você será solicitado a alterar a senha no primeiro login.
Abra http://localhost:1351 no seu navegador. Faça login com `admin` / `admin`. Você será solicitado a alterar a senha no primeiro login.
## Estrutura do projeto {#project-structure}
@@ -220,6 +221,17 @@ Use cache mounts do BuildKit para rebuilds mais rápidos:
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile -t snapotter:latest .
```
## Domínios de versões de lançamento {#release-version-domains}
SnapOtter possui intencionalmente três domínios de versão. Não copie um domínio para outro durante um lançamento:
- A versão de lançamento do aplicativo abrange o manifesto raiz, todos os pacotes de espaço de trabalho privado e `APP_VERSION`. Semantic-release fornece esse valor e `pnpm version:sync <version>` atualiza cada espaço de trabalho antes do lançamento do aplicativo.
- OpenAPI `info.version` é o contrato público estável API-major. Todas as especificações localizadas permanecem em `<major>.0.0` para versões de aplicativos compatíveis e mudam somente quando o contrato API passa para uma nova versão principal.
- `docker/feature-manifest.json` mantém `imageVersion: 2.0.0` como a época de armazenamento de pacote de recursos legado imutável. Esses caminhos de arquivo v2 não são versões de pacotes de aplicativos. O OCR preciso usa o formato de tempo de execução v3 e registra a origem da versão do aplicativo separadamente.
`tests/unit/infra/release-version-policy.test.ts` impõe esses limites. Um novo domínio de versão ou migração deve atualizar esse contrato e o design de migração de artefato relevante juntos.
Os valores independentes API e do pacote legado residem em `config/release-version-policy.json`; a sincronização de versão do aplicativo nunca deve reescrever esse arquivo de política implicitamente.
## Variáveis de ambiente {#environment-variables}
Veja o [Guia de configuração](/pt-BR/guide/configuration) para a lista completa. As principais para desenvolvimento:
+8 -7
View File
@@ -1,8 +1,9 @@
---
description: "Tags de imagem Docker do SnapOtter, benchmarks de GPU, fixação de versão e suporte multiplataforma para AMD64 e ARM64."
i18n_output_hash: 5f53a19b9daf
i18n_source_hash: fda322e78b4b
i18n_source_hash: 566e20ca07fc
i18n_provenance: human
i18n_output_hash: adee6f234943
i18n_hash_version: 2
---
# Imagem Docker {#docker-image}
@@ -93,13 +94,13 @@ services:
image: postgres:17-alpine
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_PASSWORD: snapotter # Altere isso para implantações não locais
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 @@ Para aceleração NVIDIA CUDA via Docker Compose, adicione a seção deploy ao s
| Tag | Descrição |
|-----|------------|
| `latest` | Última versão |
| `1.11.0` | Versão exata |
| `1.11` | Último patch na 1.11.x |
| `1` | Última versão menor na 1.x |
| `2.1.0` | Versão exata |
| `2.1` | Último patch na 2.1.x |
| `2` | Última versão menor na 2.x |
## Plataformas {#platforms}
+27 -60
View File
@@ -1,8 +1,9 @@
---
description: "Instale o SnapOtter com Docker em um único comando. Inclui configuração de Docker Compose, build a partir do código-fonte e uma visão geral completa das features."
i18n_output_hash: 8584b5780f52
i18n_source_hash: 68bf7f60b68d
i18n_provenance: human
i18n_source_hash: 8040133a6982
i18n_provenance: machine
i18n_output_hash: c4412ce7358f
i18n_hash_version: 2
---
# Primeiros Passos {#getting-started}
@@ -17,7 +18,7 @@ Explore a interface completa em [demo.snapotter.com](https://demo.snapotter.com)
docker run -d --name SnapOtter -p 1349:1349 -v SnapOtter-data:/data snapotter/snapotter:latest
```
Este contêiner único roda tudo de que precisa: sem um `DATABASE_URL` definido, ele inicia seu próprio PostgreSQL e Redis na interface de loopback (modo embutido) e mantém todos os dados no volume `SnapOtter-data`. É a maneira mais rápida de experimentar o SnapOtter ou fazer self-host em um homelab. Para produção, rode a stack do [Docker Compose](#docker-compose) abaixo, que mantém o PostgreSQL e o Redis em seus próprios contêineres. O modo embutido roda como root (o padrão) e se desliga automaticamente assim que você define `DATABASE_URL`.
Este contêiner único executa tudo o que precisa: sem nenhum conjunto `DATABASE_URL`, ele inicia seu próprio PostgreSQL e Redis na interface de loopback (modo incorporado) e mantém todos os dados no volume `SnapOtter-data`. É a maneira mais rápida de experimentar o SnapOtter ou auto-hospedar em um homelab. Para produção, use a [pilha canônica do Docker Compose](#docker-compose), que mantém PostgreSQL e Redis em seus próprios contêineres. O modo incorporado é executado como root (o padrão) e é desativado automaticamente assim que você define `DATABASE_URL`.
Vai instalar em um Raspberry Pi, um notebook antigo ou um VPS pequeno? Veja [Ambientes com Poucos Recursos](/pt-BR/guide/low-resource) para um passo a passo ajustado e o que esperar de hardware limitado.
@@ -40,7 +41,7 @@ Adicione `--gpus all` para remoção de fundo, aumento de escala, aprimoramento
docker run -d --name SnapOtter -p 1349:1349 --gpus all -v SnapOtter-data:/data snapotter/snapotter:latest
```
Requer o [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html). Faz fallback para CPU automaticamente quando o CUDA está indisponível. A aceleração por iGPU Intel/AMD através de VA-API, Quick Sync ou OpenCL não é suportada para inferência de IA no momento. Veja [Tags Docker](/pt-BR/guide/docker-tags) para benchmarks.
Requer o [kit de ferramentas NVIDIA Container](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html). Volta para a CPU automaticamente quando CUDA não está disponível. A aceleração Intel/AMD iGPU por meio de VA-API, Quick Sync ou OpenCL não é suportada atualmente para inferência de IA. Consulte [Tags Docker](/pt-BR/guide/docker-tags) para benchmarks. Se as ferramentas de IA forem executadas na CPU apesar de `--gpus all`, consulte [Verificar aceleração de GPU](/pt-BR/guide/deployment#verify-gpu-acceleration).
:::
::: details Também no GHCR
@@ -53,65 +54,31 @@ Ambos os registries publicam a mesma imagem a cada release.
## 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
Use o arquivo de produção mantido e testado com cada versão em vez de copiar um exemplo abreviado do Compose desta página:
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
```
Veja [Configuração](/pt-BR/guide/configuration) para todas as variáveis de ambiente.
O [`docker/docker-compose.yml`](https://github.com/snapotter-hq/SnapOtter/blob/v2.1.0/docker/docker-compose.yml) canônico inclui todos os quatro volumes de tempo de execução, verificações de integridade, limites de recursos, configuração durável do Redis, imagens de banco de dados/cache fixadas e a proteção atual do contêiner. Altere a senha de administrador padrão imediatamente após o primeiro login. Para uma implantação reproduzível, fixe a imagem do aplicativo SnapOtter na tag de lançamento ou resumo que você verificou em vez de seguir `latest`.
Consulte [Configuração](/pt-BR/guide/configuration) para todas as variáveis de ambiente e [Segurança e proteção](/pt-BR/guide/security) para segredos, política de rede e orientação de backup.
## Build a partir do Código-Fonte {#build-from-source}
**Pré-requisitos:** Node.js 22+, pnpm 9+, Docker (para Postgres + Redis), Python 3.10+ (para features de IA), Git.
**Pré-requisitos:** Node.js 22.22+, pnpm 9+, Docker (para Postgres + Redis), Python 3.11+ (para features de IA), Git.
```bash
git clone https://github.com/snapotter-hq/SnapOtter.git
@@ -121,7 +88,7 @@ pnpm install
pnpm dev
```
- Frontend: [http://localhost:1349](http://localhost:1349)
- Frontend: [http://localhost:1351](http://localhost:1351)
- Backend: [http://localhost:13490](http://localhost:13490)
## O Que Você Pode Fazer {#what-you-can-do}
@@ -130,11 +97,11 @@ pnpm dev
| Modalidade | Contagem | Ferramentas de Exemplo |
|----------|-------|---------------|
| **Imagem** | 105 | Redimensionar, Recortar, Comprimir, Converter, Remover Fundo, Upscale, OCR, Marca d'água, Colagem, Colorizar, Ferramentas de GIF, presets de formato |
| **Imagem** | 107 | Redimensionar, Recortar, Comprimir, Converter, Remover Fundo, Upscale, OCR, Marca d'água, Colagem, Colorizar, Ferramentas de GIF, presets de formato |
| **Vídeo** | 57 | Cortar, Recortar, Comprimir, Converter, Mesclar, Extrair Áudio, Legendas Automáticas, Vídeo para GIF, Redimensionar, Estabilizar, presets de formato |
| **Áudio** | 27 | Cortar, Mesclar, Converter, Normalizar, Redução de Ruído, Transcrever, Alteração de Pitch, Fade, Criador de Toques, presets de formato |
| **PDF / Documento** | 42 | Mesclar, Dividir, Comprimir, OCR, Marca d'água, Ocultar, Word para PDF, Excel para PDF, Girar, Proteger, Reparar |
| **Arquivos** | 10 | CSV para JSON, JSON para XML, Mesclar CSVs, Dividir CSV, Criar ZIP, Extrair ZIP, Criador de Gráficos, YAML/JSON |
| **PDF / Documento** | 29 | Mesclar, Dividir, Comprimir, OCR, Marca d'água, Ocultar, Word para PDF, Excel para PDF, Girar, Proteger, Reparar |
| **Arquivos** | 23 | CSV para JSON, JSON para XML, Mesclar CSVs, Dividir CSV, Criar ZIP, Extrair ZIP, Criador de Gráficos, YAML/JSON |
### Pipelines {#pipelines}
+4 -3
View File
@@ -1,7 +1,8 @@
---
i18n_source_hash: f5de74aee1b9
i18n_source_hash: 521c03a6416c
i18n_provenance: machine
i18n_output_hash: 507cd5624773
i18n_output_hash: ee71505c5fbe
i18n_hash_version: 2
---
# Ambientes com Poucos Recursos {#low-resource-setups}
@@ -59,7 +60,7 @@ services:
image: postgres:17-alpine
environment:
- POSTGRES_USER=snapotter
- POSTGRES_PASSWORD=snapotter
- POSTGRES_PASSWORD=snapotter # Altere isso para implantações não locais
- POSTGRES_DB=snapotter
volumes:
- ./postgres-data:/var/lib/postgresql/data
+12 -7
View File
@@ -1,8 +1,9 @@
---
description: "Configure o provisionamento SCIM 2.0 para sincronizar usuários e grupos do seu provedor de identidade com o SnapOtter. Abrange Okta, Azure AD / Entra ID e integrações personalizadas."
i18n_source_hash: bbd50119ec12
i18n_source_hash: 06ee702b386e
i18n_provenance: human
i18n_output_hash: 6140de972193
i18n_output_hash: e7fd4fb6bdad
i18n_hash_version: 2
---
# Provisionamento SCIM {#scim-provisioning}
@@ -17,7 +18,7 @@ O provisionamento SCIM requer uma licença **enterprise** com o recurso `scim`.
- Uma instância do SnapOtter em execução, acessível em uma URL pública
- Uma chave de licença enterprise com o recurso `scim`
- Acesso de administrador ao SnapOtter (a permissão `users:manage` é necessária para gerar ou revogar um token SCIM)
- Uma conta SnapOtter `admin` integrada com seu conjunto completo de permissões efetivas. Uma função personalizada delegada ou uma chave de API de administrador sem qualquer permissão de administrador não pode gerar ou revogar o token SCIM global.
- Acesso de administrador às configurações de provisionamento do seu provedor de identidade
## Início rápido {#quick-start}
@@ -34,7 +35,7 @@ A resposta contém o token. Salve-o imediatamente; ele não pode ser recuperado
```json
{
"token": "a1b2c3d4e5f6...",
"token": "so_scim_v2_a1b2c3d4e5f6...",
"message": "Save this token - it cannot be retrieved again"
}
```
@@ -49,15 +50,19 @@ Os endpoints SCIM usam um token Bearer dedicado, separado das sessões de usuár
### Gerando um token {#generating-a-token}
`POST /api/v1/enterprise/scim/token` gera um novo token SCIM. Este endpoint requer uma sessão válida com a permissão `users:manage`.
`POST /api/v1/enterprise/scim/token` gera um novo token SCIM. Como o token pode provisionar e alterar usuários em toda a instância, esse endpoint requer a função `admin` integrada com o conjunto completo de permissões de administrador efetivas. Manter `users:manage` em uma função personalizada não é suficiente.
O token é retornado em texto simples exatamente uma vez. O SnapOtter armazena apenas um hash scrypt. Se você perder o token, revogue-o e gere um novo.
Apenas um token SCIM fica ativo por vez. Gerar um novo token substitui o anterior.
::: warning Reemissão de token após atualização
Os tokens SCIM legados e não versionados são rejeitados. Após atualizar para uma versão que emita tokens `so_scim_v2_...`, gere um novo token e atualize seu provedor de identidade antes de retomar o provisionamento.
:::
### Revogando um token {#revoking-a-token}
`DELETE /api/v1/enterprise/scim/token` revoga o token SCIM atual. Este endpoint também requer `users:manage`.
`DELETE /api/v1/enterprise/scim/token` revoga o token SCIM atual. Ele tem os mesmos requisitos de administração integrados completos da geração de token.
### Limite de taxa {#rate-limiting}
@@ -279,7 +284,7 @@ A requisição SCIM não incluiu um cabeçalho `Authorization: Bearer <token>`.
### 401 "Invalid token" {#_401-invalid-token}
O token não corresponde ao hash armazenado. Isso acontece se o token foi revogado e regerado. Atualize o token nas configurações de provisionamento do seu IdP.
O token está malformado, usa o formato não versionado retirado ou não corresponde ao hash armazenado. Gere um token `so_scim_v2_...` atual e atualize o token nas configurações de provisionamento do seu IdP.
### 401 "SCIM not configured" {#_401-scim-not-configured}
+92 -164
View File
@@ -1,8 +1,9 @@
---
description: "Guia de hardening de segurança para o SnapOtter. Segurança de contêineres, isolamento de rede, Docker secrets, implantação em Kubernetes e artefatos de conformidade."
i18n_source_hash: 986f7658430c
i18n_provenance: human
i18n_output_hash: fe0dc1598bab
i18n_source_hash: 9ff337fa0417
i18n_provenance: machine
i18n_output_hash: 1639c7483bb1
i18n_hash_version: 2
---
# Segurança e Hardening {#security-hardening}
@@ -11,133 +12,42 @@ O SnapOtter processa arquivos inteiramente na sua infraestrutura. Ele envia anal
O contêiner roda como um usuário dedicado não-root (`snapotter`) com todas as capabilities do Linux removidas, exceto o conjunto mínimo necessário. Para a política completa de divulgação de vulnerabilidades e a arquitetura de segurança, veja [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) no GitHub.
## Hardening de Contêiner {#container-hardening}
## Endurecimento de contêineres {#container-hardening}
O [docker-compose.yml padrão](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) inclui hardening de segurança para produção. Aqui está um detalhamento de cada opção e por que ela importa:
Os arquivos canônicos [CPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) e [GPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose-gpu.yml) Compose são a fonte da verdade. Não copie um exemplo abreviado para produção; implante o arquivo da tag de lançamento que você verificou.
```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
Ambas as pilhas aplicam os seguintes controles:
# --- 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
- Os limites de memória, swap, CPU e PID contêm processamento nativo descontrolado.
- Cada serviço elimina todos os recursos do Linux. O aplicativo adiciona de volta apenas `CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILL` para propriedade de volume, queda de identidade `gosu` unidirecional e encaminhamento de sinal elegante. PostgreSQL e Redis recebem apenas o subconjunto necessário para seus pontos de entrada oficiais.
- `security_opt: [no-new-privileges:true]` evita que processos no aplicativo, PostgreSQL e contêineres Redis obtenham privilégios adicionais. Isso permanece compatível com `gosu`: o ponto de entrada começa como root, prepara os volumes e passa apenas para o usuário `snapotter` dedicado.
- As entradas de imagem PostgreSQL e Redis são fixadas pelo resumo. O aplicativo também deve ser fixado em uma tag de lançamento verificada ou resumo, em vez de `latest`.
- Verificações de integridade, rotação de log JSON limitada, Redis AOF durável e política de reinicialização são definidas centralmente nos arquivos canônicos.
# --- 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
Para uma implantação voltada para a Internet, vincule a porta 1349 ao loopback e encerre o TLS em um proxy reverso mantido. Gere credenciais exclusivas do PostgreSQL e Redis, armazene segredos em arquivos protegidos ou em um gerenciador de segredos e altere a senha inicial do administrador imediatamente.
# --- Logging ---
logging:
driver: json-file
options:
max-size: "50m" # Rotate logs at 50 MB
max-file: "5" # Keep 5 rotated log files
### Por que `read_only` não está definido como {#why-read-only-is-not-set}
# --- 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
`read_only: true` não está definido porque o remapeamento PUID/PGID grava em `/etc/passwd` e `/etc/group` na inicialização. Se você usar o sinalizador `--user` do Docker ou Kubernetes `runAsUser` em vez de PUID/PGID, poderá ativar com segurança um sistema de arquivos raiz somente leitura.
shm_size: "2gb" # Required for Python ML shared memory
restart: unless-stopped
## Isolamento de rede {#network-isolation}
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
O processamento de arquivos é local, mas uma instalação padrão **não é um sistema livre de saída**. A análise anônima de produtos usa PostHog e os relatórios de falhas usam Sentry quando a telemetria está habilitada. Defina `SNAPOTTER_TELEMETRY=0` (ou desative a análise em Configurações > Sistema > Privacidade) para desligar ambos. SnapOtter nunca inclui arquivos carregados, nomes de arquivos, saída de OCR, texto de documento ou outro conteúdo de arquivo nesses eventos.
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:
```
### Por Que `no-new-privileges` Não Está Definido {#why-no-new-privileges-is-not-set}
`security_opt: [no-new-privileges:true]` é omitido intencionalmente. O entrypoint inicia como root para corrigir a propriedade dos volumes, depois muda para o usuário `snapotter` via [gosu](https://github.com/tianon/gosu), que requer setuid. Assim que a mudança de privilégio se completa, o processo roda como `snapotter` com todas as capabilities removidas, exceto as cinco listadas acima.
Se você usa Kubernetes ou a flag `--user` do Docker para rodar diretamente como não-root (contornando o gosu), `no-new-privileges` é seguro de habilitar.
### Por Que `read_only` Não Está Definido {#why-read-only-is-not-set}
`read_only: true` não está definido porque o remapeamento de PUID/PGID escreve em `/etc/passwd` e `/etc/group` na inicialização. Se você usa a flag `--user` do Docker ou o `runAsUser` do Kubernetes em vez de PUID/PGID, pode habilitar com segurança um sistema de arquivos raiz somente leitura.
## Isolamento de Rede {#network-isolation}
Durante a operação normal, o contêiner faz **zero conexões de rede de saída**. Todo o processamento de arquivos acontece localmente usando bibliotecas empacotadas.
```
Browser --> Reverse Proxy (TLS) --> SnapOtter container --> (nothing)
```
A única exceção são os **downloads de modelos de IA**: quando um usuário instala um bundle de feature de IA pela interface, o contêiner baixa o arquivo do bundle pré-construído do Hugging Face, além de alguns arquivos de modelo individuais do GitHub Releases, do Google Storage e do PyPI. Esses downloads acontecem uma vez por bundle e são armazenados no volume `/data`.
Outro tráfego de saída é orientado por recursos: instalação de pacote/modelo de IA baixa entradas de liberação assinadas; A importação de URL busca um URL público solicitado pelo usuário; e OIDC, SAML, OpenTelemetry, webhooks, armazenamento compatível com S3 ou integrações semelhantes configurados explicitamente entram em contato com os destinos escolhidos pelo administrador. Os downloads de modelos em tempo de execução são desativados por padrão. Defina `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1` somente para ativar explicitamente os downloads automáticos de fallback. Uma [importação de pacote off-line](/pt-BR/guide/deployment) pode provisionar recursos de IA sem saída do modelo de tempo de execução.
**Recomendações de firewall:**
| Cenário | Regra de saída |
|Cenário|Regra de saída|
|---|---|
| Air-gapped (sem IA) | Bloqueie todo o tráfego de saída do contêiner |
| Bundles de IA necessários | Permita HTTPS para `huggingface.co`, `*.xethub.hf.co`, `cdn-lfs.huggingface.co`, `github.com`, `objects.githubusercontent.com`, `storage.googleapis.com`, `pypi.org`, `files.pythonhosted.org` durante a instalação, depois bloqueie |
| Após a instalação da IA | Bloqueie todo o tráfego de saída - os modelos ficam em cache localmente |
|Sem ar|Defina `SNAPOTTER_TELEMETRY=0` e `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0`, use a importação de pacote de IA off-line, desative a importação de URL e integrações externas e, em seguida, bloqueie a saída|
|Telemetria padrão|Permita os endpoints PostHog e Sentry listados pelos registros do seu navegador/rede; desativar a telemetria se a política não permitir|
|Pacotes de IA necessários|Durante a instalação, permita HTTPS para `huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org`; então bloqueie esses hosts|
|Integrações externas|Permitir apenas os destinos OIDC/SAML/OTLP/webhook/armazenamento de objetos exatos configurados pelo administrador|
Os arquivos de bundle são servidos pelo armazenamento Xet do Hugging Face, que transfere pelos endpoints `*.xethub.hf.co` em paralelo e é o que torna rápidos os downloads de bundles de vários GB. Se seu firewall permite `huggingface.co` mas bloqueia `*.xethub.hf.co`, as instalações ainda têm êxito, mas recorrem a um download de fluxo único mais lento, então coloque os hosts Xet na allowlist para permanecer no caminho rápido. Instalações totalmente offline podem pular tudo isso e usar a [Importação de Bundle Offline](/pt-BR/guide/deployment) em vez disso.
Os arquivos de pacotes são servidos a partir do armazenamento Xet do Hugging Face, que é transferido pelos endpoints `*.xethub.hf.co` em paralelo e é o que torna rápidos os downloads de pacotes de vários GB. Se o seu firewall permitir `huggingface.co`, mas bloquear `*.xethub.hf.co`, as instalações ainda serão bem-sucedidas, mas voltarão para um download de fluxo único mais lento, portanto, coloque os hosts Xet na lista de permissões para permanecer no caminho rápido. Instalações totalmente off-line podem ignorar tudo isso e usar [Importação de pacote off-line](/pt-BR/guide/deployment).
Para a configuração de proxy reverso (Nginx, Traefik, Caddy, Cloudflare Tunnels), veja o [guia de Implantação](/pt-BR/guide/deployment#reverse-proxy).
Para configuração de proxy reverso (Nginx, Traefik, Caddy, Cloudflare Tunnels), consulte o [Guia de implantação](/pt-BR/guide/deployment#reverse-proxy).
## Docker Secrets {#docker-secrets}
@@ -255,85 +165,103 @@ Como `runAsUser: 999` é definido no nível do pod, o entrypoint pula o gosu por
Para o dimensionamento de recursos, veja [Requisitos de Hardware](/pt-BR/guide/deployment#hardware-requirements).
## Backup e Recuperação {#backup-and-recovery}
## Backup e recuperação {#backup-and-recovery}
O estado persistente é dividido entre dois volumes:
A pilha de produção do Compose define quatro volumes. Interrompa a entrada e deixe os trabalhos ativos terminarem antes de fazer um backup coordenado para que o PostgreSQL, o Redis e o estado do arquivo descrevam o mesmo momento.
| Volume | Conteúdo | Crítico? |
|Volume|Conteúdo|Tratamento de recuperação|
|---|---|---|
| `SnapOtter-pgdata` | Banco de dados PostgreSQL (usuários, configurações, pipelines, jobs, log de auditoria) | Sim |
| `/data` (volume do app) | Arquivos enviados pelo usuário, modelos de IA, venv Python | Parcialmente (veja abaixo) |
|`SnapOtter-pgdata`|Usuários, configurações, pipelines, trabalhos, metadados de arquivos e log de auditoria do PostgreSQL|Crítico; use um dump lógico rápido para recuperação portátil|
|`SnapOtter-data`|Objetos de biblioteca salvos, logs e estado de IA (`/data/files, /data/logs, /data/ai, /data/ai/venv`)|Faça backup de todo o volume; para economizar espaço, omitir deliberadamente todo o estado da IA e reinstalar seus pacotes|
|`SnapOtter-redisdata`|Redis AOF para estado de fila BullMQ durável|Faça backup após pausar o aplicativo e forçar `SAVE`; necessário para retomar o trabalho na fila exatamente|
|`SnapOtter-workspace`|Chaves temporárias de armazenamento de objetos (`/tmp/workspace/uploads, /tmp/workspace/outputs`)|Não faça backup depois que todos os trabalhos forem esgotados ou cancelados; nunca descarte-o enquanto os trabalhos estiverem ativos|
Dentro do volume `/data`:
| Caminho | Conteúdo | Crítico? |
|---|---|---|
| `/data/uploads/`, `/data/outputs/` | Arquivos do usuário e resultados de processamento | Sim |
| `/data/ai/` | Arquivos de modelo de IA baixados | Não (podem ser rebaixados) |
| `/data/venv/` | Ambiente virtual Python | Não (reconstruído na inicialização) |
O Compose normalmente prefixa os nomes dos volumes com o nome do projeto. Resolva o volume de origem real do contêiner montado em vez de assumir que um nome de exibição como `SnapOtter-data` é o nome do volume do Docker.
### Backup do banco de dados {#database-backup}
Use `pg_dump` para fazer backup do banco de dados enquanto a stack está em execução:
Use o formato de arquivo personalizado do PostgreSQL e verifique o arquivo antes de considerar o backup completo:
```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
```
Alternativamente, pare a stack e faça um snapshot do volume `SnapOtter-pgdata`:
Teste cada backup restaurando-o em uma pilha isolada, verificando os registros do banco de dados e as somas de verificação dos arquivos e iniciando o aplicativo. O `tests/qa/backup-restore-drill.sh` do repositório automatiza esse portão de liberação em relação a um `QA_IMAGE` explícito.
Se, em vez disso, sua plataforma tirar snapshots de volumes consistentes com falhas, interrompa a pilha inteira primeiro e faça snapshots de todos os volumes críticos como um conjunto. Uma cópia bruta do diretório de dados PostgreSQL de um contêiner em execução não é um backup lógico compatível.
### Backup de arquivos e filas {#file-and-queue-backup}
Pause o aplicativo antes de capturar volumes de arquivos e filas. Use `docker inspect` para resolver o nome real do volume, forçar o Redis a persistir em seu estado atual e arquivar com propriedade e permissões preservadas:
```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
```
### Backup dos arquivos do usuário {#user-files-backup}
Reinicie o Redis antes do aplicativo. Se você excluir `/data/ai` intencionalmente, remova toda a subárvore AI em vez de preservar um registro `installed.json` sem seus modelos ou ambiente virtual. Mantenha os arquivos de backup criptografados, com acesso controlado e separados do host que executa o SnapOtter.
```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 .
```
## Artefatos de conformidade {#compliance-artifacts}
Os modelos de IA totalizam cerca de 24 GB entre todos os bundles. Como podem ser rebaixados, exclua `/data/ai/` e `/data/venv/` dos backups para economizar espaço. Apenas o banco de dados e os arquivos do usuário são críticos.
Cada versão SnapOtter inclui os seguintes artefatos de segurança:
## Artefatos de Conformidade {#compliance-artifacts}
Cada release do SnapOtter inclui os seguintes artefatos de segurança:
| Artefato | Formato | Onde encontrar |
| Artefato | Formatar | Onde encontrar |
|---|---|---|
| SBOM (CycloneDX) | JSON | Ativo do [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.cdx.json` |
| SBOM (SPDX) | JSON | Ativo do [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.spdx.json` |
| Verificação de vulnerabilidades | Trivy JSON | Ativo do [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-trivy.json` |
| Verificação de vulnerabilidades | SARIF | Aba [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security) |
| Análise estática | CodeQL (JS/TS + Python) | Aba [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security), roda semanalmente + por PR |
| Revisão de dependências | Nativo do GitHub | Verificação por PR, falha em adições de alta severidade |
| Auditoria de dependências Python | pip-audit | Log de execução do CI a cada push |
| Liberar vinculação de assunto | Atestado canônico JSON + GitHub | [Lançamento GitHub](https://github.com/snapotter-hq/SnapOtter/releases) ativo: `snapotter-v{version}-release-subjects.json` |
| Arquivo SBOM | CycloneDX e SPDX JSON | Liberar ativos: `snapotter-v{version}-archive-linux-{arch}-sbom.{cdx,spdx}.json` |
| Imagem SBOM | CycloneDX e SPDX JSON | Liberar ativos: `snapotter-v{version}-image-linux-{arch}-sbom.{cdx,spdx}.json` |
| Verificações de vulnerabilidade | Trivy JSON | Liberar ativos com prefixos `archive-linux-{arch}` ou `image-linux-{arch}` correspondentes |
| Verificação de vulnerabilidade | SARIF | Guia [Segurança GitHub](https://github.com/snapotter-hq/SnapOtter/security) |
| Análise estática | CodeQL (JS/TS + Python) | Guia [Segurança GitHub](https://github.com/snapotter-hq/SnapOtter/security), executada semanalmente + por PR |
| Revisão de dependência | GitHub nativo | Verificação por PR, falha em adições de alta gravidade |
| Auditoria de dependência Python | pip-audit | Log de execução do CI em cada push |
| Política de segurança | Markdown | [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) no repositório |
| Atualizações de dependências | Dependabot | PRs semanais automatizados para npm, pip, Docker, Actions |
| Atualizações de dependência | Dependabot | PRs semanais automatizados para npm, pip, Docker, Actions |
**Rodando sua própria verificação:**
**Executando sua própria verificação:**
Baixe o SBOM do release e faça a verificação com a ferramenta de sua preferência:
Baixe o manifesto do assunto do lançamento e verifique se ele foi atestado pelo fluxo de trabalho do lançamento:
```bash
gh attestation verify snapotter-v2.1.0-release-subjects.json \
--repo snapotter-hq/SnapOtter \
--signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml
```
O manifesto registra `releaseTag`, `releaseCommit` e `workflowTriggerCommit` separadamente. Verifique se `releaseCommit` é o commit retirado da tag imutável e, em seguida, verifique o resumo SHA-256 do arquivo, imagem, SBOM ou varredura que você consome em relação à sua entrada em `subjects`. Essa distinção é intencional: o check-out de um commit de versão recém-criado não altera a identidade do commit na credencial OIDC do fluxo de trabalho.
Você também pode digitalizar um SBOM baixado ou a imagem diretamente:
```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
O SBOM e a verificação de vulnerabilidades refletem a imagem exata publicada para aquele release. Os bundles de modelos de IA instalados após a implantação não estão incluídos no SBOM, já que são baixados em tempo de execução.
::: info
A imagem SBOMs e as varreduras refletem a imagem exata específica da arquitetura publicada para essa versão. O arquivo SBOMs e as varreduras descrevem o arquivo pré-construído separadamente. Os pacotes configuráveis do modelo AI instalados após a implementação não são incluídos nestes SBOMs porque são baixados no tempo de execução.
:::
+6 -2
View File
@@ -11,7 +11,7 @@ O SnapOtter processa arquivos em cinco modalidades: imagem, vídeo, áudio, PDF
## Formatos de Imagem {#image-formats}
O SnapOtter suporta mais de 55 formatos de imagem para entrada e 13 formatos para saída.
O SnapOtter suporta mais de 55 formatos de imagem para entrada e 17 formatos para saída.
## Formatos de Entrada {#input-formats}
@@ -104,7 +104,7 @@ O SnapOtter suporta mais de 55 formatos de imagem para entrada e 13 formatos par
| PAM | .pam | Sharp (nativo) | Mapa arbitrário |
| PFM | .pfm | Sharp (nativo) | Mapa de ponto flutuante |
## Formatos de Saída (13) {#output-formats-13}
## Formatos de Saída (17) {#output-formats-13}
| Formato | Codificador | Controle de Qualidade | Disponível Em |
|--------|---------|----------------|-------------|
@@ -121,6 +121,10 @@ O SnapOtter suporta mais de 55 formatos de imagem para entrada e 13 formatos par
| ICO | CLI ImageMagick | Sem perdas | Ferramenta de conversão |
| JP2 | CLI opj_compress | Taxa de compressão | Ferramenta de conversão |
| QOI | Codec inline | Sem perdas | Ferramenta de conversão |
| PSD | CLI ImageMagick | Sem perdas | Ferramenta de conversão |
| PPM | CLI ImageMagick | Sem perdas | Ferramenta de conversão |
| EPS | CLI ImageMagick | Sem perdas | Ferramenta de conversão |
| TGA | CLI ImageMagick | Sem perdas | Ferramenta de conversão |
## Formatos de Vídeo {#video-formats}
+13 -10
View File
@@ -1,8 +1,9 @@
---
description: "Gerencie usuários, papéis integrados e personalizados, permissões, chaves de API, times, sessões e o log de auditoria no SnapOtter."
i18n_source_hash: 5e28af686c96
i18n_source_hash: bea8955f3aff
i18n_provenance: human
i18n_output_hash: 520ee01a5650
i18n_output_hash: 4146f4acdfc0
i18n_hash_version: 2
---
# Usuários, Papéis e Permissões {#users-roles-permissions}
@@ -82,12 +83,12 @@ Todas as 17 permissões. Controle total sobre a instância.
| `pipelines:all` | Ver e gerenciar os pipelines de todos os usuários |
| `settings:read` | Ver as configurações da instância |
| `settings:write` | Modificar as configurações da instância |
| `users:manage` | Criar, atualizar e excluir contas de usuário |
| `users:manage` | Criar e gerenciar contas de usuário dentro dos limites de autoridade do ator |
| `teams:manage` | Criar, atualizar e excluir times |
| `features:manage` | Instalar e gerenciar bundles de recursos de IA |
| `system:health` | Acessar os endpoints de health e readiness |
| `audit:read` | Ver o log de auditoria e listar papéis |
| `compliance:manage` | Gerenciar o ciclo de vida de GDPR e recursos de conformidade |
| `compliance:manage` | Gerenciar o ciclo de vida e os recursos de conformidade do GDPR; operações destrutivas do usuário permanecem limitadas pela autoridade |
| `webhooks:manage` | Configurar webhooks de saída |
| `security:manage` | Gerenciar configurações de segurança (lista de IPs permitidos, imposição de SSO) |
@@ -110,15 +111,17 @@ curl -X POST http://localhost:1349/api/v1/roles \
Os nomes de papéis devem ter de 2 a 30 caracteres, alfanuméricos em minúsculas, com hifens e underscores.
### Permissões reservadas ao admin {#admin-reserved-permissions}
### Limites de administração delegada {#delegated-administration-boundaries}
Três permissões são reservadas aos papéis integrados e não podem ser atribuídas a papéis personalizados:
Todas as 17 permissões podem ser delegadas por meio de funções personalizadas, mas uma permissão administrativa não torna essa função equivalente à função `admin` integrada. Mutações de usuário autorizadas por `users:manage`, operações destrutivas autorizadas por `compliance:manage` e gerenciamento de função personalizada autorizada por `security:manage` são limitadas pela autoridade atual do ator:
- `compliance:manage`
- `webhooks:manage`
- `security:manage`
- As funções integradas seguem `admin` > `editor` > `user`; as funções personalizadas estão abaixo das funções integradas.
- As permissões do alvo devem estar contidas nas permissões **efetivas** do ator. Uma chave de API com escopo definido, portanto, não pode exercer permissões omitidas de seu escopo.
- O acesso à ferramenta de uma função alvo deve ser contido pelo acesso à ferramenta do próprio ator.
- Uma conta desativada é verificada em relação à sua função original quando essa função é registrada como `disabled:<original-role>`.
- A exclusão de uma função personalizada também requer autoridade para atribuir o substituto `user` integrado; membros desabilitados permanecem desabilitados como `disabled:user`.
A API de papéis rejeita qualquer requisição que inclua essas permissões. Somente o papel integrado `admin` tem acesso a elas.
As credenciais e a configuração globais são mais rigorosas: a emissão ou revogação do token SCIM e a importação da configuração da instância exigem a função `admin` integrada com autoridade administrativa completa e efetiva.
### Permissões no nível de ferramenta {#tool-level-permissions}