M1chlCZ/nsfw_sherlock
M1chlCZ/nsfw_sherlock has 31 GitHub stars and 8 forks. Star changes are not measured yet. Last checked 2026-11-12 20:06 UTC. Last push 2026-11-10.
Free, self-hosted image moderation API in Go. Check NSFW images on your own server with HTTP, gRPC, Docker, and optional OCR. No subscription or per-image API fees.
Crecimiento de estrellas medido
- in 24h
- Recopilando historial
- in 7 days
- Recopilando historial
- in 15 days
- Recopilando historial
- in 1 month
- Recopilando historial
Cambios netos en períodos que se solapan, no totales diarios. Los datos no disponibles se dejan en blanco. Las estrellas indican interés, no calidad.
README.md
Original
NSFW Sherlock
Free, self-hosted image moderation.
Run it on your server. Keep image analysis local.
NSFW Sherlock is a free, open-source API that checks images for NSFW content on your own hardware. It has no subscription or per-image API fees. You cover your own hardware and hosting costs.
- Self-hosted: Image analysis runs on your server, without an external moderation API.
- Easy to connect: Use HTTP or gRPC from your application.
- Image checks: Classify photos and anime, and detect explicit regions.
- Optional OCR: Use Tesseract to detect words in images.
- Docker: Start with one command on Linux amd64 or arm64.
One Go binary runs the models with ONNX Runtime. The code uses AGPL-3.0. Model licenses apply separately.
Run with Docker
Run the CPU image:
docker run -e APP_ENV=web -p 4000:4000 m1chl/nsfw-sherlock
Make sure that the service responds:
curl http://localhost:4000/ping
The image includes the models and OCR support. It supports Linux amd64 and arm64. Docker Hub and GHCR publish the same images.
For gRPC, set APP_ENV=grpc. The default interface is HTTP.
Run a release binary
The releases page has archives for Linux amd64, Linux arm64, and macOS arm64, with checksums. The binaries do not include OCR support.
Clone the repository and unpack the archive into it. Then download the models and the runtime:
scripts/fetch-models.sh --ort-only
scripts/fetch-models.sh
./nsfw-sherlock
API
All image endpoints accept this JSON request:
{"base64":"<base64-encoded image>","filename":"image.jpg"}
Set sensitivity between 0.1 and 10 to change the decision thresholds. Higher values flag more images. The default is 1. The gRPC requests have the same field.
With API_KEYS, every image endpoint requires the key in the X-API-Key header. /ping and /metrics stay open.
| HTTP endpoint | Response |
|---|---|
GET /ping |
Service status |
GET /metrics |
Prometheus metrics |
POST /pic/check |
nsfwPic and nsfwText flags |
POST /pic/labels |
Probabilities for drawings, hentai, neutral, porn, and sexy |
POST /pic/analyze |
Full analysis, model scores, region detections, and elapsed time |
POST /pic/batch |
One /pic/check result per image, up to BATCH_MAX_IMAGES |
POST /pic/upload |
Raw image bytes as the request body; the same body as /pic/check |
POST /video/check |
Multipart video upload; one analysis per sampled frame and a summary |
GET /ready |
Readiness probe; reports whether video scanning is available |
POST /jobs |
Start a background batch or video analysis |
GET /jobs |
Kept background jobs, newest first, with state counts |
GET /jobs/{id} |
Job state and result |
GET /jobs/{id}/events |
Server-sent events for a job's state changes |
DELETE /jobs/{id} |
Cancel a queued job or a running analysis |
Example response from /pic/check:
{"status":"ok","message":"success","nsfwText":false,"nsfwPic":false}
The full analysis includes a verdict (sfw, suggestive, or explicit) and an nsfw score.
Errors use {"errorMessage":"...","status":"FAIL","hasError":true}.
The batch endpoint accepts {"images":[<the request above>, ...]} and returns {"status":"ok","message":"success","results":[...]}.
Each result is a /pic/check body or the error body.
The default limit is 4 images.
Send multipart form data instead with one file field per image and an optional sensitivity field for the whole batch:
curl -fsS -F "[email protected]" -F "[email protected]" -F "sensitivity=1.5" \
http://localhost:4000/pic/batch
When CACHE_ENTRIES is greater than 0, the service stores responses for identical image bytes and returns them again without running the models.
Video scanning
POST /video/check accepts a multipart form with the video in the file
field. The service samples frames with ffmpeg (VIDEO_SAMPLE_FPS, default 1
frame per second, at most VIDEO_MAX_FRAMES, default 24), analyzes each frame,
and returns the worst verdict:
curl -fsS -F "[email protected]" http://localhost:4000/video/check
The response holds summary (verdict, maximum NSFW score, frame counts and
elapsed time) and frames, with one analysis per sampled frame. Install
ffmpeg on the host, or use the Docker image, which includes it. Without ffmpeg
the endpoint answers 503. Uploads are limited to VIDEO_MAX_BYTES and the
whole scan stops after 120 seconds.
Every response carries an X-Request-ID header. JSON logs repeat it as requestId.
With RATE_LIMIT_RPM set, limited responses also carry X-RateLimit-Limit,
X-RateLimit-Remaining, and X-RateLimit-Reset (Unix seconds); a 429 adds
Retry-After.
Async jobs
Large batches and videos can run in the background. POST /jobs accepts the
same JSON body as /pic/batch, or a multipart video in the file field:
curl -fsS -X POST http://localhost:4000/jobs \
-H "Content-Type: application/json" -d '{"images":[...]}'
curl -fsS -F "[email protected]" http://localhost:4000/jobs
The service answers 202 with a job id. Poll GET /jobs/{id} for the state
(queued, running, done, failed or canceled) and the result.
DELETE /jobs/{id} cancels a queued job or a running analysis.
Job callbacks
Add callbackUrl to a batch job, or a callbackUrl form field to a video
job, to receive the result without polling:
curl -fsS -X POST http://localhost:4000/jobs \
-H "Content-Type: application/json" \
-d '{"images":[...],"callbackUrl":"https://example.com/hooks/nsfw"}'
curl -fsS -F "[email protected]" -F "callbackUrl=https://example.com/hooks/nsfw" \
http://localhost:4000/jobs
When the job reaches done or failed, the service posts this JSON body to
the callback URL:
{"event":"job.finished","job":{...the same shape as GET /jobs/{id}...}}
The request carries X-NSFW-Event, X-NSFW-Job-ID, and, when
WEBHOOK_SECRET is set, X-NSFW-Timestamp and X-NSFW-Signature. The
signature is sha256= followed by the hex HMAC-SHA256 of
<timestamp>.<body> with the secret as the key.
Callbacks retry on network errors and non-2xx answers. WEBHOOK_RETRIES sets
the number of retries after the first attempt, and WEBHOOK_TIMEOUT_SECONDS
bounds each attempt. Redirects are not followed. Private, loopback, and
link-local addresses are rejected unless WEBHOOK_ALLOW_PRIVATE=true.
GET /jobs lists the kept jobs newest first. Pass state to filter
(queued, running, done, failed or canceled) and limit to cap the
result; the default is 100. The counts field always covers every kept job.
GET /jobs/{id}/events streams the state changes with server-sent events:
curl -N http://localhost:4000/jobs/<id>/events
Each event is named after the state and carries the same body as
GET /jobs/{id}. The stream ends when the job finishes. A keepalive comment
every 15 seconds keeps the connection open.
JOB_WORKERS sets how many jobs run at once, JOB_MAX_ENTRIES caps the kept
jobs, and JOB_TTL_SECONDS sets how long a finished job stays. The defaults
are 2 workers, 64 jobs, and 600 seconds.
The nsfw_request_duration_seconds histogram carries an endpoint label.
The /metrics output also includes nsfw_jobs_submitted_total,
nsfw_jobs_rejected_total, nsfw_jobs_current, nsfw_jobs_finished_total,
and nsfw_job_duration_seconds. Callback delivery counters are
nsfw_webhook_deliveries_total and nsfw_webhook_failures_total.
GET /ready reports whether video scanning is available.
The gRPC service exposes Detect, DetectLabels, and Analyze.
It also serves the standard gRPC health service and server reflection.
The service definition contains the request and response types.
GPU support
NVIDIA · CUDA
Install an NVIDIA driver compatible with CUDA 12.9 and the NVIDIA Container Toolkit. Then run the CUDA image:
docker run --gpus all -p 4000:4000 m1chl/nsfw-sherlock:v1.4.0-cuda
This image supports Linux amd64. It includes ONNX Runtime 1.29.0, CUDA 12, and cuDNN.
ORT_DEVICE_ID selects the GPU index, with 0 as the default.
AMD · ROCm / MIGraphX
Install ROCm and a MIGraphX-enabled ONNX Runtime 1.29.0 shared library.
Set ORT_PROVIDER=rocm.
Point ORT_LIB to that library.
Keep the provider libraries beside it.
Add the ROCm dependencies to the library search path.
The rocm value is an alias for migraphx. The Go binding requires ORT API 29.
Older runtimes are incompatible. This project does not publish an AMD image.
The MIGraphX guide describes the runtime build.
NVIDIA · TensorRT
Use an ONNX Runtime shared library built with the TensorRT execution provider.
Then set ORT_PROVIDER=tensorrt.
TensorRT compiles each model for your GPU at the first run. This step is slow.
Intel · OpenVINO
Use an ONNX Runtime shared library built with the OpenVINO execution provider.
Then set ORT_PROVIDER=openvino.
OpenVINO runs on an Intel CPU or GPU.
If the selected provider is unavailable, the service stops at startup. Unsupported operators can still use the CPU. The v1.0.5 Linux amd64 binary passed a physical CUDA test on an RTX 5070 Ti under WSL2, with ONNX Runtime 1.29.0, CUDA 13 and cuDNN 9.24. See the test setup, results and reproduction client. This test does not cover the CUDA 12 Docker image, AMD/MIGraphX, TensorRT or OpenVINO.
Configuration
| Variable | Default | Purpose |
|---|---|---|
APP_ENV |
web |
HTTP (web) or gRPC (grpc). http is a deprecated alias. |
PORT |
4000 |
Service port |
NSFW_PROFILE |
balanced |
Model profile |
ORT_PROVIDER |
cpu |
cpu, cuda, migraphx, tensorrt, or openvino. rocm aliases migraphx. |
ORT_DEVICE_ID |
0 |
GPU index |
OCR_ENABLED |
true |
Text detection. Requires a build with OCR support. |
API_KEYS |
(none) | Comma-separated keys for the X-API-Key header. Empty disables the check. |
RATE_LIMIT_RPM |
0 |
Requests per minute for each key or IP. 0 disables the limit. |
Paths, limits, and logging
| Variable | Default | Purpose |
|---|---|---|
MODELS_DIR |
./assets/models |
Model directory |
MANIFEST |
models/manifest.json |
Model manifest |
ORT_LIB |
Auto-detected | ONNX Runtime shared library path |
BAD_WORDS_FILE |
Auto-detected | OCR word list. Uses an embedded list as a fallback. |
MAX_IMAGE_BYTES |
20971520 (20 MiB) |
Maximum image size |
MAX_IMAGE_PIXELS |
50000000 |
Maximum decoded pixel count |
SESSION_POOL_SIZE |
2 |
Sessions per model |
BATCH_MAX_IMAGES |
4 |
Images per /pic/batch request (1..64). Raises the request body limit. |
CACHE_ENTRIES |
0 |
Stored responses for identical image bytes. 0 disables the cache. |
JOB_WORKERS |
2 |
Background jobs that run at once (1..32) |
JOB_MAX_ENTRIES |
64 |
Kept background jobs (1..10000) |
JOB_TTL_SECONDS |
600 |
Seconds a finished job stays available (1..86400) |
WEBHOOK_SECRET |
(none) | HMAC-SHA256 key for callback signatures. Empty disables signing. |
WEBHOOK_TIMEOUT_SECONDS |
10 |
Seconds per callback attempt (1..60) |
WEBHOOK_RETRIES |
3 |
Retries after a failed callback attempt (0..10) |
WEBHOOK_ALLOW_PRIVATE |
false |
Allow callback URLs on private and loopback addresses |
FFMPEG_PATH |
ffmpeg |
ffmpeg binary used by /video/check |
VIDEO_SAMPLE_FPS |
1 |
Frames sampled per second (0.1..30) |
VIDEO_MAX_FRAMES |
24 |
Frame cap per video (1..240) |
VIDEO_MAX_BYTES |
67108864 (64 MiB) |
Maximum video upload |
NSFW_ALLOW_DEGRADED |
false |
Allow startup after a required model fails to load. |
LOG_LEVEL |
info |
debug, info, warn, or error |
LOG_FORMAT |
text |
text or json |
Docker images supply the model and runtime paths. They use JSON logs.
Model profiles
| Profile | Photo model | Anime model | Region detector |
|---|---|---|---|
balanced (default) |
Freepik EVA02 | CaFormer | NudeNet 320px |
max |
Freepik EVA02 | CaFormer | NudeNet 640px |
fast |
Freepik EVA02 | MobileNetV3 | NudeNet 320px |
compat |
ViT5 with five legacy labels | None | None |
Build from source
Requirements: Go 1.27, a C compiler, Python 3, and curl. The runtime download supports Linux amd64, Linux arm64, and macOS arm64.
Download the source, runtime, and models. Then build the binary. Run the binary:
git clone https://github.com/M1chlCZ/nsfw_sherlock
cd nsfw_sherlock
scripts/fetch-models.sh --ort-only
scripts/fetch-models.sh
go build .
./nsfw_sherlock
The download script uses pinned SHA-256 digests for the runtime and models.
The engine also compares each model digest with the manifest at startup.
This source build excludes OCR and always reports nsfwText: false.
For OCR support, install Tesseract, Leptonica, their development libraries, and the English language data. Then build with the OCR tag:
go build -tags ocr .
OCR_ENABLED=false also disables text detection.
Development
Run the tests and lint checks:
go test ./... -count=1 -race
go test -tags ocr ./... -count=1 -race
golangci-lint run
golangci-lint run --build-tags ocr
PARITY_REQUIRE=1 go test ./engine -run TestParity
The OCR tests require Tesseract. The parity tests require the models and ONNX Runtime.
CI also runs go vet, govulncheck, and a Docker smoke test.
Release jobs require successful CI checks before they publish images.
The lint configuration adapts the Golden config.
Model tools: manifest · download · export · parity fixtures · benchmark.
Stub generation uses make proto.
Models and licenses
The code uses AGPL-3.0. Model licenses apply separately.
| Model | License |
|---|---|
| Freepik photo classifier | MIT |
| deepghs anime classifiers | MIT |
| NudeNet region detectors | AGPL-3.0 |
| ViT5 compatibility classifier | CC-BY-NC-ND-4.0 |
Review the model licenses before commercial use.
The compat model has non-commercial and no-derivatives restrictions.
Review its license before you export or redistribute it.
Runtime: onnxruntime_go. OCR: gosseract and Tesseract.
Support
If this project helps you, you can support its development.
If this project helps you, consider giving it a star. Bug reports and contributions are welcome.
Explorar proyectos relacionados
tensorflow/tensorflow
Un framework de código abierto de aprendizaje automático para todos
201,584 estrellashuggingface/transformers
🤗 Transformers: el framework de definición de modelos para modelos de aprendizaje automático de vanguardia en texto, visión, audio y multimodal, tanto para inferencia como para entrenamiento.
168,917 estrellasAUTOMATIC1111/stable-diffusion-webui
Interfaz web de Stable Diffusion
165,300 estrellasComfy-Org/ComfyUI
La GUI, la API y el backend más potentes y modulares para modelos de difusión, con una interfaz de grafos y nodos. El motor de inferencia local más rápido del mundo.
146,472 estrellas