> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lumovi.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The kit: a plugin's pages

> What a plugin's code can make a page of: Lumovi's own parts, what each one takes, what a page can ask Lumovi for, and the limits it runs within.

A plugin with code doesn't draw its page. It describes it, as parts Lumovi has, and Lumovi draws them: the same tables, tiles and cards its own pages are made of, in its own type, colors and spacing, on the desktop and on a phone. Its code runs in a [sealed box](/custom-resources/plugins#pages-drawn-by-a-plugin’s-code), and everything it says passes through Lumovi's door, which checks each part against what's on this page.

This is the reference for the parts, for what a page can ask of Lumovi, and for its limits. To start a plugin of your own with code, use the [template](https://github.com/Lumovi/Lumovi-plugins/tree/main/template) in Lumovi-plugins, and see [Your own plugins with code](/custom-resources/plugins#your-own-plugins-with-code). What a plugin's file can say is in the [plugin format](/reference/plugin-format).

## What a page is

* **A page is made only of Lumovi's parts.** Where you need one Lumovi doesn't have, ask for it in an [issue](https://github.com/Lumovi/Lumovi/issues): a plugin can't draw its own. There's no HTML, no CSS and no canvas, and no width, gap or position is a plugin's to give.
* **Every text is drawn as text.** A name, a word, a line or a server's error is the plugin's, and Lumovi draws it as its characters: never as a link, a class or markup, and a character that can't be seen, or that would turn a line round, shows as a placeholder.
* **Levels and icons are Lumovi's.** A status is one of `healthy`, `progressing`, `warning`, `critical` and `neutral`, with the plugin's word beside it. An icon is one of the [icons](/reference/plugin-format#icons) a view picks from.
* **Times are said by Lumovi.** A moment is given as RFC 3339 text, and Lumovi says how long ago or how soon, and keeps it current.
* **What's pressed opens Lumovi's own.** A row, a card or a tile that opens something names an object Lumovi handed the page, or a list of a kind its file says its pages [open](/reference/plugin-format#spec), and Lumovi opens it.

## The parts

Three arrangements hold the rest, and nothing else does:

| Part | Holds | Is |
| - | - | - |
| `Stack` | Any part but a `Split` | One under another: what a page is when nothing else is said |
| `Pair` | Exactly two `Card`s | Two cards side by side, `even` unless `first: wider` (seven parts of twelve). One under the other on a narrow page |
| `Split` | One `Stack` | A pick list at its side, then a stack that follows what's picked. A page has one at most, and nothing holds it. Narrower than 640 pixels, it's two pages: the list, or **Back** above the picked one's |

And the parts they hold:

| Part | Takes | Is |
| - | - | - |
| `Header` | `name`, `status` (a level, a word, a `detail` line), a `secondStatus` beside it, up to 8 `buttons` | What's picked, its status, and what can be done with it. Three buttons show, the rest behind **…** |
| `Tiles` | 2 to 4 `tiles`: a `key`, a `label`, an `icon`, a `value`, a `detail` line with a `level`, `opens` | A row of headlines, of equal width. A value is a `figure` ("2/3"), `words` ("Never") or a `time` |
| `Card` | `title`, `note`, `about`; holds one `Table`, `Graph`, `Counts` or `Steps` | One part under a title, with a quiet note at its right |
| `Table` | `title`, up to 8 `columns`, `headings`, `rows`, `shows`, `state` | Rows with a lead (a name, a word beside, a line under, a level) and a cell for each column. Rows may hold rows, one level, and stand in groups |
| `Graph` | `title`, `legend`, `columns`, `nodes`, `edges`, `state` | Cards with lines from one to another, solid or dashed. Past 300 cards or 600 lines, it's shown as its table, and says why |
| `Steps` | 2 to 12 `steps`, each a `key`, a `name` and a state (done, running, to come or failed); `waiting` | Where a run is: its steps in order on one line, never by colour alone. Under them, what the running step waits on (`waiting`: a lead, what it says, and an object Lumovi opens). On a phone, one sentence, "Step 3 of 5, Wave 1, running: after Wave 0, before Wave 2.", and a card |
| `Counts` | Up to 6 `groups`, each a `title` and up to 50 `rows` of a `value` and a `count`, which may say where it `opens` | Shares of a whole, as bars, the largest first |
| `Logs` | `title`, `pod`, `container` | A pod's logs in Lumovi's own log view, where the file says [`logs`](/custom-resources/plugins#logs-on-a-plugin’s-page). The plugin's code never sees a line |
| `Secret` | `title`, `secret`, `keys` | A Secret's keys, each hidden until **Reveal**, with **Copy**, in Lumovi's own card, where the file says [`showsSecrets`](/reference/plugin-format#spec). The plugin's code never sees a value |

### Every part, exactly

What each part takes, as Lumovi's door checks it. A property not listed here stops the page, as a part Lumovi doesn't have does. Properties starting with `on` are what the page is told: a row opened, a button pressed, a pick.

| Part | Holds | Takes | Needs | Tells the page |
| - | - | - | - | - |
| `Table` | nothing | `columns`, `cut`, `filters`, `headings`, `leadHeading`, `nameSorts`, `onOpen`, `onPress`, `onRetry`, `rows`, `search`, `shows`, `state`, `ticks`, `title` | `columns`, `headings`, `rows`, `title` | `open`, `press`, `retry` |
| `Graph` | nothing | `columns`, `edges`, `legend`, `nodes`, `onOpen`, `onRetry`, `state`, `title` | `columns`, `edges`, `nodes`, `title` | `open`, `retry` |
| `Tiles` | nothing | `onOpen`, `tiles` | `tiles` | `open` |
| `Header` | nothing | `buttons`, `name`, `onPress`, `secondStatus`, `status` | `name` | `press` |
| `Split` | `Stack` | `list`, `onPick` | `list` | `pick` |
| `Card` | `Table`, `Graph`, `Counts`, `Steps` | `about`, `note`, `title` | `title` | nothing |
| `Pair` | `Card` | `first` | nothing | nothing |
| `Counts` | nothing | `groups` | `groups` | nothing |
| `Steps` | nothing | `steps`, `waiting` | `steps` | nothing |
| `Stack` | `Tiles`, `Header`, `Card`, `Pair`, `Table`, `Graph`, `Counts`, `Steps`, `Logs`, `Secret` | nothing | nothing | nothing |
| `Logs` | nothing | `container`, `pod`, `title` | `title` | nothing |
| `Secret` | nothing | `keys`, `secret`, `title` | `title` | nothing |

### A table's cells

A column has a `name`, a `kind`, a `width` (`narrow`, `normal`, `wide` or `text`) and whether it `sorts`. A cell's kind is its column's:

| Kind | Holds |
| - | - |
| `text` | Words. `mono` sets them in fixed-width type, and `full` is the whole of what they stand for (a commit's whole hash behind its first seven), said in the tooltip |
| `number` | A number, or one with its unit as text ("7 nodes") |
| `age` | A moment, which Lumovi says as how long ago |
| `status` | A level and its word. Sorts worst first |
| `meter` | `used` of `of`, with its text ("24 of 64 cores"). Without `of`, there's no limit |
| `button` | A button of the row's own: a `key`, a `name`, and whether it `changes` something or `asks` first. Off, it stays in its place and says why, up to 80 characters. A row without one says a word in its place ("Current"). It's pressed on its own, and a row with a button opens nothing; Enter presses the active row's. A phone doesn't draw one that changes something. A table has one such column, and only its own rows have buttons, never rows inside a row |
| `none` | Nothing to say: a dash |

A row has one tooltip, and it's Lumovi's own, under the pointer and on the row the keyboard is on. It says, in this order and only what there is to say: where the row goes, if it goes to an object's **Logs** tab, its **Shell** tab or **Port forward**, and Lumovi offers that place here; its name, the words beside it and the line under it, whole, where any of the three is cut; and the columns that gave way, each with its heading and its value. Where a table is too narrow for its columns, some give way whole, so none is squeezed. A cell that's only cut by its column's width has a tooltip of its own under the pointer; on the keyboard's row, the row's tooltip says it too. A screen reader is read where the row goes, and the columns that gave way, after the row. Under a finger there's no tooltip.

Beside a name, the words take the room the name leaves. Where the two don't fit, the words give way first, down to about five characters, and only then does the name lose its middle.

A table can have a filter bar, Lumovi's own: `search`, a field that keeps the rows whose name, or the words beside it, has what's typed, and up to 8 `filters`, each with a `key` of up to 40 characters. A filter is a menu of the values the rows give it in their `facets` (up to 32 a row), with how many have each, or of the rows' own levels: Failing, Warning, In progress, Healthy, Inactive. Values in one filter are either; filters together are all. The bar says how many rows show, like "2 of 5", and each filter that's set as a chip, "Take off Status: Failing". On a phone, the filters are behind **Filters**. Nothing typed, picked or ticked there reaches the plugin: Lumovi picks the rows.

With `ticks`, a person can select the rows that open an object, one by one, every row shown at once, or with Space, and Lumovi's own bar offers the file's actions that say [`bulk`](/reference/plugin-format#actions) for them, as Lumovi's own lists do for [several at once](/changes/bulk). The plugin's code picks no row, starts nothing, and is told nothing of what's selected or done.

A link to one of the plugin's own pages, `{ page }`, can set that page's filters and search, as a row, a tile or a count's row opens it: `{ page: applications, filters: { status: [Failing] } }`. It names a page by its id under the file's [`pages`](/reference/plugin-format#spec), and a link to any other goes nowhere. A count's row that `opens` is pressed whole, and Lumovi opens where it says, as a tile does.

A table's status cell can say `since`, and Lumovi counts how long, "Syncing 1m 05s", as a header's second status does.

A graph's card can be `marked`, drawn with an edge in the warning colour and said in the legend's `marked` words, which it then has to give. It can be shown for `context`, dashed and quiet, opening nothing. And where the page holds no token for its object, it can say where it `goes`, a list or place of Lumovi's own as a table's row does, with its kind's own icon. A card opens an object or goes somewhere, not both.

A run's steps run from 2 to 12 steps, each with a key of its own; a screen reader hears each as done, running now, to come, or failed.

In place of rows, a table (or a graph, or a pick list) can give a `state`: `busy` while it reads, `failed` with a title and what the cluster said, and **Try again**, `empty` with a title and a line, or `quiet`, one calm line. Where a table's rows come from a read Lumovi cut at its limit, the table names that read's `cut`, and Lumovi says so above the rows, from its own numbers.

### A Secret, shown and not held

Where a plugin's file says its pages show a Secret that its own kind names ([`showsSecrets`](/reference/plugin-format#spec)), a `Secret` part shows it: Lumovi's own card, captioned "Shown by Lumovi. Toys can’t see it." for a plugin named Toys, with a row for each key, hidden until **Reveal**, and **Copy** without revealing.

* **It takes a token, never a Secret.** `secret` is the token Lumovi handed the page with the object that names the Secret, at the path the file declares. `keys` can only narrow the keys the file names: one outside them stops the page. A token the page doesn't hold shows a card that says "This object names no Secret.", and nothing is read.
* **Lumovi reads it as you, on your press, and never before.** Each read is in the [audit log](/audit/events#access-access) as `secret.read`, naming the plugin.
* **Nothing goes back to the plugin's code.** The part holds nothing and tells the page nothing: not a value, its length, or that it was revealed, copied or refused.
* **Only on a page whose code Lumovi handed over.** Anywhere else, the same part shows its title and no Secret.
* Where your organization's [policy](/desktop/policy#pluginsecrets) says `pluginSecrets: off`, the card says "Your organization’s policy doesn’t let plugins show what’s in Secrets.", and nothing is read.

A page has at most 20 of them, each with at most 20 keys. None of the plugins Lumovi comes with shows a Secret yet.

### A card's `about`

A card can say where its data comes from, when its title and note can't say it: up to 240 characters, shown quietly under the title, two lines at most, the rest in its tooltip. Under a finger, where there's no tooltip, it wraps whole. Put what matters most first: on a narrow card, two lines hold about 60 characters.

### A header's buttons

A button has a `key` and a `name`, and may say:

* **`changes: true`**: the button changes something. Only its press lets the page propose a change, which waits for the person in Lumovi's dialog. A proposal after any other press is refused. Of a header's buttons, the last that changes something is drawn last, filled; one that only reads or goes never is. A phone doesn't draw a button that changes something, and says where it is.
* **`asks: true`**: it asks before it does anything, so its name ends in "…".
* **`note`**: what it does, up to 120 characters, under the buttons.
* **`off`**: why it can't be pressed now, up to 80 characters. It's drawn off, with this as its tooltip. Under a finger, the reason stands under the buttons, before the note, as "Back up now is off: A backup is running."
* **`busy`**: what it's busy with, `{ word, since }`: drawn where it was, with Lumovi's turning mark, the word, and how long since `since` where it's given, counted by Lumovi. It can't be pressed, its tooltip says "Syncing for 1m 05s. Sync can be pressed again when it’s done.", and a screen reader hears "Sync, busy: syncing, 1 minute", in whole minutes. A busy button can't also be `off` or `goes`. And after a change its press proposed is made, Lumovi holds that button busy as "Sent", with no time, until the page draws again with the changed object, 10 seconds at most: "Sent. Sync can be pressed again when it’s done." Nothing is held after **Cancel**, a refusal or a failure.
* **`goes`**: where it goes, in place of telling the page it was pressed: one of Lumovi's own places for an object the page was handed (a pod's **Logs** or **Shell** tab, **Port forward**), where the file's [`links`](/custom-resources/plugins#links-to-lumovi’s-own-places) say so, or a list of Lumovi's own. It changes nothing and asks nothing first.

### A header's second status

Beside its status, a header can say a second one, `secondStatus`: a level and a word for what's happening to it now, with how long since `since`, where it's given, counted by Lumovi. Its `detail`, where it has one, is the line under the header, in place of the first's: "Sync running: waiting for healthy state of apps/Deployment/storefront".

## What a page can ask for

A page's code asks Lumovi, and Lumovi reads as the person, within their access:

* **A list of a kind, or one object**: its own kinds whole, and of Kubernetes' own only the kinds Lumovi lets a plugin read (Pods, Nodes and ReplicaSets), each only the fields its file's [`reads`](/reference/plugin-format#spec) names. A list can be picked by labels and by the fields Lumovi picks a kind by, and can ask for each object's live usage. Lumovi keeps a list up to date, and hands each object with a token: what the page names when a row opens it.
* **An object's warning events**, where its file's read of the kind says `events: true`: of an object the page holds, by its token, kept up to date as the object is. Lumovi picks them by the object's uid, so an object made again under the same name isn't handed the old one's, and hands the newest 50, each only its reason, its message, how many times, and when first and last. A message is cut where it would carry what only a person may read, and says "(the rest left out by Lumovi)" there: at the API server's `Invalid value:`, `Unsupported value:`, `Duplicate value:`, `Not found:` and `Forbidden:`, at a probe's `probe failed:` or `probe errored:`, at a lifecycle hook's `lifecycle hook (`, and at an admission webhook's `denied the request:`. Then it's cut at 500 characters. That narrows what a message can carry; it doesn't make every message safe to hand.
* **A pod's last log lines**, once, where its file says `logs: read`. See [Logs on a plugin's page](/custom-resources/plugins#logs-on-a-plugin’s-page).
* **A path of its tool's own server**, once, where its file says `server`. See [A plugin's own server](/custom-resources/plugins#a-plugin’s-own-server).
* **A change**, proposed after a press of a button that says `changes: true`: of its own kinds, as its file's `changes` say. See [Changes a page proposes](/custom-resources/plugins#changes-a-page-proposes). Its dialog can ask the person things first, up to 24 fields: text, numbers and choices; a `check`, yes or no; `picks`, several of a list, like the resources to sync; and a `switch`, yes or no, which can show the fields after it only while it's on (`shows`), fill in values of its own off and on (`values`), and give the button its word while it's on (`verb`, like **Dry run**). Past a few, they stand under `section` headings. A page can add harms of its own to what its file says a change does, never take one away: see [What a change harms](/reference/plugin-format#what-a-change-harms).

A read Lumovi refuses is answered with why, in a word and a sentence of Lumovi's, and the page goes on. An ask that isn't written as one, or that goes past a limit, stops the page.

## What a page can't do

Its code runs in Lumovi's sealed box, where it has:

* no network: nothing reaches out, whatever it tries;
* no WebAssembly, and no more code: no workers, no `import()`, no `eval`;
* no `crypto.subtle`, in any browser or on the desktop: hashing, signing and verifying are Lumovi's. Random numbers are there, from `crypto.getRandomValues`;
* no storage that another plugin, another of its pages, or Lumovi can reach;
* nothing of Lumovi's page, its cookies or its credentials.

A plugin of your own may be at most 2 MB of code.

## Limits

A page past any of these is stopped, and says why in its place. The rest of Lumovi goes on.

| What | At most |
| - | - |
| Parts and texts a page holds | 5,000, 32 deep, 1,000 in one part |
| A text | 4,000 characters; a name, a word or a cell 253, a line 500 |
| Everything a page holds | 2 MB |
| A message to the door | 1 MiB, and 2,000 steps in one commit |
| In a second | 20,000 steps, 60 commits and 10 asks |
| A table's rows | 5,000, with 200 inside one row, and 8 columns |
| A graph | 5,000 cards and 10,000 lines |
| A pick list | 1,000 rows |
| Lists kept up to date | 8 by a page, 16 by all of a plugin's pages |
| Reads going at once | 50 by a page, 100 by a plugin, 20 not yet answered |
| Secrets shown | 20 `Secret` parts on a page, with 20 keys in one |
| Logs shown at once | 4 by a page, 8 by a plugin: more wait their turn |
| Asks of its server not yet answered | 2 by a page; 4 by a plugin, and more wait their turn |
| A refresh | Once in 10 seconds: one asked sooner isn't made |

One more isn't a stop. A plugin's pages read at most 200 different things in 10 minutes in one cluster: objects, logs and asks of its server together. The same read again doesn't count. Past that, a new read is refused as `too-many-reads` until the ten minutes end, and the answer says how many minutes are left; what the page already reads goes on. So everything a plugin's code reads as you is in the [audit history](/audit/events), or it isn't read.

In the desktop app, Lumovi also stops a page that uses more memory than a plugin may, or more than half of a processor core over half a minute.

## What needs which Lumovi

Everything on this page comes with Lumovi 1.22.0, the first with plugins' pages. A plugin's `needs.lumovi` is at least the first Lumovi that carries everything it uses: the checks in Lumovi-plugins read that from the Lumovi they pin, and refuse a plugin whose file says an older one.

<Columns cols={2}>
  <Card title="Plugins" icon="package" href="/custom-resources/plugins">
    Pages drawn by a plugin's code, and what each plugin asks for.
  </Card>

  <Card title="Plugin format" icon="braces" href="/reference/plugin-format">
    Every field a plugin's file can have.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.