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.
6.9 KiB
i18n_source_hash, i18n_provenance, i18n_output_hash, i18n_hash_version
| i18n_source_hash | i18n_provenance | i18n_output_hash | i18n_hash_version |
|---|---|---|---|
| 521c03a6416c | machine | ee71505c5fbe | 2 |
Ambientes com Poucos Recursos
O SnapOtter roda bem em hardware modesto: um Raspberry Pi 4 ou 5, um notebook antigo ou um VPS de 2 GB. Esta página é o guia prático para essas máquinas: o que esperar, uma configuração pronta para copiar e colar com limites sensatos e quais features pular. Os dados completos de benchmark por trás desses números estão em Requisitos de Hardware.
Antes de tudo, duas restrições rígidas:
- Apenas 64 bits. A imagem é construída para
linux/amd64elinux/arm64. ARM de 32 bits (armv7/armhf) não é suportado, então os Pis de primeira geração e a família Pi Zero ficam de fora. - Piso de memória de 2 GB. Com 512 MB a stack nem inicia, e 1 GB falha em lotes com vários arquivos. 2 GB com 2 núcleos é a menor configuração que funciona com folga.
O que roda bem em hardware modesto
Toda ferramenta sem IA funciona em uma máquina de 2 GB / 2 núcleos: as seções de Imagem e Arquivos inteiras, as ferramentas de PDF e as operações de vídeo e áudio por stream-copy (cortar, silenciar, remux de contêiner). A maioria termina em menos de um segundo.
Duas cargas de trabalho são as exceções:
- Recodificação de vídeo (converter entre codecs) é limitada pela CPU. Um clipe 1080p que leva ~40 s em uma CPU de desktop rápida pode levar vários minutos em uma CPU da classe do Pi. As operações de stream-copy continuam instantâneas.
- Ferramentas de IA precisam de RAM (4 GB recomendados) e disco (os bundles maiores têm 4-5 GB cada), e as pesadas (upscale, restauração de fotos, remoção de fundo) não são práticas em CPUs da classe do Pi. IA leve, como detecção de rosto e OCR, é utilizável se você tiver memória para isso.
Nenhuma das duas é instalada ou fica rodando a menos que você a use: sem bundles de IA instalados, o aplicativo fica ocioso em torno de 360 MB, e os bundles de IA só são baixados quando um admin os habilita.
Passo a passo para Raspberry Pi / notebook antigo
Esta é a instalação padrão com Compose de Primeiros Passos, mais limites de recursos e tetos conservadores. Ela pressupõe um sistema operacional de 64 bits (em um Pi: Raspberry Pi OS 64-bit ou Ubuntu Server arm64).
services:
snapotter:
image: snapotter/snapotter:latest
ports:
- "1349:1349"
volumes:
- ./snapotter-data:/data
environment:
- DATABASE_URL=postgres://snapotter:snapotter@db:5432/snapotter
- REDIS_URL=redis://redis:6379
# Small-box profile: see the table below for what each cap does.
- CONCURRENT_JOBS=1
- MAX_WORKER_THREADS=2
- MAX_BATCH_SIZE=5
- MAX_UPLOAD_SIZE_MB=100
- MAX_MEGAPIXELS=50
- MAX_VIDEO_DURATION_S=300
deploy:
resources:
limits:
cpus: "2"
memory: 2G
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:17-alpine
environment:
- POSTGRES_USER=snapotter
- POSTGRES_PASSWORD=snapotter # Altere isso para implantações não locais
- POSTGRES_DB=snapotter
volumes:
- ./postgres-data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:8-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy noeviction
restart: unless-stopped
Observações para máquinas da classe do Pi:
- Prefira um SSD USB a um cartão SD para o volume de dados e o Postgres. As áreas de trabalho dos jobs fazem IO de disco de verdade, e cartões SD são lentos e se desgastam rápido.
- O contêiner único tudo-em-um também funciona aqui (Postgres e Redis embutidos quando
DATABASE_URL/REDIS_URLnão estão definidos), e em um host com pouca memória você deve reduzir o teto do Redis embutido comREDIS_MAXMEMORY(veja Configuração). O Compose dá um controle mais fino por serviço, e é por isso que este passo a passo o utiliza. - Adicione swap em dispositivos de 2 GB. Isso evita que um pico ocasional (um PDF grande, um lote que você esqueceu de limitar) termine em um kill por falta de memória. zram é a opção amigável ao cartão SD.
- A imagem arm64 é apenas CPU; não há CUDA em placas ARM.
Os ajustes disponíveis
Todos os limites são variáveis de ambiente, documentadas por completo em Configuração. 0 significa ilimitado ou automático. Os que importam em hardware modesto:
| Variável | Sugestão para máquinas pequenas | O que protege |
|---|---|---|
CONCURRENT_JOBS |
1 |
Quantos jobs rodam em paralelo. A detecção automática usa o número de núcleos de CPU menos um, o que funciona bem em máquinas grandes e é agressivo demais em uma máquina de 2 núcleos sob pressão de memória. |
MAX_WORKER_THREADS |
2 |
Pool de threads de processamento de imagem. |
MAX_BATCH_SIZE |
5 |
É nos lotes que máquinas de 1-2 GB ficam sem memória primeiro. |
MAX_UPLOAD_SIZE_MB |
100 |
Impede que um único arquivo enorme ocupe toda a área de trabalho. |
MAX_MEGAPIXELS |
50 |
Decodificar uma imagem de 100+ MP custa RAM independentemente do tamanho do arquivo. |
MAX_VIDEO_DURATION_S |
300 |
Transcodificações longas monopolizam uma CPU pequena por minutos ou horas. |
PROCESSING_TIMEOUT_S |
600 |
Teto rígido para que um job descontrolado acabe liberando a máquina. |
Esses limites se aplicam ao que o servidor aceita, então defina-os de acordo com o que você realmente usa, e não com o menor valor possível. Se você nunca mexe com vídeo, um limite em MAX_VIDEO_DURATION_S não custa nada; se você digitaliza documentos todos os dias, não limite MAX_PDF_PAGES.
O que pular
- Bundles de IA pesados. Upscale, restauração de fotos e remoção de fundo pedem uma GPU ou uma CPU rápida com muitos núcleos, e cada bundle custa 4-5 GB de disco. Em uma máquina pequena, simplesmente não os instale; ferramentas cujo bundle está ausente mostram um aviso de instalação em vez de rodar.
- Recodificação de vídeo como carga de trabalho rotineira. Transcodificações ocasionais são aceitáveis (só são lentas); uma fila constante de transcodificação pede núcleos de CPU, não um Pi.
- Ferramentas não usadas em geral. Um admin pode desligar ferramentas individuais em Configurações, o que as remove da interface e deixa de registrar suas rotas de API. Isso por si só não economiza memória, mas evita que uma instância pequena compartilhada seja usada justamente para a carga de trabalho que o hardware não aguenta.
Se mais tarde você mover a instância para um hardware maior, remova os limites (defina-os de volta para 0) e o mesmo volume de dados vai junto.