| 1 | # The instance-report contract (`/.carlos/instances`) |
| 2 | |
| 3 | Some CARLOS apps are internally plural: one route on the platform, many |
| 4 | instances inside the app (Kass and its training instances, titogo and its |
| 5 | event routers). The platform's registry sees the route; only the app knows |
| 6 | its insides. This contract lets an app volunteer that knowledge to the |
| 7 | console. It is entirely optional — an app that doesn't implement it renders |
| 8 | in the console exactly as before. |
| 9 | |
| 10 | ## What the app exposes |
| 11 | |
| 12 | ``` |
| 13 | GET /.carlos/instances |
| 14 | ``` |
| 15 | |
| 16 | on its **public host** (the same host the platform routes to it). Response: |
| 17 | |
| 18 | - Status `200`, body a **JSON array** of instance objects: |
| 19 | |
| 20 | ```json |
| 21 | [ |
| 22 | {"slug": "riders-2026", "status": "ready", "region": "eu-west-1"}, |
| 23 | {"slug": "spring-camp", "status": "ready", "region": "eu-west-1"}, |
| 24 | {"slug": "trial-run", "status": "archived", "detail": "read-only since June"} |
| 25 | ] |
| 26 | ``` |
| 27 | |
| 28 | - `slug` (string, **required**): the instance's stable short name. Entries |
| 29 | without a slug are dropped. |
| 30 | - `status` (string, recommended): one short word — the console renders it |
| 31 | verbatim. Use the platform's route-status vocabulary where it fits |
| 32 | (`ready`, `starting`, `failed`) plus whatever the app means (`archived`, |
| 33 | `migrating`). |
| 34 | - `region` (string, optional): where the instance's data lives. |
| 35 | - `detail` (string, optional): one short human sentence. |
| 36 | - Unknown fields are ignored; the contract may grow additively. |
| 37 | |
| 38 | Limits: at most **100 instances** and **64 KiB** of body are read; beyond |
| 39 | either, the report is treated as absent. |
| 40 | |
| 41 | The endpoint must serve **non-secret operational metadata only**. It is |
| 42 | unauthenticated by design (the console calls through the public host, like |
| 43 | any visitor), so anything an app would not put on a status page does not |
| 44 | belong in it. Apps that want the console table implement the endpoint; apps |
| 45 | that don't, 404 — both are correct behavior. |
| 46 | |
| 47 | ## What the console does |
| 48 | |
| 49 | - Polls the app's **first ready, non-static route that has not opted into |
| 50 | hibernation** (routes sort by host). Hibernatable hosts are never polled: |
| 51 | a poll could wake them, and the console must never wake an app to draw a |
| 52 | table. |
| 53 | - Uses a **1.5 s timeout** and treats anything but a fast, valid `200` — |
| 54 | connection refused, timeout, non-200, junk JSON, oversized body — as |
| 55 | "not reporting". Not reporting is silent: the app page shows the ordinary |
| 56 | route-level view, nothing more. |
| 57 | - **Caches per host for 30 s** (successes and failures alike), so page |
| 58 | reloads don't drum on tenants. |
| 59 | - Renders the instance count and a slug/status/region/detail table in an |
| 60 | "Instances" section on the app page. |
| 61 | |