Shared URL settings

Copy Markdown View Source

These settings must match between an application that builds URLs with image_pipe_url and the image_pipe_server or ImagePipe.Plug that serves them. A mismatch makes the server reject URLs the builder produced. Building URLs for the server walks through setting up both sides.

ImagePipe.Plug takes the builder's settings under the same names: pass the same keys and source encryption options to ImagePipe.config/1, and a base_url for URLs it builds itself. Where a Plug host spells a setting differently, the entry says so.

Signing keys

The hex-encoded HMAC keys that sign each URL. The server's list must hold the first key in the builder's list, which is the one the builder signs with. When neither side has keys, URLs are unsigned. See signing URLs and rotating keys.

Elixir (URL builder)

ImagePipe.URL.config(keys: [System.fetch_env!("IMAGE_PIPE_SIGNING_KEY")])

image_pipe_server

[url]
keys = ["0123abcd…"]

Or IPS_URL__KEYS=0123abcd…, comma-separated for several keys. See [url].

On a mismatch:

  • A signature that no server key verifies answers 403 with the body invalid signature.
  • An unsigned URL sent to a server with keys answers 403 with the body invalid signature.
  • A signed URL sent to a server without keys answers 400. The body points at the sig= segment with sig is not accepted: no signing keys are configured.

Source encryption keys

The 32-byte keys, written as 64 hex digits, that encrypt the source into an enc/<token> segment. Generate one with openssl rand -hex 32. The builder encrypts with the first key. The server decrypts with any key in its list. See source concealment.

Elixir (URL builder)

ImagePipe.URL.config(
  keys: [System.fetch_env!("IMAGE_PIPE_SIGNING_KEY")],
  source_encryption_keys: [System.fetch_env!("IMAGE_PIPE_SOURCE_KEY")],
  encrypt_source: true
)

image_pipe_server

[url]
keys = ["0123abcd…"]
source_encryption_keys = ["89abcdef…"]

Or IPS_URL__SOURCE_ENCRYPTION_KEYS=89abcdef…, comma-separated for several keys. See [url].

encrypt_source (encrypt every source) and iv_mode (whether a source always encrypts to the same token) are builder settings. The server decrypts every token without them. Its [url] doesn't accept them, and the server stops at boot with url.encrypt_source: unknown setting.

Encrypted watermark sources (wm-enc) use the same keys. Encryption keys without signing keys, or an encryption key equal to a signing key, make ImagePipe.URL.config/1 and ImagePipe.config/1 raise, and stop image_pipe_server at boot.

On a mismatch:

  • A token that no server key decrypts answers 404 with the body not found.
  • An enc/ URL sent to a server without source encryption keys answers 400. The body points at the token with enc/ is not accepted: no source encryption keys are configured.

Base URL and mount path

The builder's base_url is the address clients use, including the path the server serves images under. The signature covers only the path after that prefix, so the prefix can change without re-signing.

Elixir (URL builder)

ImagePipe.URL.config(base_url: "https://images.example.com/images", keys: keys)

image_pipe_server

[server]
mount_path = "/images"

The host part of the address comes from DNS, a proxy, or a CDN in front of the server. With auth_token set in [server], image requests must also send Authorization: Bearer <token>. [url] doesn't accept base_url, and the server stops at boot with url.base_url: unknown setting. See [server].

A Plug host's prefix is the path its router mounts ImagePipe.Plug at, such as forward "/images", ImagePipe.Plug, …. Its base_url only prefixes URLs built with ImagePipe.url_config/2. The Plug doesn't check it.

On a mismatch:

  • A path outside mount_path answers 404 with an empty body.
  • A base_url path longer than mount_path leaves extra segments in front of the signature. With signing keys, the request answers 403 with invalid signature. Without them, it answers 400 with the parse error.
  • With [server] auth_token set, a request without the bearer token answers 401 with an empty body. A URL can't carry that header, so a browser loading builder URLs gets 401 unless a proxy or CDN in front of the server adds it.

Preset names

URLs carry preset names, such as preset=card. The server applies its current definition of each name, so the builder needs no definitions to build URLs. The signature covers the name only, so changing a definition on the server keeps signed URLs valid. Define a preset on the server before the app builds URLs with it, and keep it there while those URLs are in use. The server answers 400 to a URL with a preset it doesn't have. See presets.

Elixir (URL builder)

ImagePipe.URL.config(
  keys: keys,
  validate_against: [
    presets: %{"card" => "w=400/h=300/fit=cover"},
    request_defaults: "q=80"
  ]
)

validate_against, a copy of the server's presets, request defaults, and watermark settings, is optional. It lets ImagePipe.URL.validate/1 and ImagePipe.URL.url/3 check plans against the server's presets, request defaults, and watermark settings. It never changes a generated URL. preset_lookup: true says the server resolves names missing from presets with a lookup, so the builder leaves them to it. See ImagePipe.URL.validate/1.

image_pipe_server

[processing]
request_defaults = "q=80"

[processing.presets]
card = "w=400/h=300/fit=cover"

See [processing].

A Plug host defines them as presets: and request_defaults: in ImagePipe.config/1, and may add a preset_lookup:. Its builder gets them filled in with ImagePipe.url_config/2.

On a mismatch:

  • A name the server doesn't define answers 400. The body points at the preset= option with unknown preset: card.
  • A validate_against copy that differs from the server gives wrong validation results in the builder. The URLs stay the same, and the server checks them against its own definitions.

Source prefixes and schemes

The URL carries the whole source string, escaped or encrypted, and the server routes it by its first path segment or its scheme. The string must start with a prefix or scheme a server source serves. The source's name (static in [sources.static]) never appears in URLs. See routing image paths to sources.

Elixir (URL builder)

ImagePipe.URL.url!(builder, "media/photos/beach.jpg")
ImagePipe.URL.url!(builder, "https://assets.example.com/beach.jpg")

image_pipe_server

[sources.media]
adapter = "file"
match = { prefix = "media" }
root = "/data/media"
root_id = "media"

[sources.web]
adapter = "http"
match = { scheme = ["http", "https"] }
allowed_hosts = ["assets.example.com"]

See [sources.<name>].

A Plug host configures the same rules as match: in sources: of ImagePipe.config/1.

On a mismatch:

  • A path that no prefix matches, on a server without a match = "path" source, answers 404 with source not found. With a match = "path" source, that source looks the path up and answers 404 when nothing is there.
  • A URL whose scheme no source matches, such as https://…, s3://…, or asset://…, answers 400 with invalid source.

Watermarks

wm=<name> selects a watermark image the server defines. See watermark assets.

Elixir (URL builder)

config =
  ImagePipe.URL.config(
    keys: keys,
    validate_against: [
      presets: %{"card" => "w=400/h=300/fit=cover"},
      watermarks: [:logo],
      request_watermarks: true
    ]
  )

builder = ImagePipe.URL.new(config)
ImagePipe.URL.group(builder, watermark: :logo)
ImagePipe.URL.group(builder, watermark_source: "brand/badge.png")

watermarks and request_watermarks in validate_against are optional. With watermarks, ImagePipe.URL.validate/1 and ImagePipe.URL.url/3 check watermark: names against the server's. With request_watermarks: false, they reject watermark_source:, as a server without request_watermarks does. true, or leaving it out, lets watermark sources through. validate_against always checks presets too, so list the server's presets next to the watermark settings.

image_pipe_server

[processing]
request_watermarks = true

[processing.watermarks.logo]
source = "brand/logo.png"

See [processing].

A Plug host defines them as watermarks: and request_watermarks: in ImagePipe.config/1, and its builder gets both filled in with ImagePipe.url_config/2. request_watermarks lets URLs name their own watermark source (watermark_source: in the builder, wm-src64 or wm-enc in the URL).

On a mismatch:

  • A name the server doesn't define answers 400. The body points at the wm= option with unknown watermark. A builder whose validate_against lists the names returns an :unknown_watermark issue instead of a URL.
  • A watermark source in the URL, on a server without request_watermarks, answers 400 with request watermark sources are not enabled. A builder whose validate_against has request_watermarks: false returns a :watermark_source_disabled issue instead of a URL.