Livery Server-driven Remote Compose templates GitHub

Images and fonts#

A template never fetches anything itself; every image and font it draws comes through the client.

Catalog assets#

Put images a design needs in designs/<id>/assets/ and fonts in fonts/ with fonts/fonts.json:

[ { "file": "Figtree-SemiBold.ttf", "family": "Figtree", "weight": 600, "license": "OFL-1.1" } ]

A template lists the families it uses ("fonts": ["Figtree"], or google: / system: families). Assets are content-addressed and verified, and download with the template; a template is never handed out without them. Catalogs with assets require protocol level 2 clients.

Images in data#

Value Meaning
asset:<sha256> a catalog image
https://… a remote image, through the image proxy or an allowlisted host
app:<key> an image your app supplies (an avatar, an entity picture)

Anything else is refused by the contract check. A template draws a data-bound image from the URL livery:value/<input>.

The image proxy#

Declare it in catalog.json; livery build writes it into the manifest:

"imageProxy": {
  "urlTemplate": "https://img.acme.example/{width}x{height}/{format}?u={url}",
  "sourceHosts": ["cdn.adnet.example"], "maxWidthPx": 1600, "maxHeightPx": 1600
}

The device then talks only to the proxy: images arrive sized for the slot, in one format, without cookies or trackers. Without a proxy, only hosts the app allowlists (ImageResolver(directHosts = …)) are fetched. Redirects are checked hop by hop, URLs with user info are refused, and downloads stop at maxBytes.

val images = ImageResolver(repository, KtorFetcher(), JavaCrypto, directHosts = setOf("cdn.acme.example"),
  app = { key, w, h -> avatars.load(key, w, h) })
val host = LiveryHost(repository, actions, events, clock, images = images)

The host keeps what it loads in memory (host.resources): decoded images by reference and size, and fonts by asset hash. A picture or a face that any slot on the host has already drawn appears on the next slot's first frame, and a template whose faces another template loaded draws without waiting. An app: image is the app's own and may change under the same key, so it is asked for every time; give your app loader its own memory cache to draw it on the first frame. host.resources.preload(plan) loads a plan's document, fonts and images ahead of time, and host.resources.readyToDraw(template) its document and fonts; both prepare the document off the main thread.

Edit this page