Livery

Remote Compose for Android, Wear OS, iOS and desktop

Change what's on screen without shipping an app.

Livery publishes Remote Compose templates as a signed catalog on any static host. Apps, launcher widgets, watches and notifications fetch, verify and cache them, fill them with typed data and play them, without ever waiting on the network.

9:41
Rendered sample: the Kitchen status tile, On · 80%
Remote Compose templates in productionEngineering · 2h androidsdui
In-app slots
KitchenOn · 80%
Launcher widget
KitchenOn · 80%
Wear widget

Design once. Publish anywhere. Play everywhere.

Three steps, and the app never needs a release to show a new look.

  1. 1

    Agree a contract

    The contract is the only thing the app and the designer share: what a template may read and which actions it may emit. Author the .rc in any tool.

    "id": "promo/banner", "major": 1,
    "inputs": {
      "headline": { "type": "string",
                    "maxLength": 40 },
      "accent":   { "type": "color" } },
    "actions": {
      "cta": { "kind": "navigate" } }
  2. 2

    Build, sign, publish

    The livery CLI refuses a broken catalog, signs the manifest and writes content-addressed files for a GitHub release, a CDN or your own server.

    livery keygen acme
    livery build catalog build/release \
      --github acme/app-templates \
      --tag catalog-1 \
      --key acme.key --key-id acme-1
  3. 3

    Play it in a slot

    The client picks the right template for each player and size, binds data through the contract and draws it, from disk first, with your native fallback underneath.

    LiverySlot(
      key = tile,
      host = host,
      fallback = { NativeTile(tile) },
    ) {
      planner.forAppData(query, data)
    }

How it fits together

Livery sits between "there is a .rc document" and "it is on screen with the right data", and reports what was shown and tapped.

  1. Authorany tool.rc + sidecar JSON
  2. livery CLIvalidate, signmanifest + blobs
  3. Any hostGitHub release, CDNdesign server, assets
  4. Your appfetch, verify, cacheselect, bind, play
Contract
What a template reads and may emit (promo/banner@1).
Design
One look for a contract, as a picker shows it (banner-classic).
Template
One values-free .rc per design and target.
Catalog
A signed manifest.json plus content-addressed files.

One template, any data, one gate

A template is values-free. The app, a decision server or a feed supplies the data, and ContractBinder checks it against the contract on the way in: the same check in the app, the publisher and agent tools. Data that doesn't fit is refused, not dropped, and the slot shows your native fallback instead.

Designs are swappable too: ship a bolder banner to a rollout cohort or let people install a skin, and a design that can't play falls back to the automatic one, never to nothing.

Contracts, designs and templates →

{ "name": "Kitchen", "state": "On · 80%",
  "is_on": true, "level": 0.8 }
Rendered: Kitchen, On · 80%, bulb lit
{ "name": "Kitchen", "state": "Off",
  "is_on": false, "level": 0 }
Rendered: Kitchen, Off, bulb grey
{ "name": "Kitchen", "state": "Unavailable",
  "available": false }
Rendered: Kitchen, Unavailable
{ "name": "Kitchen", "state": 80,
  "colour": "red" }
Refusedstate: expected string · colour: not in live/status-tile@1

Get started

Libraries on Maven Central as ee.schimke.composeai:<module>; the livery and livery-mcp CLIs attached to each GitHub release.

dependencies {
  implementation(platform("ee.schimke.composeai:livery-bom:<version>"))
  implementation("ee.schimke.composeai:livery-client")   // fetch, verify, cache, select, bind
  implementation("ee.schimke.composeai:livery-compose")  // LiveryTemplate, LiverySlot
  // livery-launcher-widgets, livery-wear-widgets, livery-notifications
}

Android, JVM, iOS and Wasm from one Kotlin Multiplatform client. Set up the repository →

// Package.swift
.package(url: "https://github.com/yschimke/livery", branch: "main")

Livery and LiveryUI for iOS and macOS, held to the Kotlin client by shared conformance fixtures. Swift details →

git clone https://github.com/yschimke/livery && cd livery
./gradlew :livery-publish:installDist :livery-capture-cmp:sampleCatalogs
export PATH="$PWD/livery-publish/build/install/livery/bin:$PATH"
livery build livery-capture-cmp/build/samples/catalog build/sample-catalog

Validate, sign and lay out a catalog for any host. Hosting →

./gradlew :livery-mcp:installDist
claude mcp add livery -- /abs/path/livery-mcp path/to/catalog \
  --write --preview-out build/previews

Contracts, the data gate and renders for any MCP agent, but never the signing key. AI workflows →

  1. Make a signing keyYour apps embed the public half.
  2. Write a catalogA directory of contracts and designs.
  3. Publish a releaseAny static host works.

What you can build

Each page goes from the contract to a deployment checklist.

Built for templates in production

Never waits on the network

Slots draw from disk, then the bundled catalog, then your native fallback. refresh() runs in the background.

Refused, not dropped

One gate, ContractBinder, checks data in the app, the publisher, decision servers and agent tools alike.

Signed catalogs

Ed25519 manifests, partner keys scoped to a contract prefix, rotation, freshness and a kill switch. Signing →

Any static host

GitHub releases, a CDN or a design server. Content-addressed files cache forever. Hosting →

The right template per player

TemplateSelector matches surface, Remote Compose level, profiles and size.

Agents included

livery-mcp gives agents contracts, validation and PNG previews; drafts stay unsigned. AI workflows →

Kotlin and Swift agree

Both ports run the same conformance/ fixtures for selection, binding, resources and trust.

An open protocol

A normative, versioned manifest: new fields have defaults, old clients ignore what they don't know. Protocol →

Platforms

Platform details →

Your next screen change doesn't need a release.