Skip to content

About

Compresses uploads to AVIF and builds thumbnails for a Gryt server. Runs as its own process so a malformed image cannot take down the one holding the keys and the sockets.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Gryt logo

Gryt Media Worker

Checks and rewrites uploads for the Gryt voice & video platform.
Pictures, video and emoji are decoded in a jail, written out again, and only then shown to anyone. Images go through Sharp, video through ffmpeg.


Docker

docker pull ghcr.io/gryt-chat/image-worker:latest
docker run -v gryt-data:/data --env-file .env ghcr.io/gryt-chat/image-worker:latest

Browse tags at ghcr.io/gryt-chat/image-worker.

It started out handling only pictures and was called the image worker. The docs and the app call it the media worker now, but the repository, the container image and IMAGE_WORKER_URL kept the old name. Renaming those would break every compose file and server that's already deployed, for nothing anyone would notice.

It parses files strangers uploaded

Compressing an avatar sounds like a utility job. This is the process that hands attacker-controlled bytes to an image decoder, so it's a review-required path in the AI policy and changes here get read line by line.

Video posters

A video's poster is one frame that ffmpeg grabs and sharp turns into a JPEG. The worker opens the file and hands ffmpeg the open descriptor, so ffmpeg never opens a path itself. It may only use the mov/mp4 and matroska/webm demuxers and the h264, hevc, vp8, vp9 and AV1 (libdav1d) decoders. It runs on one thread with a 1 GiB address-space cap, and it's killed after 15 seconds. The flags are in src/videoPoster.ts.

The Docker image builds its own static ffmpeg with nothing else in it: those two demuxers, those decoders, the PNG encoder, and the fd and pipe protocols. The versions and hashes are at the top of the Dockerfile, and a new ffmpeg release means bumping them by hand.

In the image, ffmpeg doesn't run as the worker. The container starts as root just long enough for jail/entrypoint.sh to start ffjail, and then the worker runs as gryt. ffjail runs each decode as a second user, gryt-ff, inside a chroot that holds the ffmpeg binary and nothing else. There's no /proc, /data or /app in there to read. ffmpeg gets an empty environment, no capabilities, the 1 GiB cap, and a seccomp filter that refuses sockets, ptrace and new processes. All it has is the input as fd 3 and a pipe for the PNG. test/image checks that on every pull request, with a probe that tries to read the worker's environment and storage from inside the jail.

If you start the container as a non-root user, the jail can't start, and videos don't get a poster. The image never runs ffmpeg outside the jail.

Without ffmpeg, on a dev machine or your own build, videos just don't get a poster. A system ffmpeg has to be 6.0 or newer, for the fd protocol. On Linux the worker also needs prlimit from util-linux, and it skips posters rather than run ffmpeg without the cap.

Running your own build in production (NODE_ENV=production) without the jail, the worker doesn't touch the system ffmpeg at all, so videos get no poster. A decoder bug in a stranger's file would run as you, with your files in reach. If you accept that, set GRYT_ALLOW_HOST_FFMPEG=1.

Configuration

Variable Default Description
DATA_DIR ./data Path to the shared data directory (contains gryt.db)
S3_BUCKET — S3 bucket name (or subdirectory name for filesystem storage)
STORAGE_BACKEND s3 Storage backend: s3 or filesystem
S3_ENDPOINT — S3-compatible endpoint (e.g. MinIO)
S3_REGION auto S3 region
S3_ACCESS_KEY_ID — S3 access key
S3_SECRET_ACCESS_KEY — S3 secret key
S3_FORCE_PATH_STYLE false Use path-style S3 URLs (required for MinIO)
IMAGE_WORKER_CONCURRENCY 2 Max concurrent image processing jobs (1–8)
IMAGE_WORKER_POLL_MS 1000 Database polling interval in milliseconds (250–10000)
HEALTH_PORT 8080 HTTP health check port
HEALTH_HOST 127.0.0.1 Address the health server binds to. Empty listens on every interface; the Docker image sets 0.0.0.0

Quick start (development)

yarn install
yarn dev

Build

yarn build
yarn start

Documentation

Full docs at docs.gryt.chat/docs/deployment.

Issues

Please report bugs and request features in the main Gryt repository.

Sponsors

What sponsoring pays for, the tiers, and everyone who has sponsored: gryt.chat/sponsors. To sponsor: GitHub Sponsors.

The list itself lives in the Gryt README, in one place rather than ten, so it cannot fall out of step across repositories.

License

AGPL-3.0 — Part of Gryt

About

Compresses uploads to AVIF and builds thumbnails for a Gryt server. Runs as its own process so a malformed image cannot take down the one holding the keys and the sockets.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages