Docs menu

Reference

Container contract

What a slowapp container is given and what it must produce, under contract 3: command, environment, files, exit codes and limits.

The contract is the set of rules between a hub and an app’s container. Any image that follows it is a slowapp, in any language. The Python SDK implements all of it for Python apps; Slowapps in any language follows it by hand in Go.

Command

The hub starts the container with one of two commands:

  • job <name> runs one job. The container is removed when it exits.
  • serve runs the app’s web page, listening on the Unix socket /run/sb/ui.sock.

While the page is running, the hub sends jobs to it as POST /_sb/jobs/<name> instead of starting another container.

Environment

VariableValue
SLOWAPP_CONTRACT3
SLOWAPP_APP, SLOWAPP_JOB, SLOWAPP_RUN_IDThe app id, the job name and this run’s id
SLOWAPP_REASONWhy the job runs: planner, schedule, manual, event or fixture
SLOWAPP_NOWThe hub’s current time
SLOWAPP_FORThe time the cards will be shown
SLOWAPP_DEADLINEWhen the job must be finished
TZThe screen’s time zone
SLOWAPP_MODElive, or fixture in a test render
SLOWAPP_FEEDSThe feeds wanted this run, as JSON: [{"feed":"prompts","slot":"right","w":452,"h":444,"inks":"k4","bpp":2,"key":"black"}]
SLOWAPP_STATE/data
OPENBLAS_NUM_THREADS, OMP_NUM_THREADS1

Times are RFC 3339 with an offset. A feed declared with size = "slot" gets the slot’s real size in SLOWAPP_FEEDS.

Files

PathAccessContents
/dataread-write (read-only in a test render)The app’s own folder, kept between runs
/tmp, /cacheread-write, in memoryScratch space, sized by the manifest
/outread-writeThe run’s output, read by the hub after the job exits
/inread-onlysettings.json, layout.json, and events.jsonl for an event job
/run/secrets/<name>read-onlyOne file per secret (live runs only)
/run/sbread-writeThe web page’s socket and log

Everything else is read-only.

Output files

/out/feeds/<feed>/deck.json        the feed's cards, in order, and how long they are valid
/out/feeds/<feed>/cards/<id>.png   one picture per card
/out/prompts.json                  questions (needs the prompts grant)
/out/prompts/<id>/<answer>.png     the rest card for each answer
/out/prompts/<id>/ask.png          an optional question card
/out/summary.json                  status lines and buttons for the hub's app card (any job)
/out/cache_key                     if equal to the last run's, the previous cards are kept
/out/timing.json                   {"next_change_at": "…"}: when the content next changes

deck.json:

{"cards": [{"id": "home", "title": "Today", "peek": "17° cloudy", "kind": "home"},
           {"id": "hours", "title": "Next 24 hours", "kind": "detail", "keep_s": 600}],
 "valid_from": "2026-10-01T13:15:00-07:00",
 "valid_until": "2026-10-01T14:15:00-07:00"}
  • The first card has kind home; every other card is detail.
  • Card ids use lowercase letters, digits, - and _.
  • Every card needs a title. peek and keep_s are optional.
  • valid_until must be after valid_from.

A question with a question card fills its slot’s whole card area, and the strip shows only the answers. Without one, the question goes in the strip. The answer names are own.tap, spare.tap, own.hold and spare.hold; see Questions and answers.

Card pictures

Both the hub and slowd reject a card that breaks these rules:

  • exactly the surface’s width and height;
  • a PNG with 2-bit or 8-bit indexed colour;
  • a palette that contains only the surface’s inks (for k4: black, white, yellow and red) and no transparency;
  • at most 256 KB;
  • at most 32 cards per feed, and at most 8 questions.

Exit codes

ExitMeaning
0Store the output.
3Unchanged: keep the previous cards.
4Temporary failure: try again later, or once the network is back.
5Needs configuration or sign-in: shown on the hub, not retried until a setting, secret or grant changes.
64Contract error: the app broke these rules.
137Stopped: out of memory or out of time.

Any other code is a failure, retried with a growing delay.

Test renders

In a test render (SLOWAPP_MODE=fixture) the container has no network and no secrets, /data is the example’s read-only fixture, and SLOWAPP_NOW and SLOWAPP_FOR are fixed to the example’s time. The same fixture must always give the same pictures. Test without hardware has the details.

Updates, now and then

A short note when there is a new board, a new slowapp, or kit news. Rarely more than one a month.