> ## Documentation Index
> Fetch the complete documentation index at: /docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Strict-safe mode and the FFmpeg fallback

> Run no FFmpeg code on files you did not make, or let coreav ffprobe and coreav ffmpeg hand the command lines CoreAV does not implement to the real FFmpeg.

`coreav ffprobe` and `coreav ffmpeg` take FFmpeg's command lines. CoreAV implements a subset of them. A command line outside that subset either goes to the real `ffprobe` or `ffmpeg` (the fallback), or is refused (strict-safe mode).

The fallback is on by default, so a script that calls FFmpeg keeps working. If your service reads files it did not make, such as uploads, turn on strict-safe mode. It is the only mode in which no FFmpeg code runs, in every build: CoreAV then never runs an `ffmpeg` or `ffprobe` program, not in place of a command line, not as a decoder and not as an encoder ([what strict-safe mode covers](#what-strict-safe-mode-covers)).

> [!NOTE]
>
> The other commands (`probe`, `check`, `clip`, `split`, `join`, `remux`, `sanitize`, `extract-audio`, `extract-subtitles`, `chapters`, `decode-audio`, `hls`) never run FFmpeg, in either mode.

## What strict-safe mode covers

In strict-safe mode CoreAV never runs an `ffmpeg` or `ffprobe` program, for any command and in any build:

| Where an `ffmpeg` program would run | What strict-safe mode does |
| - | - |
| In place of a command line that `coreav ffprobe` or `coreav ffmpeg` does not implement (the [fallback](#the-ffmpeg-fallback)) | Refuses the command line, with exit status 4. |
| As the decoder of `coreav thumbnail`, `coreav transcode` and the `libx264` command lines of `coreav ffmpeg`, in a build with the `ffmpeg-fallback` backend ([decoding through FFmpeg](#decoding-through-ffmpeg-in-builds-with-a-decoder-backend)) | Decodes with the build's other backends (`native-h264`, `dav1d`, `libvpx`). A video that only FFmpeg would decode is refused, with exit status 4. |
| As the encoder of `coreav transcode` and the `libx264` command lines of `coreav ffmpeg` | Encodes with an `x264` program only. `--encoder ffmpeg` is refused, and so is `--encoder auto` (the default) when no `x264` program is found (`COREAV_X264`, `--x264`, or `x264` on `PATH`), both with exit status 4 and before the input is read. |
| In CoreAV's internal decode worker and encoder launcher, which CoreAV starts in strict-safe mode when it is on, or which are run directly with `COREAV_STRICT_SAFE` set | The worker refuses the FFmpeg backend, and the launcher refuses a program whose file name, or the file it links to, begins with `ffmpeg` or `ffprobe`. An `x264` program named that way is refused before the input is read. |

A default build, which the release binaries planned for 0.1.0 are, has no decoder backend, so only the first row applies to it. The `dav1d` and `libvpx` backends are C libraries of their own, not FFmpeg, and run in the sandboxed worker in strict-safe mode too.

To transcode in strict-safe mode, install an `x264` program and use a build whose backends decode your sources: `native-h264` for H.264, `dav1d` for AV1, `libvpx` for VP9 ([build features](/docs/build-features.md)).

## Turn on strict-safe mode

Set the environment variable or pass the flag:

```sh theme={null}
export COREAV_STRICT_SAFE=1
coreav ffprobe -v error -show_format input.mp4

coreav --strict-safe ffprobe -v error -show_format input.mp4
```

* Any value of `COREAV_STRICT_SAFE` other than empty or `0` turns the mode on, so a mistyped value fails closed.
* Under a link named `ffprobe` or `ffmpeg` that points to `coreav`, every argument belongs to the tool. Only the variable works there.

## What a refusal looks like

In strict-safe mode, a command line that CoreAV does not implement is refused by name, with exit status 4:

```console theme={null}
$ coreav --strict-safe ffprobe -v error -show_frames input.mp4
coreav ffprobe: option '-show_frames' is not supported: frames need a decoder
$ echo $?
4
```

A refusal can come after the input is opened, for example for a source with chapters, an output file that already exists, or a value such as `-crf 90`. No refusal writes or changes the output. A program that gets exit status 4 can send the job somewhere else, or reject it.

`coreav thumbnail` and `coreav transcode` refuse with exit status 4 too, and print a `coreav.error/1` object whose message says what to change. Here, in a build whose only decoder backend is `ffmpeg-fallback`:

```console theme={null}
$ coreav --strict-safe thumbnail input.mp4 -o thumb.jpg
{"kind":"unsupported_feature","limit":null,"message":"strict-safe mode never runs the ffmpeg program, so it cannot decode the h264 video, and no other decoder of this build does: build coreav with the native-h264 feature, or run without --strict-safe and COREAV_STRICT_SAFE (no decoder for h264: nvdec: not available; native: not available; dav1d: not available; libvpx: not available; ffmpeg: unsupported: not run in strict-safe mode)","offset":null,"schema":"coreav.error/1"}
$ echo $?
4
```

A refused encoder (`--encoder ffmpeg`, or no `x264` program) is refused before the input is read. A refused decoder is refused once CoreAV has read the container and before any frame is decoded. Neither leaves an output file.

## The FFmpeg fallback

Without strict-safe mode, a command line that CoreAV does not implement runs the real program instead. That program is FFmpeg's C code, and CoreAV's limits and checks do not apply to it. CoreAV says so in one line on standard error, at every log level, `-v quiet` included:

```console theme={null}
$ coreav ffprobe -v error -show_frames -of csv input.mp4
coreav: ran /usr/bin/ffprobe instead (not native: -show_frames)
frame,video,0,1,0,0.000000,...
```

Standard output is the program's, byte for byte, and so is the exit status. On Unix CoreAV replaces itself with the program, so its process id and signals are FFmpeg's too.

### Which program runs

1. If `COREAV_FALLBACK` is set, it names the program. A directory names its `ffmpeg` and `ffprobe` (with `.exe` on Windows). A file names that one program. If the path does not exist, cannot run, or names the other tool, nothing runs: the refusal stands, with exit status 4 and a line that names the variable and the reason.
2. Otherwise CoreAV runs the first `ffmpeg` or `ffprobe` on `PATH` that it can run and that is not `coreav` itself.
3. If there is none, the refusal stands, with exit status 4.

### What is never handed on

Only a refusal goes to FFmpeg. These keep their own exit status and never reach FFmpeg:

| Exit status | Meaning |
| - | - |
| 1 | CoreAV rejected the file. Handing it on would send FFmpeg exactly the files the checks stopped. |
| 2 | The command line is malformed. |
| 3 | An input cannot be read, or an output cannot be written. |

### Log the commands that were not native

Set `COREAV_FALLBACK_LOG` to a file path, and CoreAV appends one JSON line for each command line that was not native, whether FFmpeg ran it or CoreAV refused it:

```json theme={null}
{"args":["-v","error","-show_frames","-of","csv","INPUT"],"coreav":"0.0.1","ran":"/usr/bin/ffprobe","reason":"-show_frames","time":"2026-10-30T00:51:20Z","tool":"ffprobe"}
{"args":["-i","INPUT","-vf","VALUE","OUTPUT"],"coreav":"0.0.1","not_run":"strict_safe","ran":null,"reason":"-vf","time":"2026-10-30T00:51:20Z","tool":"ffmpeg"}
```

File names, URLs and option values become placeholders (`INPUT`, `OUTPUT`, `VALUE`), so the log holds nothing private and you can share it. Only option names and values that FFmpeg publicly lists are kept. When nothing ran, `ran` is `null` and `not_run` says why: `strict_safe`, `no_program`, `fallback_unusable` or `recursion_guard`. A log that cannot be written costs one line on standard error and never stops the command.

### The recursion guard

The program that the fallback starts gets `COREAV_FALLBACK_ACTIVE=1` in its environment. A `coreav` started with that variable set never falls back. It refuses with exit status 4 instead, so a wrapper script or a renamed copy of `coreav` on `PATH` cannot cause a loop.

## Decoding through FFmpeg in builds with a decoder backend

Builds with the `ffmpeg-fallback` [build feature](/docs/build-features.md) also use an `ffmpeg` program to decode video for [`coreav thumbnail`](/docs/commands/thumbnail), [`coreav transcode`](/docs/commands/transcode) and the `libx264` command lines of [`coreav ffmpeg`](/docs/commands/ffmpeg). This is a separate path from the command-line fallback, and it is never in a default build. Strict-safe mode turns it off too ([what strict-safe mode covers](#what-strict-safe-mode-covers)). Outside strict-safe mode:

* CoreAV demuxes the file itself and sends FFmpeg only the packets of one video stream, in a Matroska stream that CoreAV writes. FFmpeg's container probing never runs on the untrusted file.
* The decoder is chosen by name from an allowlist, so a file cannot steer FFmpeg to another decoder.
* FFmpeg runs as a child of a separate worker process with resource limits. Its libraries are never linked into CoreAV.

## More detail

* [Fallback to FFmpeg](https://github.com/coreav-dev/coreav/blob/main/docs/design/compat-cli.md#fallback-to-ffmpeg), the design note, covers the rules above in full, including how the log is redacted.
* [The FFmpeg fallback](https://github.com/coreav-dev/coreav/blob/main/docs/design/decode.md#the-ffmpeg-fallback) and [the sandboxed worker](https://github.com/coreav-dev/coreav/blob/main/docs/design/decode.md#the-sandboxed-worker) cover the decode path.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.