Files
av-club/docs/SHARP.md
T
PastilhasandClaude Opus 5 1597fccd75 The HTTP service: assets, probe, and the full sharp surface
Exposes sharp and ffprobe with their own vocabulary — operations are named what
sharp names them, ffprobe fields come back spelled the way ffprobe spells them —
so the tools' own documentation applies and there is no house dialect to learn.

Safety comes from one architectural invariant rather than from filtering: the
caller never supplies a path, a URL or a protocol. Input is an id we issued, output
is a name we chose. That removes the entire class of attack that makes "just pass
the flags through" a remote shell, which is precisely what makes it safe to open up
everything else. docs/SECURITY.md has the reasoning and the specific attacks.

The vocabulary is a data table, so the validator and docs/SHARP.md are generated
from one source and cannot drift. Two things are deliberately absent from it:
withMetadata, because that is how a client's GPS coordinates get published, and
limitInputPixels, because a caller who could raise it could turn a 1 KB PNG into a
40 GB allocation.

Verified against a running server, not asserted: traversal ids rejected, unknown
operations and typo'd parameters rejected by name, out-of-range values rejected
with the bound, a GPS-tagged photo coming out clean end to end, video probe, Range
requests, and a rejected upload leaving nothing on disk.

Found while building: config threw at import if AV_CLUB_TOKEN was unset, so
generating documentation required a production secret. The token is read at boot in
server.ts now — still a refusal to start, no longer a module-scope landmine.

ffmpeg is designed in docs/API.md and not built yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 02:37:57 +01:00

492 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# sharp — the operation vocabulary
**Generated from `src/sharp/vocabulary.ts` by `scripts/gen-docs.ts`. Do not edit by hand —
run `bun run docs`.**
Every operation is named what sharp names it and takes sharp's own arguments, so
[sharp's documentation](https://sharp.pixelplumbing.com/api-operation) describes what each one
*does*. This page describes what av-club *accepts*: the bounds, which exist because an unbounded
number here is a denial of service rather than a picture.
Requests go to `POST /v1/sharp` — see [API.md](API.md) for the envelope.
```json
{
"input": "as_9f86d081884c7d659a2feaa0c55ad015",
"options": { "failOn": "warning" },
"pipeline": [
{ "rotate": {} },
{ "resize": { "width": 1200, "fit": "inside", "withoutEnlargement": true } }
],
"output": { "format": "avif", "options": { "quality": 55 } }
}
```
## Three things that catch people
**`rotate` with no angle applies the EXIF orientation.** Output is re-encoded and metadata is
dropped, so a photo that looked upright in a phone's gallery arrives sideways on the web unless you
include `{ "rotate": {} }`. This is the most commonly forgotten operation.
**`resize` enlarges by default.** A 300px source asked for 800px comes back 800px and soft. Pass
`withoutEnlargement: true` unless you actually want that.
**Metadata is always stripped and there is no way to keep it.** `withMetadata` is deliberately not
in this vocabulary — it is how a client's GPS coordinates end up published. Dimensions come back in
the response instead.
## Input options
sharp's constructor options, passed as `options`.
| Parameter | Accepts |
|---|---|
| `failOn` | `none` · `truncated` · `error` · `warning`. How strict to be about a damaged file. sharp's default is 'warning'. |
| `density` | number, 1–2400. DPI for vector input. |
| `pages` | integer, -1–1000. Pages to read from a multi-page input. -1 for all. |
| `page` | integer, 0–100000. First page to read. |
| `animated` | boolean. Read all frames of an animated GIF/WebP. Equivalent to pages: -1. |
| `ignoreIcc` | boolean. Ignore any embedded ICC profile. |
| `autoOrient` | boolean. Apply EXIF orientation on load. See the note about rotate() in docs/SHARP.md. |
| `sequentialRead` | boolean |
| `level` | integer, 0–256. Level to extract from a multi-level input. |
| `subifd` | integer, -1–100000. Subldeaf IFD to extract from an OME-TIFF. |
`limitInputPixels` is **not** accepted: it is the decompression-bomb guard and is set by the
server. A 1 KB PNG can expand to tens of gigabytes.
## Operations
32 operations, run in the order given.
### `resize`
Resize to given dimensions.
| Parameter | Accepts |
|---|---|
| `width` | integer, 1–16383 |
| `height` | integer, 1–16383 |
| `fit` | `cover` · `contain` · `fill` · `inside` · `outside`. How the image fits the box. Default 'cover'. |
| `position` | `centre` · `center` · `top` · `right top` · `right` · `right bottom` · `bottom` · `left bottom` · `left` · `left top` · `north` · `northeast` · `east` · `southeast` · `south` · `southwest` · `west` · `northwest` · `entropy` · `attention`. Which part survives when fit is 'cover'. 'attention' picks the busiest region. |
| `background` | colour — a CSS string like `#ffffff`, or `{r, g, b, alpha}`. Fill colour when fit is 'contain'. |
| `kernel` | `nearest` · `linear` · `cubic` · `mitchell` · `lanczos2` · `lanczos3` |
| `withoutEnlargement` | boolean. Never scale UP. Almost always what you want — see docs/SHARP.md. |
| `withoutReduction` | boolean. Never scale DOWN. |
| `fastShrinkOnLoad` | boolean |
### `extend`
Add pixels to the edges.
| Parameter | Accepts |
|---|---|
| `top` | integer, 0–16383 |
| `bottom` | integer, 0–16383 |
| `left` | integer, 0–16383 |
| `right` | integer, 0–16383 |
| `extendWith` | `background` · `copy` · `repeat` · `mirror` |
| `background` | colour — a CSS string like `#ffffff`, or `{r, g, b, alpha}` |
### `extract`
Crop to a region.
| Parameter | Accepts |
|---|---|
| `left` | integer, 0–16383 |
| `top` | integer, 0–16383 |
| `width` | integer, 1–16383 |
| `height` | integer, 1–16383 |
### `trim`
Trim uniform borders.
| Parameter | Accepts |
|---|---|
| `background` | colour — a CSS string like `#ffffff`, or `{r, g, b, alpha}` |
| `threshold` | number, 0–255 |
| `lineArt` | boolean |
### `rotate`
Rotate. With no angle, applies the EXIF orientation — see docs/SHARP.md.
| Parameter | Accepts |
|---|---|
| `angle` | number, -3600–3600. Multiples of 90 are lossless; anything else needs a background. |
| `background` | colour — a CSS string like `#ffffff`, or `{r, g, b, alpha}` |
### `flip`
Mirror vertically.
Takes `flip` directly — `{ "flip": { "flip": … } }`.
| Parameter | Accepts |
|---|---|
| `flip` | boolean |
### `flop`
Mirror horizontally.
Takes `flop` directly — `{ "flop": { "flop": … } }`.
| Parameter | Accepts |
|---|---|
| `flop` | boolean |
### `sharpen`
Sharpen.
| Parameter | Accepts |
|---|---|
| `sigma` | number, 0.000001–10 |
| `m1` | number, 0–1000000 |
| `m2` | number, 0–1000000 |
| `x1` | number, 0–1000000 |
| `y2` | number, 0–1000000 |
| `y3` | number, 0–1000000 |
### `blur`
Gaussian blur.
Takes `sigma` directly — `{ "blur": { "sigma": … } }`.
| Parameter | Accepts |
|---|---|
| `sigma` | number, 0.3–1000 |
### `median`
Median filter — good for noise.
Takes `size` directly — `{ "median": { "size": … } }`.
| Parameter | Accepts |
|---|---|
| `size` | integer, 1–1000 |
### `dilate`
Morphological dilate.
Takes `width` directly — `{ "dilate": { "width": … } }`.
| Parameter | Accepts |
|---|---|
| `width` | integer, 1–1000 |
### `erode`
Morphological erode.
Takes `width` directly — `{ "erode": { "width": … } }`.
| Parameter | Accepts |
|---|---|
| `width` | integer, 1–1000 |
### `flatten`
Composite transparency onto a background.
| Parameter | Accepts |
|---|---|
| `background` | colour — a CSS string like `#ffffff`, or `{r, g, b, alpha}` |
### `unflatten`
Make pure white pixels transparent.
_Takes no options._
### `gamma`
Apply gamma correction.
| Parameter | Accepts |
|---|---|
| `gamma` | number, 1–3 |
| `gammaOut` | number, 1–3 |
### `negate`
Invert.
| Parameter | Accepts |
|---|---|
| `alpha` | boolean. Also invert the alpha channel. |
### `normalise`
Stretch the histogram to full range.
| Parameter | Accepts |
|---|---|
| `lower` | number, 0–99 |
| `upper` | number, 1–100 |
### `clahe`
Contrast-limited adaptive histogram equalisation.
| Parameter | Accepts |
|---|---|
| `width` | integer, 1–16383 |
| `height` | integer, 1–16383 |
| `maxSlope` | integer, 0–100 |
### `convolve`
Apply a convolution kernel.
| Parameter | Accepts |
|---|---|
| `width` | integer, 1–100 |
| `height` | integer, 1–100 |
| `kernel` | array of 1–10000 numbers, each -1000–1000 |
| `scale` | number, -10000–10000 |
| `offset` | number, -10000–10000 |
### `threshold`
Reduce to black and white at a cutoff.
| Parameter | Accepts |
|---|---|
| `threshold` | integer, 0–255 |
| `greyscale` | boolean |
### `linear`
Multiply and add: out = in * a + b.
| Parameter | Accepts |
|---|---|
| `a` | array of 1–4 numbers, each -1000–1000 |
| `b` | array of 1–4 numbers, each -1000–1000 |
### `recomb`
Recombine channels with a 3x3 matrix.
Takes `matrix` directly — `{ "recomb": { "matrix": … } }`.
| Parameter | Accepts |
|---|---|
| `matrix` | array of 9 numbers, each -100–100 |
### `modulate`
Adjust brightness, saturation, hue and lightness.
| Parameter | Accepts |
|---|---|
| `brightness` | number, 0–100 |
| `saturation` | number, 0–100 |
| `hue` | number, -3600–3600. Degrees. |
| `lightness` | number, -100–100 |
### `tint`
Tint with a colour.
Takes `color` directly — `{ "tint": { "color": … } }`.
| Parameter | Accepts |
|---|---|
| `color` | colour — a CSS string like `#ffffff`, or `{r, g, b, alpha}` |
### `greyscale`
Convert to greyscale.
Takes `greyscale` directly — `{ "greyscale": { "greyscale": … } }`.
| Parameter | Accepts |
|---|---|
| `greyscale` | boolean |
### `toColourspace`
Convert to a colourspace.
Takes `colourspace` directly — `{ "toColourspace": { "colourspace": … } }`.
| Parameter | Accepts |
|---|---|
| `colourspace` | `srgb` · `rgb` · `cmyk` · `lab` · `b-w` · `hsv` · `scrgb` · `lch` · `grey16` · `rgb16` |
### `pipelineColourspace`
Set the colourspace operations are performed in.
Takes `colourspace` directly — `{ "pipelineColourspace": { "colourspace": … } }`.
| Parameter | Accepts |
|---|---|
| `colourspace` | `srgb` · `rgb` · `cmyk` · `lab` · `b-w` · `hsv` · `scrgb` · `lch` · `grey16` · `rgb16` |
### `removeAlpha`
Drop the alpha channel.
_Takes no options._
### `ensureAlpha`
Add an alpha channel if absent.
Takes `alpha` directly — `{ "ensureAlpha": { "alpha": … } }`.
| Parameter | Accepts |
|---|---|
| `alpha` | number, 0–1 |
### `extractChannel`
Keep one channel.
Takes `channel` directly — `{ "extractChannel": { "channel": … } }`.
| Parameter | Accepts |
|---|---|
| `channel` | `red` · `green` · `blue` · `alpha` · `0` · `1` · `2` · `3` |
### `bandbool`
Boolean-combine all channels into one.
Takes `operator` directly — `{ "bandbool": { "operator": … } }`.
| Parameter | Accepts |
|---|---|
| `operator` | `and` · `or` · `eor` |
### `composite`
Overlay another asset. `input` is an asset id, never a path.
| Parameter | Accepts |
|---|---|
| `input` | **asset id** (never a path). The asset to overlay. Must already be uploaded. |
| `blend` | `clear` · `source` · `over` · `in` · `out` · `atop` · `dest` · `dest-over` · `dest-in` · `dest-out` · `dest-atop` · `xor` · `add` · `saturate` · `multiply` · `screen` · `overlay` · `darken` · `lighten` · `colour-dodge` · `color-dodge` · `colour-burn` · `color-burn` · `hard-light` · `soft-light` · `difference` · `exclusion` |
| `gravity` | `centre` · `center` · `north` · `northeast` · `east` · `southeast` · `south` · `southwest` · `west` · `northwest` |
| `top` | integer, 0–16383 |
| `left` | integer, 0–16383 |
| `tile` | boolean |
| `premultiplied` | boolean |
## Output formats
Given as `output.format`, with `output.options` carrying the encoder settings. Defaults to `webp`.
### `jpeg`
JPEG.
| Parameter | Accepts |
|---|---|
| `quality` | integer, 1–100 |
| `progressive` | boolean |
| `chromaSubsampling` | `4:4:4` · `4:2:2` · `4:2:0` |
| `optimiseCoding` | boolean |
| `mozjpeg` | boolean. Turns on a bundle of mozjpeg settings at once. |
| `trellisQuantisation` | boolean |
| `overshootDeringing` | boolean |
| `optimiseScans` | boolean |
### `png`
PNG.
| Parameter | Accepts |
|---|---|
| `progressive` | boolean |
| `compressionLevel` | integer, 0–9 |
| `adaptiveFiltering` | boolean |
| `palette` | boolean. Quantise to a palette — much smaller for flat graphics. |
| `quality` | integer, 0–100 |
| `effort` | integer, 1–10 |
| `colours` | integer, 2–256 |
| `dither` | number, 0–1 |
### `webp`
WebP.
| Parameter | Accepts |
|---|---|
| `quality` | integer, 1–100 |
| `alphaQuality` | integer, 0–100 |
| `lossless` | boolean |
| `nearLossless` | boolean |
| `smartSubsample` | boolean |
| `preset` | `default` · `photo` · `picture` · `drawing` · `icon` · `text` |
| `effort` | integer, 0–6 |
| `loop` | integer, 0–100000 |
| `minSize` | boolean |
| `mixed` | boolean |
### `avif`
AVIF. Smaller than WebP at the same quality, slower to encode.
| Parameter | Accepts |
|---|---|
| `quality` | integer, 1–100 |
| `lossless` | boolean |
| `effort` | integer, 0–9. Higher is smaller and much slower. |
| `chromaSubsampling` | `4:4:4` · `4:2:0` |
| `bitdepth` | `8` · `10` · `12` |
### `tiff`
TIFF.
| Parameter | Accepts |
|---|---|
| `quality` | integer, 1–100 |
| `compression` | `none` · `jpeg` · `deflate` · `packbits` · `ccittfax4` · `lzw` · `webp` · `zstd` · `jp2k` |
| `predictor` | `none` · `horizontal` · `float` |
| `pyramid` | boolean |
| `tile` | boolean |
| `tileWidth` | integer, 1–16383 |
| `tileHeight` | integer, 1–16383 |
| `xres` | number, 0.01–100000 |
| `yres` | number, 0.01–100000 |
| `resolutionUnit` | `inch` · `cm` |
| `bitdepth` | `1` · `2` · `4` · `8` |
### `gif`
GIF.
| Parameter | Accepts |
|---|---|
| `reuse` | boolean |
| `progressive` | boolean |
| `colours` | integer, 2–256 |
| `effort` | integer, 1–10 |
| `dither` | number, 0–1 |
| `interFrameMaxError` | number, 0–32 |
| `interPaletteMaxError` | number, 0–256 |
| `loop` | integer, 0–100000 |
### `jp2`
JPEG 2000.
| Parameter | Accepts |
|---|---|
| `quality` | integer, 1–100 |
| `lossless` | boolean |
| `tileWidth` | integer, 1–16383 |
| `tileHeight` | integer, 1–16383 |
| `chromaSubsampling` | `4:4:4` · `4:2:2` · `4:2:0` |