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: "Struktur monorepo, arsitektur aplikasi dan paket, siklus hidup permintaan, dan jejak sumber daya SnapOtter."
i18n_output_hash: d4bd8ceef301
i18n_source_hash: a53946e760b0
i18n_source_hash: 50e076925c4b
i18n_provenance: human
i18n_output_hash: d266f832d3f1
i18n_hash_version: 2
---
# Arsitektur {#architecture}
@@ -52,7 +53,7 @@ Tipe TypeScript bersama, konstanta (seperti `APP_VERSION` dan definisi tool), da
### API (`apps/api`) {#api-apps-api}
Server Fastify v5 yang mengekspos 241 route tool di lima modalitas (image, video, audio, PDF, file) yang menangani:
Server Fastify v5 yang mengekspos 243 route tool di lima modalitas (image, video, audio, PDF, file) yang menangani:
- Unggahan file, manajemen workspace sementara, dan penyimpanan file persisten
- Pustaka file pengguna (tabel `user_files`): secara default, sebuah editan yang disimpan disimpan sebagai file baru yang independen, atau sebagai versi yang tertaut ke induk ketika Anda menimpa file asli. Ia mencatat tool mana yang diterapkan (`toolChain`) dan mendapatkan thumbnail yang dibuat otomatis untuk halaman Files
- Eksekusi tool (mengarahkan setiap permintaan tool ke image engine atau AI bridge)
+40 -17
View File
@@ -1,8 +1,9 @@
---
description: "Semua variabel lingkungan SnapOtter dengan default-nya. Konfigurasi auth, penyimpanan, model AI, analitik, dan lainnya."
i18n_source_hash: 8e9e9ca2840c
i18n_source_hash: 25970c776f7c
i18n_provenance: human
i18n_output_hash: 48af4392804f
i18n_output_hash: 9c349ff65524
i18n_hash_version: 2
---
# Konfigurasi {#configuration}
@@ -19,28 +20,51 @@ Semua konfigurasi dilakukan melalui variabel lingkungan. Setiap variabel memilik
| `RATE_LIMIT_PER_MIN` | `1000` | Permintaan maksimum per menit per IP. Setel ke 0 untuk menonaktifkan pembatasan laju. |
| `CORS_ORIGIN` | (kosong) | Origin yang diizinkan dipisahkan koma untuk CORS, atau kosong untuk hanya same-origin. |
| `LOG_LEVEL` | `info` | Verbositas log. Salah satu dari: `fatal`, `error`, `warn`, `info`, `debug`, `trace`. |
| `TRUST_PROXY` | `true` | Percayai header `X-Forwarded-For` dari reverse proxy. Setel ke `false` jika tidak di belakang proxy. |
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Peer mana yang boleh menetapkan IP klien lewat `X-Forwarded-For`. Nilai default hanya memercayai peer di jaringan privat, jadi reverse proxy di jaringan Docker atau di LAN dipercaya sedangkan header palsu dari klien publik tidak. Setel `true` hanya jika di depan ada proxy yang Anda kendalikan pada alamat publik. |
### Autentikasi {#authentication}
Dua boolean di bawah ini hanya menerima `true` dan `false`. Apa pun selain itu, `1` atau `yes` atau `on`, gagal validasi dan server keluar sebelum mulai mendengarkan.
| Variabel | Default | Deskripsi |
|---|---|---|
| `AUTH_ENABLED` | `false` | Setel ke `true` untuk mewajibkan login. Image Docker default ke `true`. |
| `AUTH_ENABLED` | `true` | Wajibkan login. Setel ke `false` untuk berjalan tanpa akun sama sekali, yang memberi setiap permintaan hak admin, jadi batasi itu pada jaringan tepercaya. |
| `DEFAULT_USERNAME` | `admin` | Nama pengguna untuk akun admin awal. Hanya digunakan pada run pertama. |
| `DEFAULT_PASSWORD` | `admin` | Kata sandi untuk akun admin awal. Ubah ini setelah login pertama. |
| `MAX_USERS` | `0` (tak terbatas) | Jumlah maksimum akun pengguna terdaftar. Setel ke 0 untuk tak terbatas. |
| `SESSION_DURATION_HOURS` | `168` | Masa hidup sesi login dalam jam (default adalah 7 hari). |
| `SKIP_MUST_CHANGE_PASSWORD` | - | Setel ke nilai apa pun yang tidak kosong untuk melewati prompt ganti-kata-sandi paksa pada login pertama |
| `SKIP_MUST_CHANGE_PASSWORD` | `false` | Setel ke `true` untuk melewati prompt ganti-kata-sandi paksa pada login pertama. |
### Penyimpanan {#storage}
| Variabel | Default | Deskripsi |
|---|---|---|
| `STORAGE_MODE` | `local` | `local` atau `s3`. S3/MinIO memerlukan lisensi dengan fitur s3_storage. |
| `DATABASE_URL` | `postgres://snapotter:snapotter@postgres:5432/snapotter` | String koneksi PostgreSQL. |
| `REDIS_URL` | `redis://redis:6379` | String koneksi Redis (digunakan untuk antrean job BullMQ). |
| `WORKSPACE_PATH` | `./tmp/workspace` | Direktori untuk file sementara selama pemrosesan. Dibersihkan secara otomatis. |
| `FILES_STORAGE_PATH` | `./data/files` | Direktori untuk file pengguna persisten (gambar yang diunggah, hasil yang disimpan). |
| `STORAGE_MODE` | `local` | `local` atau `s3`. S3 dan MinIO memerlukan lisensi dengan fitur s3_storage plus variabel `S3_*` di bawah. |
| `DATABASE_URL` | `postgres://snapotter:snapotter@localhost:5432/snapotter` | String koneksi PostgreSQL. Stack Compose mengarahkan ini ke layanan `postgres` miliknya; biarkan tidak diatur (bersama `REDIS_URL`) untuk mendapatkan mode tertanam. |
| `REDIS_URL` | `redis://localhost:6379` | String koneksi Redis (digunakan untuk antrean job BullMQ). Compose mengarahkan ini ke layanan `redis` miliknya. |
| `WORKSPACE_PATH` | `./tmp/workspace` | Direktori untuk file sementara selama pemrosesan. Dibersihkan secara otomatis. Image menyetelnya ke `/tmp/workspace`. |
| `FILES_STORAGE_PATH` | `./data/files` | Direktori untuk file pengguna persisten (gambar yang diunggah, hasil yang disimpan). Image menyetelnya ke `/data/files`. |
### Penyimpanan objek S3 {#s3-object-storage}
Hanya dibaca saat `STORAGE_MODE=s3`. Lewatkan salah satu dari tiga yang wajib dan startup gagal dengan menyebut nama variabel yang Anda tinggalkan.
| Variabel | Default | Deskripsi |
|---|---|---|
| `S3_BUCKET` | (kosong) | Bucket yang menyimpan unggahan dan keluaran. Wajib. |
| `S3_ACCESS_KEY_ID` | (kosong) | Access key. Wajib. Di dalam kontainer Anda bisa memasangnya sebagai file, lewat `S3_ACCESS_KEY_ID_FILE`. |
| `S3_SECRET_ACCESS_KEY` | (kosong) | Secret key. Wajib. Konvensi file yang sama: `S3_SECRET_ACCESS_KEY_FILE`. |
| `S3_REGION` | `us-east-1` | Region bucket. |
| `S3_ENDPOINT` | (kosong) | Endpoint kustom untuk MinIO, R2, Backblaze, dan penyimpanan lain yang kompatibel dengan S3. Kosong berarti AWS. |
| `S3_FORCE_PATH_STYLE` | `false` | Setel ke `true` untuk MinIO dan apa pun yang menginginkan `endpoint/bucket/key` alih-alih pengalamatan virtual-host. |
| `S3_PREFIX` | (kosong) | Prefiks key, sehingga satu bucket bisa menampung beberapa instans. |
### Enkripsi saat disimpan {#encryption-at-rest}
| Variabel | Default | Deskripsi |
|---|---|---|
| `DATA_ENCRYPTION_KEY` | (kosong) | 64 karakter heksadesimal (32 byte). Mengenkripsi pengaturan sensitif yang disimpan di basis data. Apa pun yang bukan 64 karakter heksadesimal ditolak saat startup. |
| `DATA_ENCRYPTION_KEY_PREVIOUS` | (kosong) | Kunci yang sedang Anda tinggalkan dalam rotasi, format yang sama. Setel keduanya selama rotasi agar baris yang ada tetap bisa didekripsi, lalu hapus yang ini. |
### Mode tertanam {#embedded-mode}
@@ -59,16 +83,15 @@ Catatan telemetri: mode tertanam mewarisi default analitik image seperti konfigu
| Variabel | Default | Deskripsi |
|---|---|---|
| `MAX_UPLOAD_SIZE_MB` | `100` | Ukuran file maksimum per unggahan dalam megabyte. Setel ke 0 untuk tak terbatas. |
| `MAX_BATCH_SIZE` | `100` | Jumlah maksimum file dalam satu permintaan batch. Setel ke 0 untuk tak terbatas. |
| `MAX_UPLOAD_SIZE_MB` | `0` (tak terbatas) | Ukuran file maksimum per unggahan dalam megabyte. Setel ke 0 untuk tak terbatas. Image yang diterbitkan dikirim dengan `0`; build dari sumber dimulai pada 100. |
| `MAX_BATCH_SIZE` | `0` (tak terbatas) | Jumlah maksimum file dalam satu permintaan batch. Setel ke 0 untuk tak terbatas. Image yang diterbitkan dikirim dengan `0`; build dari sumber dimulai pada 100. |
| `CONCURRENT_JOBS` | `0` (otomatis) | Jumlah job batch yang berjalan secara paralel. Setel ke 0 untuk deteksi otomatis berdasarkan inti CPU yang tersedia. |
| `MAX_MEGAPIXELS` | `0` (tak terbatas) | Resolusi gambar maksimum yang diizinkan dalam megapiksel. Setel ke 0 untuk tak terbatas. |
| `MAX_WORKER_THREADS` | `0` (otomatis) | Thread worker maksimum untuk pemrosesan gambar. Setel ke 0 untuk deteksi otomatis berdasarkan inti CPU yang tersedia. |
| `PROCESSING_TIMEOUT_S` | `0` (tanpa batas) | Waktu pemrosesan maksimum per permintaan dalam detik. Setel ke 0 untuk tanpa timeout. |
| `MAX_PIPELINE_STEPS` | `20` | Jumlah maksimum langkah dalam sebuah pipeline. Setel ke 0 untuk tanpa batas. |
| `MAX_CANVAS_PIXELS` | `0` (tanpa batas) | Ukuran kanvas maksimum dalam piksel untuk gambar keluaran. Setel ke 0 untuk tanpa batas. |
| `MAX_SVG_SIZE_MB` | `0` (tak terbatas) | Ukuran file SVG maksimum dalam megabyte. Setel ke 0 untuk tak terbatas. |
| `MAX_SPLIT_GRID` | `100` | Dimensi kisi maksimum untuk tool split gambar. |
| `MAX_SVG_SIZE_MB` | `50` | SVG terbesar yang diterima sebelum disanitasi, dalam megabyte. `0` berperilaku berbeda di sini dibanding baris-baris di sekitarnya. Ia menghapus batas ukuran pra-parse sepenuhnya alih-alih menaikkannya, jadi biarkan yang satu ini tetap disetel. |
| `MAX_PDF_PAGES` | `0` (tak terbatas) | Jumlah maksimum halaman PDF untuk konversi PDF-ke-image. Setel ke 0 untuk tak terbatas. |
### Pembersihan {#cleanup}
@@ -82,7 +105,7 @@ Catatan telemetri: mode tertanam mewarisi default analitik image seperti konfigu
| Variabel | Default | Deskripsi |
|---|---|---|
| `DEFAULT_THEME` | `light` | Tema default untuk sesi baru. `light` atau `dark`. |
| `DEFAULT_THEME` | `light` | Tema default untuk sesi baru. `light`, `dark`, atau `system`. |
| `DEFAULT_LOCALE` | `en` | Bahasa antarmuka default. |
| `DEFAULT_TOOL_VIEW` | `sidebar` | Tata letak tool default. `sidebar` atau `fullscreen`. |
@@ -124,13 +147,13 @@ services:
image: postgres:17-alpine
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_PASSWORD: snapotter # Ubah ini untuk penerapan non-lokal
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: "Cara berkontribusi ke SnapOtter. Laporan bug, permintaan fitur, pull request, dan persyaratan CLA."
i18n_source_hash: 528802503035
i18n_source_hash: 6c920a5f83e0
i18n_provenance: human
i18n_output_hash: bf536bb687ec
i18n_output_hash: 33567fdbf8ef
i18n_hash_version: 2
---
# Berkontribusi {#contributing}
@@ -53,7 +54,7 @@ Jika Anda berkontribusi atas nama pemberi kerja Anda dan pemberi kerja Anda meme
### Prasyarat {#prerequisites}
- Node.js 22+
- Node.js 22.22+
- pnpm 9+
- Python 3.11+ (hanya untuk tool AI)
- Docker (opsional, untuk pengujian integrasi penuh)
@@ -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
```
+35 -15
View File
@@ -1,8 +1,9 @@
---
description: "Skema database PostgreSQL, tabel, migrasi, dan prosedur pencadangan untuk SnapOtter."
i18n_source_hash: 50d5d4f220cf
i18n_provenance: human
i18n_output_hash: 8a963902d4f7
i18n_source_hash: a68264552836
i18n_provenance: machine
i18n_output_hash: e23431b3174a
i18n_hash_version: 2
---
# Database {#database}
@@ -145,6 +146,17 @@ Log aksi yang relevan dengan keamanan.
| `details` | jsonb | Data khusus aksi |
| `createdAt` | timestamp | Waktu aksi |
### user_preferences {#user-preferences}
Status UI per pengguna, dikunci berdasarkan nama preferensi. Menyimpan alat yang disematkan di halaman beranda, yang ditulis melalui `PUT /api/v1/preferences`.
| Kolom | Tipe | Catatan |
|---|---|---|
| `userId` | text | FK ke users, menghapus secara berantai. Primary key bersama `key` |
| `key` | text | Nama preferensi. Primary key bersama `userId` |
| `value` | jsonb | Muatan preferensi |
| `updatedAt` | timestamp | Penulisan terakhir |
## Migrasi {#migrations}
Drizzle menangani migrasi skema. File migrasi berada di `apps/api/drizzle/`. Selama pengembangan:
@@ -157,29 +169,37 @@ npx drizzle-kit migrate # apply pending migrations
Di produksi, migrasi yang tertunda diterapkan secara otomatis saat startup.
## Pencadangan dan pemulihan {#backup-and-restore}
## Cadangkan dan pulihkan {#backup-and-restore}
Database relasional berada di volume `SnapOtter-pgdata` kontainer Postgres, bukan di volume `/data` aplikasi.
Basis data relasional berada di volume `SnapOtter-pgdata` container Postgres, bukan volume `/data` aplikasi.
**Opsi 1: pg_dump (direkomendasikan)**
**Cadangan logis dengan validasi (disarankan)**
```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
```
**Opsi 2: Snapshot volume**
Dump database ini tidak berisi objek perpustakaan yang disimpan di `/data/files` atau status BullMQ yang tahan lama di Redis. Cadangkan dan pulihkan dengan prosedur terkoordinasi di [Keamanan & Pengerasan](/id/guide/security#backup-and-recovery).
**Snapshot volume dingin**
```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
```
Jangan menyalin direktori data PostgreSQL langsung dengan `tar`. Tulis nama volume awalan berdasarkan proyek, jadi selesaikan ID volume yang terpasang dari `docker inspect` atau platform penyimpanan Anda daripada menggunakan label literal `SnapOtter-pgdata`.
### Migrasi dari 1.x (SQLite) {#migrating-from-1-x-sqlite}
Memutakhirkan dari SnapOtter 1.x memiliki panduannya sendiri: lihat [Memutakhirkan dari 1.x ke 2.0](./upgrading). Singkatnya, gunakan kembali volume `/data` Anda yang ada dan 2.0 otomatis mendeteksi serta mengimpor `/data/snapotter.db` pada boot pertama (atau atur `SQLITE_MIGRATE_PATH` untuk menunjuk ke sana secara eksplisit). Cadangkan seluruh volume `/data` terlebih dahulu, bukan hanya `snapotter.db`: 1.x menggunakan mode SQLite WAL, sehingga kontainer yang dihentikan sering meninggalkan sebagian besar datanya di `snapotter.db-wal` di samping `snapotter.db` yang hampir kosong.
+23 -12
View File
@@ -1,8 +1,9 @@
---
description: "Deploy SnapOtter ke produksi dengan Docker. Persyaratan perangkat keras, penyiapan GPU, dan konfigurasi reverse proxy untuk Nginx, Traefik, dan Cloudflare."
i18n_output_hash: 5ed614569b73
i18n_source_hash: 98172965118b
i18n_source_hash: 2a722f86da75
i18n_provenance: human
i18n_output_hash: 5f4c54af9c3d
i18n_hash_version: 2
---
# Deployment {#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 # Ubah ini untuk penerapan non-lokal
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,6 +208,8 @@ volumes:
docker compose -f docker-compose-gpu.yml up -d
```
### Verifikasi akselerasi GPU {#verify-gpu-acceleration}
Periksa deteksi CUDA di log:
```bash
@@ -214,6 +217,8 @@ docker logs SnapOtter 2>&1 | head -20
# Look for: [gpu] CUDA available via torch
```
Jika alat AI berjalan di CPU meskipun `--gpus all` dan NVIDIA Container Toolkit telah dikonfigurasi dengan benar, instal ulang bundel yang terpengaruh (misalnya Penghapusan Latar Belakang) dari **Pengaturan → Fitur AI**. Penginstal memulihkan build GPU ONNX Runtime, yang mana build khusus CPU yang ditarik oleh bundel lain (seperti transkripsi) dapat membayangi lingkungan AI bersama. Jika menginstal ulang dari UI tidak memulihkan GPU pada image lama, lihat perbaikan manual di [masalah #490](https://github.com/snapotter-hq/SnapOtter/issues/490).
## Persyaratan Perangkat Keras {#hardware-requirements}
Angka-angka ini berasal dari benchmark di berbagai sistem, mulai dari workstation amd64 modern dengan NVIDIA RTX 4070 hingga Raspberry Pi, menjalankan seluruh katalog perkakas pada masing-masing dan menyapu batas sumber daya Docker untuk menemukan batas bawah yang sebenarnya.
@@ -436,11 +441,11 @@ Kesalahan startup menyebutkan UID persis yang harus digunakan, jadi jalur tercep
| `AUTH_ENABLED` | `true` | Aktifkan/nonaktifkan persyaratan login |
| `DEFAULT_USERNAME` | `admin` | Username admin awal |
| `DEFAULT_PASSWORD` | `admin` | Kata sandi admin awal (dipaksa ganti saat login pertama) |
| `MAX_UPLOAD_SIZE_MB` | `100` | Batas unggahan per file |
| `MAX_BATCH_SIZE` | `100` | Maksimum file per permintaan batch |
| `MAX_UPLOAD_SIZE_MB` | `0` (tak terbatas) | Batas unggahan per file dalam MB. Image dikirim dengan `0`; build dari kode sumber mulai dari 100 |
| `MAX_BATCH_SIZE` | `0` (tak terbatas) | Maksimum file per permintaan batch. Image dikirim dengan `0`; build dari kode sumber mulai dari 100 |
| `RATE_LIMIT_PER_MIN` | `1000` | Permintaan API per menit per IP (atur 0 untuk menonaktifkan) |
| `MAX_USERS` | `0` (tak terbatas) | Maksimum akun pengguna |
| `TRUST_PROXY` | `true` | Percayai header X-Forwarded-For dari reverse proxy |
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Peer mana yang boleh menetapkan IP klien lewat `X-Forwarded-For`. Hanya jaringan privat secara default |
| `PUID` | `999` | Jalankan sebagai UID ini (untuk izin bind mount) |
| `PGID` | `999` | Jalankan sebagai GID ini (untuk izin bind mount) |
| `LOG_LEVEL` | `info` | Verbositas log: fatal, error, warn, info, debug, trace |
@@ -483,7 +488,13 @@ curl http://localhost:1349/api/v1/health
## Reverse Proxy {#reverse-proxy}
SnapOtter mengatur `TRUST_PROXY=true` secara default sehingga pembatasan laju dan logging menggunakan IP klien sebenarnya dari header `X-Forwarded-For`.
`TRUST_PROXY` secara default bernilai `loopback,linklocal,uniquelocal`, jadi SnapOtter hanya memercayai `X-Forwarded-For` dari peer di jaringan privat. Reverse proxy di host yang sama, di jaringan Docker, atau di LAN Anda langsung dipercaya, sehingga pembatasan laju, pembatas brute force pada login, log audit, dan daftar IP yang diizinkan di edisi enterprise semuanya melihat IP klien yang sebenarnya tanpa konfigurasi apa pun.
Setel `TRUST_PROXY=true` hanya jika proxy di depan menjangkau SnapOtter dari alamat **publik**, misalnya load balancer cloud di jaringan lain. Pada instance yang terekspos langsung, nilai itu membuat `request.ip` dikendalikan penyerang, karena pemanggil yang terus mengganti header mendapat penghitung batas laju baru di setiap permintaan.
Ada dua hal yang perlu diketahui sebelum Anda mulai mengukur IP klien. Docker Desktop di macOS dan Windows menyajikan port yang dipublikasikan lewat proxy di ruang pengguna yang menulis ulang setiap alamat sumber menjadi gateway VM `192.168.65.1`, jadi di sana tidak ada nilai `TRUST_PROXY` yang bisa mengembalikan klien aslinya; terapkan di Linux untuk apa pun yang menghadap internet. Dan di platform mana pun, mencapai port yang dipublikasikan lewat `localhost` terlihat sebagai gateway bridge, bukan sebagai klien Anda, sehingga uji coba lewat localhost tidak memberi tahu apa pun tentang cara klien sungguhan diatribusikan. Tabel lengkap nilai `TRUST_PROXY` dan catatan tentang Docker Desktop ada di [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md#client-ip-resolution-trust_proxy).
Ada dua hal yang penting untuk setiap proxy di bawah ini: izinkan badan permintaan yang besar (unggahan), dan jangan melakukan buffering terhadap tanggapan. Proksi buffering respons menghentikan kemajuan SSE dan, yang lebih terlihat, membuat pengunduhan file besar "mulai tetapi tidak pernah selesai", karena proksi menyimpan seluruh file sebelum meneruskannya. SnapOtter mengirimkan `X-Accel-Buffering: no` pada unduhan sehingga nginx mengalirkannya meskipun buffering dibiarkan di tempat lain, namun proxy selain nginx memerlukan buffering respons yang dinonaktifkan secara eksplisit (ditunjukkan pada setiap konfigurasi di bawah). Jika pengunduhan terhenti di tengah jalan, proxy buffering di depan adalah hal pertama yang harus diperiksa.
### 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)
# Respons streaming alih-alih buffering: diperlukan untuk kemajuan SSE (batch, AI, pemasangan fitur) dan untuk download file besar.
proxy_buffering off;
proxy_read_timeout 300s;
}
@@ -549,7 +560,7 @@ images.example.com {
}
```
`flush_interval -1` menonaktifkan buffering respons, yang diperlukan untuk event progres SSE (pemrosesan batch, perkakas AI, instalasi fitur). Timeout yang diperpanjang memungkinkan unggahan file besar selesai tanpa Caddy menutup koneksi terlalu dini.
`flush_interval -1` menonaktifkan buffering respons, yang diperlukan untuk peristiwa kemajuan SSE (pemrosesan batch, alat AI, pemasangan fitur) dan untuk pengunduhan file besar agar dapat dilakukan streaming alih-alih terhenti. Batas waktu yang diperpanjang memungkinkan pengunggahan file besar selesai tanpa Caddy menutup koneksi lebih awal.
### Cloudflare Tunnels {#cloudflare-tunnels}
+19 -7
View File
@@ -1,8 +1,9 @@
---
description: "Penyiapan pengembangan lokal, perintah, konvensi kode, dan cara menambahkan tool baru ke SnapOtter."
i18n_source_hash: cb03724d2829
i18n_provenance: human
i18n_output_hash: 3702cfaac3e7
i18n_source_hash: 56acc1bf9a9b
i18n_provenance: machine
i18n_output_hash: 6887a74bc3ef
i18n_hash_version: 2
---
# Panduan developer {#developer-guide}
@@ -11,12 +12,12 @@ Cara menyiapkan lingkungan pengembangan lokal dan berkontribusi kode ke SnapOtte
## Prasyarat {#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/) (diperlukan untuk Postgres + Redis lokal, build kontainer, dan fitur AI)
- Git
Python 3.10+ hanya diperlukan jika Anda mengerjakan sidecar AI/ML (penghapusan latar belakang, upscaling, OCR).
Python 3.11+ hanya diperlukan jika Anda mengerjakan sidecar AI/ML (penghapusan latar belakang, upscaling, OCR).
## Penyiapan {#setup}
@@ -32,10 +33,10 @@ Ini memulai dua server dev:
| Layanan | URL | Catatan |
|----------|--------------------------|------------------------------------|
| Frontend | http://localhost:1349 | Server dev Vite, mem-proxy /api |
| Frontend | http://localhost:1351 | Server dev Vite, mem-proxy /api |
| Backend | http://localhost:13490 | Fastify API (diakses melalui proxy) |
Buka http://localhost:1349 di browser Anda. Login dengan `admin` / `admin`. Anda akan diminta untuk mengubah kata sandi saat login pertama.
Buka http://localhost:1351 di browser Anda. Login dengan `admin` / `admin`. Anda akan diminta untuk mengubah kata sandi saat login pertama.
## Struktur proyek {#project-structure}
@@ -220,6 +221,17 @@ Gunakan cache mount BuildKit untuk build ulang yang lebih cepat:
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile -t snapotter:latest .
```
## Rilis domain versi {#release-version-domains}
SnapOtter sengaja memiliki tiga versi domain. Jangan menyalin satu domain ke domain lain selama rilis:
- Versi rilis aplikasi mencakup manifes root, semua paket ruang kerja pribadi, dan `APP_VERSION`. Semantic-release menyediakan nilai ini, dan `pnpm version:sync <version>` memperbarui setiap ruang kerja sebelum aplikasi dirilis.
- OpenAPI `info.version` adalah kontrak utama publik API yang stabil. Semua spesifikasi yang dilokalkan tetap pada `<major>.0.0` untuk rilis aplikasi yang kompatibel dan hanya berubah ketika kontrak API berpindah ke versi utama baru.
- `docker/feature-manifest.json` menjadikan `imageVersion: 2.0.0` sebagai masa penyimpanan paket fitur lama yang tidak dapat diubah. Jalur arsip v2 tersebut bukan versi paket aplikasi. OCR yang akurat menggunakan format runtime v3 dan mencatat asal rilis aplikasinya secara terpisah.
`tests/unit/infra/release-version-policy.test.ts` menerapkan batasan ini. Domain atau migrasi versi baru harus memperbarui kontrak tersebut dan desain migrasi artefak yang relevan secara bersamaan.
Nilai API independen dan paket warisan ada di `config/release-version-policy.json`; sinkronisasi versi aplikasi tidak boleh menulis ulang file kebijakan tersebut secara implisit.
## Variabel lingkungan {#environment-variables}
Lihat [Panduan Konfigurasi](/id/guide/configuration) untuk daftar lengkap. Yang penting untuk pengembangan:
+8 -7
View File
@@ -1,8 +1,9 @@
---
description: "Tag image Docker SnapOtter, benchmark GPU, penyematan versi, dan dukungan multi-platform untuk AMD64 dan ARM64."
i18n_output_hash: 1285488cc707
i18n_source_hash: fda322e78b4b
i18n_source_hash: 566e20ca07fc
i18n_provenance: human
i18n_output_hash: 30b4ac3f38a5
i18n_hash_version: 2
---
# Image Docker {#docker-image}
@@ -93,13 +94,13 @@ services:
image: postgres:17-alpine
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_PASSWORD: snapotter # Ubah ini untuk penerapan non-lokal
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 @@ Untuk akselerasi NVIDIA CUDA via Docker Compose, tambahkan bagian deploy ke laya
| Tag | Deskripsi |
|-----|------------|
| `latest` | Rilis terbaru |
| `1.11.0` | Versi persis |
| `1.11` | Patch terbaru di 1.11.x |
| `1` | Minor terbaru di 1.x |
| `2.1.0` | Versi persis |
| `2.1` | Patch terbaru di 2.1.x |
| `2` | Minor terbaru di 2.x |
## Platform {#platforms}
+28 -61
View File
@@ -1,8 +1,9 @@
---
description: "Pasang SnapOtter dengan Docker dalam satu perintah. Termasuk penyiapan Docker Compose, membangun dari sumber, dan gambaran lengkap fitur."
i18n_output_hash: 6df615cedb03
i18n_source_hash: 68bf7f60b68d
i18n_provenance: human
i18n_source_hash: 8040133a6982
i18n_provenance: machine
i18n_output_hash: fbe4a9bf48c8
i18n_hash_version: 2
---
# Memulai {#getting-started}
@@ -17,7 +18,7 @@ Jelajahi UI lengkap di [demo.snapotter.com](https://demo.snapotter.com), tanpa p
docker run -d --name SnapOtter -p 1349:1349 -v SnapOtter-data:/data snapotter/snapotter:latest
```
Kontainer tunggal ini menjalankan semua yang dibutuhkannya: tanpa `DATABASE_URL` yang diatur, ia memulai PostgreSQL dan Redis sendiri pada antarmuka loopback (mode embedded) dan menyimpan semua data di volume `SnapOtter-data`. Ini adalah cara tercepat untuk mencoba SnapOtter atau self-host di homelab. Untuk produksi, jalankan stack [Docker Compose](#docker-compose) di bawah, yang menjaga PostgreSQL dan Redis di kontainer mereka sendiri. Mode embedded berjalan sebagai root (default) dan mati secara otomatis segera setelah Anda mengatur `DATABASE_URL`.
Kontainer tunggal ini menjalankan semua yang diperlukan: tanpa set `DATABASE_URL`, ia memulai PostgreSQL dan Redis sendiri pada antarmuka loopback (mode tertanam) dan menyimpan semua data dalam volume `SnapOtter-data`. Ini adalah cara tercepat untuk mencoba SnapOtter atau self-host di homelab. Untuk produksi, gunakan [tumpukan Docker Compose kanonik](#docker-compose), yang menyimpan PostgreSQL dan Redis dalam containernya masing-masing. Mode tertanam berjalan sebagai root (default) dan mati secara otomatis segera setelah Anda mengatur `DATABASE_URL`.
Memasang di Raspberry Pi, laptop lama, atau VPS kecil? Lihat [Penyiapan Sumber Daya Rendah](/id/guide/low-resource) untuk panduan yang sudah disetel dan apa yang bisa diharapkan dari perangkat keras terbatas.
@@ -40,7 +41,7 @@ Tambahkan `--gpus all` untuk penghapusan latar belakang yang dipercepat NVIDIA C
docker run -d --name SnapOtter -p 1349:1349 --gpus all -v SnapOtter-data:/data snapotter/snapotter:latest
```
Membutuhkan [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html). Otomatis kembali ke CPU ketika CUDA tidak tersedia. Akselerasi iGPU Intel/AMD melalui VA-API, Quick Sync, atau OpenCL saat ini tidak didukung untuk inferensi AI. Lihat [Docker Tags](/id/guide/docker-tags) untuk benchmark.
Membutuhkan [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html). Kembali ke CPU secara otomatis ketika CUDA tidak tersedia. Akselerasi Intel/AMD iGPU melalui VA-API, Quick Sync, atau OpenCL tidak didukung untuk inferensi AI saat ini. Lihat [Tag Docker](/id/guide/docker-tags) untuk tolok ukur. Jika alat AI berjalan pada CPU meskipun `--gpus all`, lihat [Verifikasi akselerasi GPU](/id/guide/deployment#verify-gpu-acceleration).
:::
::: details Juga di GHCR
@@ -51,67 +52,33 @@ docker run -d --name SnapOtter -p 1349:1349 -v SnapOtter-data:/data ghcr.io/snap
Kedua registry mempublikasikan image yang sama pada setiap rilis.
:::
## Docker Compose {#docker-compose}
## Penulisan Docker {#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
Gunakan file produksi yang dikelola dan diuji pada setiap rilis alih-alih menyalin contoh Compose yang disingkat dari halaman ini:
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
```
Lihat [Configuration](/id/guide/configuration) untuk semua variabel lingkungan.
[`docker/docker-compose.yml`](https://github.com/snapotter-hq/SnapOtter/blob/v2.1.0/docker/docker-compose.yml) kanonik mencakup keempat volume runtime, health check, batasan sumber daya, konfigurasi Redis yang tahan lama, gambar database/cache yang disematkan, dan pengerasan kontainer saat ini. Ubah kata sandi admin default segera setelah login pertama. Untuk penerapan yang dapat direproduksi, sematkan gambar aplikasi SnapOtter ke tag rilis atau intisari yang Anda verifikasi, bukan mengikuti `latest`.
Lihat [Konfigurasi](/id/guide/configuration) untuk semua variabel lingkungan dan [Keamanan & Pengerasan](/id/guide/security) untuk rahasia, kebijakan jaringan, dan panduan pencadangan.
## Membangun dari Sumber {#build-from-source}
**Prasyarat:** Node.js 22+, pnpm 9+, Docker (untuk Postgres + Redis), Python 3.10+ (untuk fitur AI), Git.
**Prasyarat:** Node.js 22.22+, pnpm 9+, Docker (untuk Postgres + Redis), Python 3.11+ (untuk fitur AI), 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)
## Apa yang Bisa Anda Lakukan {#what-you-can-do}
@@ -130,11 +97,11 @@ pnpm dev
| Modalitas | Jumlah | Contoh Perkakas |
|----------|-------|---------------|
| **Gambar** | 105 | Resize, Crop, Compress, Convert, Remove Background, Upscale, OCR, Watermark, Collage, Colorize, GIF Tools, preset format |
| **Gambar** | 107 | Resize, Crop, Compress, Convert, Remove Background, Upscale, OCR, Watermark, Collage, Colorize, GIF Tools, preset format |
| **Video** | 57 | Trim, Crop, Compress, Convert, Merge, Extract Audio, Auto Subtitles, Video to GIF, Resize, Stabilize, preset format |
| **Audio** | 27 | Trim, Merge, Convert, Normalize, Noise Reduction, Transcribe, Pitch Shift, Fade, Ringtone Maker, preset format |
| **PDF / Dokumen** | 42 | Merge, Split, Compress, OCR, Watermark, Redact, Word to PDF, Excel to PDF, Rotate, Protect, Repair |
| **File** | 10 | CSV to JSON, JSON to XML, Merge CSVs, Split CSV, Create ZIP, Extract ZIP, Chart Maker, YAML/JSON |
| **PDF / Dokumen** | 29 | Merge, Split, Compress, OCR, Watermark, Redact, Word to PDF, Excel to PDF, Rotate, Protect, Repair |
| **File** | 23 | CSV to JSON, JSON to XML, Merge CSVs, Split CSV, Create ZIP, Extract ZIP, Chart Maker, YAML/JSON |
### Pipeline {#pipelines}
+4 -3
View File
@@ -1,7 +1,8 @@
---
i18n_source_hash: f5de74aee1b9
i18n_source_hash: 521c03a6416c
i18n_provenance: machine
i18n_output_hash: 9d7672d6f808
i18n_output_hash: 5bf788afc4ff
i18n_hash_version: 2
---
# Penyiapan Sumber Daya Rendah {#low-resource-setups}
@@ -59,7 +60,7 @@ services:
image: postgres:17-alpine
environment:
- POSTGRES_USER=snapotter
- POSTGRES_PASSWORD=snapotter
- POSTGRES_PASSWORD=snapotter # Ubah ini untuk penerapan non-lokal
- POSTGRES_DB=snapotter
volumes:
- ./postgres-data:/var/lib/postgresql/data
+12 -7
View File
@@ -1,8 +1,9 @@
---
description: "Siapkan provisioning SCIM 2.0 untuk menyinkronkan pengguna dan grup dari penyedia identitas Anda ke SnapOtter. Mencakup Okta, Azure AD / Entra ID, dan integrasi kustom."
i18n_source_hash: bbd50119ec12
i18n_source_hash: 06ee702b386e
i18n_provenance: human
i18n_output_hash: a7f596c5fea9
i18n_output_hash: a8677d4dd025
i18n_hash_version: 2
---
# Provisioning SCIM {#scim-provisioning}
@@ -17,7 +18,7 @@ Provisioning SCIM memerlukan lisensi **enterprise** dengan fitur `scim`. Fitur i
- Sebuah instance SnapOtter yang berjalan dan dapat dijangkau melalui URL publik
- Kunci lisensi enterprise dengan fitur `scim`
- Akses admin ke SnapOtter (izin `users:manage` diperlukan untuk membuat atau mencabut token SCIM)
- Akun SnapOtter `admin` bawaan dengan set izin efektif penuh. Peran khusus yang didelegasikan atau kunci API admin yang tidak memiliki izin admin tidak dapat membuat atau mencabut token SCIM global.
- Akses admin ke pengaturan provisioning penyedia identitas Anda
## Mulai cepat {#quick-start}
@@ -34,7 +35,7 @@ Respons berisi token tersebut. Simpan segera; token tidak dapat diambil kembali.
```json
{
"token": "a1b2c3d4e5f6...",
"token": "so_scim_v2_a1b2c3d4e5f6...",
"message": "Save this token - it cannot be retrieved again"
}
```
@@ -49,15 +50,19 @@ Endpoint SCIM menggunakan Bearer token khusus, terpisah dari sesi pengguna dan A
### Membuat token {#generating-a-token}
`POST /api/v1/enterprise/scim/token` membuat token SCIM baru. Endpoint ini memerlukan sesi valid dengan izin `users:manage`.
`POST /api/v1/enterprise/scim/token` menghasilkan token SCIM baru. Karena token dapat menyediakan dan mengubah pengguna di seluruh instans, titik akhir ini memerlukan peran `admin` bawaan dengan kumpulan izin admin efektif yang lengkap. Memegang `users:manage` dalam peran khusus saja tidak cukup.
Token dikembalikan dalam bentuk teks biasa tepat satu kali. SnapOtter hanya menyimpan hash scrypt. Jika Anda kehilangan token, cabut token tersebut dan buat yang baru.
Hanya satu token SCIM yang aktif pada satu waktu. Membuat token baru akan menggantikan token sebelumnya.
::: warning Penerbitan ulang token setelah peningkatan
Token SCIM lama yang tidak berversi ditolak. Setelah meningkatkan ke rilis yang menerbitkan token `so_scim_v2_...`, buat token baru dan perbarui penyedia identitas Anda sebelum melanjutkan penyediaan.
:::
### Mencabut token {#revoking-a-token}
`DELETE /api/v1/enterprise/scim/token` mencabut token SCIM saat ini. Endpoint ini juga memerlukan `users:manage`.
`DELETE /api/v1/enterprise/scim/token` mencabut token SCIM saat ini. Ini memiliki persyaratan admin bawaan yang sama dengan pembuatan token.
### Pembatasan laju {#rate-limiting}
@@ -279,7 +284,7 @@ Permintaan SCIM tidak menyertakan header `Authorization: Bearer <token>`. Periks
### 401 "Invalid token" {#_401-invalid-token}
Token tidak cocok dengan hash yang tersimpan. Ini terjadi jika token dicabut dan dibuat ulang. Perbarui token di pengaturan provisioning IdP Anda.
Format token salah, menggunakan format tidak berversi yang sudah tidak digunakan lagi, atau tidak cocok dengan hash yang disimpan. Hasilkan token `so_scim_v2_...` saat ini dan perbarui token di pengaturan penyediaan IdP Anda.
### 401 "SCIM not configured" {#_401-scim-not-configured}
+85 -157
View File
@@ -1,8 +1,9 @@
---
description: "Panduan pengerasan keamanan untuk SnapOtter. Keamanan kontainer, isolasi jaringan, Docker secrets, deployment Kubernetes, dan artefak kepatuhan."
i18n_source_hash: 986f7658430c
i18n_provenance: human
i18n_output_hash: 75b0e460c514
i18n_source_hash: 9ff337fa0417
i18n_provenance: machine
i18n_output_hash: d220b4fcee86
i18n_hash_version: 2
---
# Keamanan & Pengerasan {#security-hardening}
@@ -13,131 +14,40 @@ Kontainer berjalan sebagai pengguna non-root khusus (`snapotter`) dengan semua k
## Pengerasan Kontainer {#container-hardening}
[docker-compose.yml default](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) menyertakan pengerasan keamanan produksi. Berikut rincian setiap opsi dan mengapa itu penting:
File Compose [CPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) dan [GPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose-gpu.yml) kanonik adalah sumber kebenarannya. Jangan menyalin contoh yang disingkat ke dalam produksi; menyebarkan file dari tag rilis yang Anda verifikasi.
```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
Kedua tumpukan menerapkan kontrol berikut:
# --- 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
- Batas memori, swap, CPU, dan PID berisi pemrosesan asli yang tidak terkendali.
- Setiap layanan menghilangkan semua kemampuan Linux. Aplikasi hanya menambahkan kembali `CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILL` untuk kepemilikan volume, penurunan identitas `gosu` satu arah, dan penerusan sinyal yang baik. PostgreSQL dan Redis hanya menerima subset yang dibutuhkan oleh titik masuk resmi mereka.
- `security_opt: [no-new-privileges:true]` mencegah proses dalam aplikasi, PostgreSQL, dan kontainer Redis mendapatkan hak istimewa tambahan. Ini tetap kompatibel dengan `gosu`: titik masuk dimulai sebagai root, menyiapkan volume, dan hanya turun ke pengguna `snapotter` khusus.
- Input gambar PostgreSQL dan Redis disematkan oleh intisari. Aplikasi juga harus disematkan ke tag rilis atau intisari yang terverifikasi, bukan `latest`.
- Pemeriksaan kesehatan, rotasi log JSON yang dibatasi, Redis AOF yang tahan lama, dan kebijakan mulai ulang ditentukan secara terpusat dalam file kanonik.
# --- 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
Untuk penerapan yang terhubung ke internet, ikat port 1349 ke loopback dan akhiri TLS pada proksi terbalik yang dikelola. Hasilkan kredensial PostgreSQL dan Redis yang unik, simpan rahasia dalam file yang dilindungi atau manajer rahasia, dan segera ubah kata sandi administrator awal.
# --- Logging ---
logging:
driver: json-file
options:
max-size: "50m" # Rotate logs at 50 MB
max-file: "5" # Keep 5 rotated log files
### Mengapa `read_only` Tidak Disetel {#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
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:
```
### Mengapa `no-new-privileges` Tidak Diatur {#why-no-new-privileges-is-not-set}
`security_opt: [no-new-privileges:true]` sengaja dihilangkan. Entrypoint mulai sebagai root untuk memperbaiki kepemilikan volume, lalu turun ke pengguna `snapotter` melalui [gosu](https://github.com/tianon/gosu), yang membutuhkan setuid. Setelah penurunan hak istimewa selesai, proses berjalan sebagai `snapotter` dengan semua kapabilitas kecuali lima yang tercantum di atas dihapus.
Jika Anda menggunakan Kubernetes atau flag `--user` Docker untuk berjalan sebagai non-root secara langsung (melewati gosu), `no-new-privileges` aman untuk diaktifkan.
### Mengapa `read_only` Tidak Diatur {#why-read-only-is-not-set}
`read_only: true` tidak diatur karena remapping PUID/PGID menulis ke `/etc/passwd` dan `/etc/group` saat startup. Jika Anda menggunakan flag `--user` Docker atau `runAsUser` Kubernetes alih-alih PUID/PGID, Anda dapat mengaktifkan filesystem root read-only dengan aman.
`read_only: true` tidak disetel karena pemetaan ulang PUID/PGID menulis ke `/etc/passwd` dan `/etc/group` saat startup. Jika Anda menggunakan flag `--user` Docker atau Kubernetes `runAsUser` dan bukan PUID/PGID, Anda dapat dengan aman mengaktifkan sistem file root read-only.
## Isolasi Jaringan {#network-isolation}
Selama operasi normal, kontainer membuat **nol koneksi jaringan keluar**. Semua pemrosesan file terjadi secara lokal menggunakan pustaka yang disertakan.
Pemrosesan file bersifat lokal, namun instalasi default **bukan sistem bebas jalan keluar**. Analisis produk anonim menggunakan PostHog dan pelaporan kerusakan menggunakan Sentry saat telemetri diaktifkan. Setel `SNAPOTTER_TELEMETRY=0` (atau nonaktifkan analitik pada Pengaturan > Sistem > Privasi) untuk mematikan keduanya. SnapOtter tidak pernah menyertakan file yang diunggah, nama file, output OCR, teks dokumen, atau konten file lainnya dalam acara tersebut.
```
Browser --> Reverse Proxy (TLS) --> SnapOtter container --> (nothing)
```
Satu-satunya pengecualian adalah **unduhan model AI**: ketika pengguna memasang bundle fitur AI melalui UI, kontainer mengunduh arsip bundle yang telah dibangun sebelumnya dari Hugging Face, ditambah beberapa file model individual dari GitHub Releases, Google Storage, dan PyPI. Unduhan ini terjadi sekali per bundle dan disimpan di volume `/data`.
Lalu lintas keluar lainnya didorong oleh fitur: unduhan instalasi bundel/model AI, input rilis yang ditandatangani; Impor URL mengambil URL publik yang diminta pengguna; dan OIDC, SAML, OpenTelemetry, webhook, penyimpanan yang kompatibel dengan S3, atau integrasi serupa yang dikonfigurasi secara eksplisit, hubungi tujuan yang dipilih oleh administrator. Pengunduhan model saat runtime dinonaktifkan secara default. Tetapkan `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1` hanya untuk mengaktifkan pengunduhan fallback otomatis secara eksplisit. [Impor paket offline](/id/guide/deployment) dapat menyediakan fitur AI tanpa keluarnya model runtime.
**Rekomendasi firewall:**
| Skenario | Aturan keluar |
|Skenario|Aturan keluar|
|---|---|
| Air-gapped (tanpa AI) | Blokir semua lalu lintas keluar dari kontainer |
| Bundle AI diperlukan | Izinkan HTTPS ke `huggingface.co`, `*.xethub.hf.co`, `cdn-lfs.huggingface.co`, `github.com`, `objects.githubusercontent.com`, `storage.googleapis.com`, `pypi.org`, `files.pythonhosted.org` selama instalasi, lalu blokir |
| Setelah instalasi AI | Blokir semua lalu lintas keluar, model di-cache secara lokal |
|Celah udara|Setel `SNAPOTTER_TELEMETRY=0` dan `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0`, gunakan impor bundel AI offline, nonaktifkan impor URL dan integrasi eksternal, lalu blokir jalan keluar|
|Telemetri bawaan|Izinkan titik akhir PostHog dan Sentry dicantumkan oleh log browser/jaringan Anda; nonaktifkan telemetri jika kebijakan tidak mengizinkannya|
|Paket AI diperlukan|Selama instalasi, izinkan HTTPS ke `huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org`; lalu blokir host tersebut|
|Integrasi eksternal|Izinkan hanya tujuan OIDC/SAML/OTLP/webhook/penyimpanan objek yang dikonfigurasikan oleh administrator|
Arsip bundle disajikan dari penyimpanan Xet Hugging Face, yang mentransfer melalui endpoint `*.xethub.hf.co` secara paralel dan itulah yang membuat unduhan bundle multi-GB cepat. Jika firewall Anda mengizinkan `huggingface.co` tetapi memblokir `*.xethub.hf.co`, instalasi tetap berhasil tetapi kembali ke unduhan single-stream yang lebih lambat, jadi masukkan host Xet ke allowlist agar tetap di jalur cepat. Instalasi sepenuhnya offline dapat melewati semua ini dan menggunakan [Impor Bundle Offline](/id/guide/deployment) sebagai gantinya.
Arsip bundel disajikan dari penyimpanan Xet Hugging Face, yang ditransfer melalui titik akhir `*.xethub.hf.co` secara paralel dan membuat pengunduhan bundel multi-GB menjadi cepat. Jika firewall Anda mengizinkan `huggingface.co` tetapi memblokir `*.xethub.hf.co`, penginstalan masih berhasil tetapi kembali ke pengunduhan aliran tunggal yang lebih lambat, jadi izinkan host Xet untuk tetap berada di jalur cepat. Penginstalan yang sepenuhnya offline dapat melewati semua ini dan menggunakan [Impor Paket Offline](/id/guide/deployment) sebagai gantinya.
Untuk konfigurasi reverse proxy (Nginx, Traefik, Caddy, Cloudflare Tunnels), lihat [panduan Deployment](/id/guide/deployment#reverse-proxy).
Untuk konfigurasi proxy terbalik (Nginx, Traefik, Caddy, Cloudflare Tunnels), lihat [Panduan penerapan](/id/guide/deployment#reverse-proxy).
## Docker Secrets {#docker-secrets}
@@ -257,51 +167,58 @@ Untuk penentuan ukuran sumber daya, lihat [Persyaratan Perangkat Keras](/id/guid
## Pencadangan dan Pemulihan {#backup-and-recovery}
State persisten dibagi di dua volume:
Tumpukan Compose produksi mendefinisikan empat volume. Hentikan ingress dan biarkan pekerjaan aktif selesai sebelum mengambil cadangan terkoordinasi sehingga PostgreSQL, Redis, dan status file menggambarkan titik waktu yang sama.
| Volume | Isi | Kritis? |
|Volume|Isi|Perawatan pemulihan|
|---|---|---|
| `SnapOtter-pgdata` | Basis data PostgreSQL (pengguna, pengaturan, pipeline, job, audit log) | Ya |
| `/data` (volume app) | File yang diunggah pengguna, model AI, venv Python | Sebagian (lihat di bawah) |
|`SnapOtter-pgdata`|Pengguna PostgreSQL, pengaturan, saluran pipa, pekerjaan, metadata file, dan log audit|Kritis; gunakan dump logis cepat gagal untuk pemulihan portabel|
|`SnapOtter-data`|Objek perpustakaan yang disimpan, log, dan status AI (`/data/files, /data/logs, /data/ai, /data/ai/venv`)|Cadangkan seluruh volume; untuk menghemat ruang, dengan sengaja hilangkan semua status AI dan instal ulang bundelnya|
|`SnapOtter-redisdata`|Redis AOF untuk status antrean BullMQ yang tahan lama|Cadangkan setelah menjeda aplikasi dan memaksa `SAVE`; diperlukan untuk melanjutkan pekerjaan yang antri dengan tepat|
|`SnapOtter-workspace`|Kunci penyimpanan objek sementara (`/tmp/workspace/uploads, /tmp/workspace/outputs`)|Jangan membuat cadangan setelah semua pekerjaan dihentikan atau dibatalkan; jangan pernah membuangnya saat pekerjaan sedang aktif|
Di dalam volume `/data`:
Biasanya menulis awalan nama volume dengan nama proyek. Selesaikan volume sumber sebenarnya dari kontainer yang terpasang alih-alih berasumsi bahwa nama tampilan seperti `SnapOtter-data` adalah nama volume Docker.
| Path | Isi | Kritis? |
|---|---|---|
| `/data/uploads/`, `/data/outputs/` | File pengguna dan hasil pemrosesan | Ya |
| `/data/ai/` | File model AI yang diunduh | Tidak (dapat diunduh ulang) |
| `/data/venv/` | Lingkungan virtual Python | Tidak (dibangun ulang saat start) |
### Cadangan basis data {#database-backup}
### Pencadangan basis data {#database-backup}
Gunakan `pg_dump` untuk mencadangkan basis data selama stack berjalan:
Gunakan format arsip khusus PostgreSQL dan verifikasi arsip sebelum menganggap pencadangan selesai:
```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
```
Atau, hentikan stack dan buat snapshot volume `SnapOtter-pgdata`:
Uji setiap cadangan dengan mengembalikannya ke tumpukan terisolasi, memeriksa catatan database dan checksum file, dan memulai aplikasi. `tests/qa/backup-restore-drill.sh` repositori mengotomatiskan gerbang rilis tersebut terhadap `QA_IMAGE` eksplisit.
Jika platform Anda mengambil snapshot volume yang konsisten dengan error, hentikan seluruh tumpukan terlebih dahulu dan ambil snapshot semua volume penting sebagai satu set. Salinan direktori data PostgreSQL mentah dari kontainer yang berjalan bukan merupakan cadangan logis yang didukung.
### File dan antrian cadangan {#file-and-queue-backup}
Jeda aplikasi sebelum mengambil file dan volume antrian. Gunakan `docker inspect` untuk menyelesaikan nama volume sebenarnya, memaksa Redis untuk mempertahankan kondisinya saat ini, dan mengarsipkan dengan kepemilikan dan izin yang dipertahankan:
```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
```
### Pencadangan file pengguna {#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 .
```
Model AI berjumlah hingga sekitar 24 GB di semua bundle. Karena dapat diunduh ulang, kecualikan `/data/ai/` dan `/data/venv/` dari pencadangan untuk menghemat ruang. Hanya basis data dan file pengguna yang kritis.
Mulai ulang Redis sebelum aplikasi. Jika Anda sengaja mengecualikan `/data/ai`, hapus seluruh subpohon AI daripada mempertahankan data `installed.json` tanpa model atau lingkungan virtualnya. Jaga agar file cadangan tetap terenkripsi, dikontrol aksesnya, dan terpisah dari host yang menjalankan SnapOtter.
## Artefak Kepatuhan {#compliance-artifacts}
@@ -309,31 +226,42 @@ Setiap rilis SnapOtter menyertakan artefak keamanan berikut:
| Artefak | Format | Di mana menemukannya |
|---|---|---|
| SBOM (CycloneDX) | JSON | Aset [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.cdx.json` |
| SBOM (SPDX) | JSON | Aset [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.spdx.json` |
| Pemindaian kerentanan | Trivy JSON | Aset [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-trivy.json` |
| Pemindaian kerentanan | SARIF | Tab [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security) |
| Lepaskan pengikatan subjek | Pengesahan kanonik JSON + GitHub | [Rilis GitHub](https://github.com/snapotter-hq/SnapOtter/releases) aset: `snapotter-v{version}-release-subjects.json` |
| Arsip SBOM | CycloneDX dan SPDX JSON | Rilis aset: `snapotter-v{version}-archive-linux-{arch}-sbom.{cdx,spdx}.json` |
| Gambar SBOM | CycloneDX dan SPDX JSON | Rilis aset: `snapotter-v{version}-image-linux-{arch}-sbom.{cdx,spdx}.json` |
| Pemindaian kerentanan | Trivy JSON | Rilis aset dengan awalan `archive-linux-{arch}` atau `image-linux-{arch}` yang cocok |
| Pemindaian kerentanan | SARIF | Tab [GitHub Keamanan](https://github.com/snapotter-hq/SnapOtter/security). |
| Analisis statis | CodeQL (JS/TS + Python) | Tab [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security), berjalan mingguan + per PR |
| Tinjauan dependensi | GitHub native | Pemeriksaan per-PR, gagal pada penambahan severity tinggi |
| Audit dependensi Python | pip-audit | Log run CI pada setiap push |
| Tinjauan ketergantungan | GitHub asli | Pemeriksaan per-PR, gagal pada penambahan dengan tingkat keparahan tinggi |
| Audit ketergantungan Python | pip-audit | CI menjalankan log pada setiap dorongan |
| Kebijakan keamanan | Markdown | [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) di repositori |
| Pembaruan dependensi | Dependabot | PR mingguan otomatis untuk npm, pip, Docker, Actions |
| Pembaruan ketergantungan | Dependabot | PR mingguan otomatis untuk npm, pip, Docker, Actions |
**Menjalankan pemindaian Anda sendiri:**
Unduh SBOM dari rilis dan pindai dengan perkakas pilihan Anda:
Unduh manifes subjek rilis dan verifikasi bahwa manifes tersebut dibuktikan oleh alur kerja rilis:
```bash
gh attestation verify snapotter-v2.1.0-release-subjects.json \
--repo snapotter-hq/SnapOtter \
--signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml
```
Manifes mencatat `releaseTag`, `releaseCommit`, dan `workflowTriggerCommit` secara terpisah. Verifikasi bahwa `releaseCommit` adalah komit yang dikupas dari tag yang tidak dapat diubah, lalu verifikasi intisari SHA-256 dari arsip, gambar, SBOM, atau pindaian yang Anda gunakan terhadap entri di `subjects`. Perbedaan ini disengaja: memeriksa komit rilis yang baru dibuat tidak mengubah identitas komit dalam kredensial OIDC alur kerja.
Anda juga dapat memindai SBOM atau gambar yang diunduh secara langsung:
```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 dan pemindaian kerentanan mencerminkan image persis yang dipublikasikan untuk rilis itu. Bundle model AI yang dipasang setelah deployment tidak disertakan dalam SBOM karena diunduh saat runtime.
::: info
Gambar SBOMs dan pindaian mencerminkan gambar spesifik arsitektur yang dipublikasikan untuk rilis tersebut. Arsip SBOMs dan pindaian menjelaskan arsip bawaan secara terpisah. Bundel model AI yang diinstal setelah penerapan tidak disertakan dalam SBOMs ini karena diunduh saat runtime.
:::
+6 -2
View File
@@ -11,7 +11,7 @@ SnapOtter memproses file di lima modalitas: image, video, audio, PDF, dan files.
## Format Gambar {#image-formats}
SnapOtter mendukung 55+ format gambar untuk masukan dan 13 format untuk keluaran.
SnapOtter mendukung 55+ format gambar untuk masukan dan 17 format untuk keluaran.
## Format Masukan {#input-formats}
@@ -104,7 +104,7 @@ SnapOtter mendukung 55+ format gambar untuk masukan dan 13 format untuk keluaran
| PAM | .pam | Sharp (native) | Peta arbitrer |
| PFM | .pfm | Sharp (native) | Peta float |
## Format Keluaran (13) {#output-formats-13}
## Format Keluaran (17) {#output-formats-13}
| Format | Encoder | Kontrol Kualitas | Tersedia Di |
|--------|---------|----------------|-------------|
@@ -121,6 +121,10 @@ SnapOtter mendukung 55+ format gambar untuk masukan dan 13 format untuk keluaran
| ICO | ImageMagick CLI | Lossless | Alat convert |
| JP2 | opj_compress CLI | Rasio kompresi | Alat convert |
| QOI | Codec inline | Lossless | Alat convert |
| PSD | ImageMagick CLI | Lossless | Alat convert |
| PPM | ImageMagick CLI | Lossless | Alat convert |
| EPS | ImageMagick CLI | Lossless | Alat convert |
| TGA | ImageMagick CLI | Lossless | Alat convert |
## Format Video {#video-formats}
+13 -10
View File
@@ -1,8 +1,9 @@
---
description: "Kelola pengguna, peran bawaan dan kustom, izin, kunci API, tim, sesi, dan log audit di SnapOtter."
i18n_source_hash: 5e28af686c96
i18n_source_hash: bea8955f3aff
i18n_provenance: human
i18n_output_hash: 1ffa8126804f
i18n_output_hash: a3c1ebf9d9e0
i18n_hash_version: 2
---
# Pengguna, Peran & Izin {#users-roles-permissions}
@@ -82,12 +83,12 @@ Semua 17 izin. Kontrol penuh atas instans.
| `pipelines:all` | Melihat dan mengelola pipeline semua pengguna |
| `settings:read` | Melihat pengaturan instans |
| `settings:write` | Mengubah pengaturan instans |
| `users:manage` | Membuat, memperbarui, dan menghapus akun pengguna |
| `users:manage` | Membuat dan mengelola akun pengguna dalam batas otoritas aktor |
| `teams:manage` | Membuat, memperbarui, dan menghapus tim |
| `features:manage` | Menginstal dan mengelola bundel fitur AI |
| `system:health` | Mengakses endpoint health dan readiness |
| `audit:read` | Melihat log audit dan mendaftar peran |
| `compliance:manage` | Mengelola siklus hidup GDPR dan fitur kepatuhan |
| `compliance:manage` | Kelola siklus hidup GDPR dan fitur kepatuhan; operasi pengguna yang merusak tetap dibatasi oleh otoritas |
| `webhooks:manage` | Mengonfigurasi webhook keluar |
| `security:manage` | Mengelola pengaturan keamanan (daftar izin IP, penegakan SSO) |
@@ -110,15 +111,17 @@ curl -X POST http://localhost:1349/api/v1/roles \
Nama peran harus 2-30 karakter, alfanumerik huruf kecil dengan tanda hubung dan garis bawah.
### Izin yang dicadangkan untuk admin {#admin-reserved-permissions}
### Batasan administrasi yang didelegasikan {#delegated-administration-boundaries}
Tiga izin dicadangkan untuk peran bawaan dan tidak dapat diberikan ke peran kustom:
Ke-17 izin tersebut dapat didelegasikan melalui peran khusus, namun izin administratif tidak membuat peran tersebut setara dengan peran `admin` bawaan. Mutasi pengguna diotorisasi oleh `users:manage`, operasi destruktif diotorisasi oleh `compliance:manage`, dan manajemen peran khusus diotorisasi oleh `security:manage` dibatasi oleh otoritas aktor saat ini:
- `compliance:manage`
- `webhooks:manage`
- `security:manage`
- Peran bawaan mengikuti `admin` > `editor` > `user`; peran khusus berada di bawah peran bawaan.
- Izin target harus terkandung dalam izin **efektif** aktor. Oleh karena itu, kunci API yang tercakup tidak dapat menggunakan izin yang dihilangkan dari cakupannya.
- Akses alat peran target harus ditampung oleh akses alat milik aktor itu sendiri.
- Akun yang dinonaktifkan diperiksa berdasarkan peran aslinya ketika peran tersebut dicatat sebagai `disabled:<original-role>`.
- Menghapus peran khusus juga memerlukan otoritas untuk menetapkan fallback `user` bawaan; anggota yang dinonaktifkan tetap dinonaktifkan sebagai `disabled:user`.
API peran menolak permintaan apa pun yang menyertakan izin-izin ini. Hanya peran `admin` bawaan yang memiliki akses ke izin-izin tersebut.
Kredensial dan konfigurasi global lebih ketat: menerbitkan atau mencabut token SCIM dan mengimpor konfigurasi instans memerlukan peran `admin` bawaan dengan otoritas admin lengkap yang efektif.
### Izin tingkat alat {#tool-level-permissions}