Configuration reference
Every option in config.yaml, generated from the schema in src/server/config.ts, so it can’t drift. Unknown keys are errors, and Valet names the closest match. ${NAME} anywhere is replaced from the environment: see secrets.
The top level of config.yaml.
| Option | Type | Default | What it does |
|---|---|---|---|
title | string | Home | The page’s name, and the browser tab’s. |
greeting | string | optional | whose homelab it is, e.g. “Sam” |
sandbox | boolean | false | for trying Valet out: nothing is saved, each visitor gets a private copy in memory |
notice | string | optional | a quiet pill above the greeting, e.g. “Demo · simulated home” |
location | string | optional | Told to the assistant, for questions about here. Weather has its own coordinates. |
timezone | string | optional | default: TZ, or else the first browser’s |
weather | string | optional | a widget key (e.g. weather.now) shown as a ring beside the clock |
calendar | string | optional | a calendar widget (e.g. family.agenda) whose events appear when you hover the clock |
search | duckduckgo, google, kagi, brave, startpage, searxng or URL | optional | web search from ⌘K: a provider name, or a URL with {q} for the words |
links | map of list of object | {} | Bookmarks by group. Each group is a widget: links.<group>. |
recap | boolean | true | the year in review |
welcome | boolean | false | the getting-started card (a fresh config turns it on) |
reload | boolean | true | watch config.yaml and apply edits live (a bad edit keeps the last good one) |
Appearance
Section titled “Appearance”Look and feel. Every option becomes a CSS variable or a class, so a change shows without a rebuild. Left out, it sets nothing and the stylesheet’s own value stands.
| Option | Type | Default | What it does |
|---|---|---|---|
appearance | object | {} | Look and feel. |
appearance.theme | auto, light, dark | auto | auto: the system’s, or dark glass over a wallpaper; the others also drop the switch |
appearance.colors | object | optional | The page’s colours. |
appearance.colors.accent | string | optional | The one accent colour. Text on it turns black or white to suit, if it’s a hex value. |
appearance.colors.good | string | optional | The colour for good news. |
appearance.colors.warn | string | optional | The colour for warnings. |
appearance.colors.bad | string | optional | The colour for trouble. |
appearance.colors.card | string | optional | The tint of cards. |
appearance.font | object | optional | The page’s type. |
appearance.font.family | string | optional | the UI’s font, which must be installed or come from url |
appearance.font.url | string | optional | a stylesheet (Google Fonts, Bunny Fonts), a font file, or a local font file served by Valet |
appearance.font.mono | string | optional | The font for code and logs, which must be installed. |
appearance.font.scale | number | optional | every size of text; 1 is as designed Range 0.7 to 1.5. |
appearance.font.headingWeight | whole number | optional | The weight of headings, 100 to 900. Range 100 to 900. |
appearance.shape | object | optional | Corners, borders and shadows. |
appearance.shape.radius | number | optional | px; everything rounded follows, and 0 squares off the pills too Range 0 to 40. |
appearance.shape.border | number | optional | px around cards Range 0 to 8. |
appearance.shape.borderColor | string | optional | The colour of card borders. |
appearance.shape.shadow | none, soft, deep | optional | How much shadow cards cast. |
appearance.glass | object | optional | the frost: how much of the card’s tint shows (0..1), and how hard the page behind blurs in px. 0 turns blur off everywhere. |
appearance.glass.opacity | number | optional | the frost: how much of the card’s tint shows (0..1), and how hard the page behind blurs in px. 0 turns blur off everywhere. Range 0 to 1. |
appearance.glass.blur | number | optional | the frost: how much of the card’s tint shows (0..1), and how hard the page behind blurs in px. 0 turns blur off everywhere. Range 0 to 80. |
appearance.wallpaper | string or list of string | optional | local file path (served by Valet), image URL, colour or gradient; any of them turns cards to glass |
appearance.wallpapers | map of string or list of string | {} | a different one per tab, by the tab’s name |
appearance.rotate | number or string | optional | how long each wallpaper in a list stays |
appearance.dim | number | 0.35 | How much to darken the wallpaper, 0 to 0.9. Range 0 to 0.9. |
appearance.blur | number | 0 | the frost: how much of the card’s tint shows (0..1), and how hard the page behind blurs in px. 0 turns blur off everywhere. Range 0 to 40. |
appearance.density | compact, comfortable, spacious | comfortable | scales all spacing at once |
appearance.gap | number | optional | px between cards Range 0 to 64. |
appearance.sectionGap | number | optional | px between sections Range 0 to 120. |
appearance.maxWidth | number or string | optional | of the page: 1320px, or 1720px with a sidebar |
appearance.rowHeight | number | optional | px, the least a row in a section with sizes takes; rows otherwise fit their tallest card Range 60 to 600. |
appearance.sizes | map of object | {} | what the sizes mean, in a grid of 2, 4 or 6 columns as the screen widens: { tall: { rows: 3 }, large: { cols: 6 } } |
appearance.sizes.<name>.cols | whole number | optional | How many columns this size spans. Range 1 to 6. |
appearance.sizes.<name>.rows | whole number | optional | How many rows this size spans. Range 1 to 6. |
appearance.rim | number | optional | a lit bevel on the edge of dark glass, 0 to 1; left out, or 0, there’s none Range 0 to 1. |
appearance.motion | full, reduced, none | full | reduced: nothing moves or settles in; none: not even fades. A system’s reduce-motion always counts. |
appearance.css | string | optional | your own CSS, loaded after everything else |
appearance.cssFile | string | optional | the same from a file; css comes after it |
The top of the overview.
| Option | Type | Default | What it does |
|---|---|---|---|
hero | object | {} | The top of the overview. |
hero.greeting | boolean or string | true | text replaces “Sam’s homelab”; {name} is greeting, {greeting} is “Good evening” |
hero.status | boolean | true | the sentence under it |
hero.search | boolean | true | the ask box (the search box without an assistant) |
hero.review | boolean or end | true | the year in review link, or end: a card closing the overview; recap turns off the review itself |
hero.align | center, left, right | center | Where the greeting and ask box sit. |
hero.scope | boolean or list of string | optional | the ask box names what it can help with (“Ask about downloads, lights, what to watch…”): true works it out from the integrations present; a list says it yourself |
The assistant’s window on the page. What it knows and may do is under assistant.
| Option | Type | Default | What it does |
|---|---|---|---|
chat | object | {} | How the assistant’s window looks. |
chat.open | center, right, left, bottom, page | center | phones always get the full sheet |
chat.width | number or string | optional | The window’s width: pixels as a number, or 90%, 40rem, 80vw. |
chat.height | number or string | optional | The window’s height, in the same forms. |
chat.overview | boolean | true | false: no ask box or chips on the overview; it’s a tab or button away |
chat.placeholder | string | optional | in the ask box; {name} is the assistant’s name |
chat.suggestions | ai, fixed, off | ai | ai: written for what’s happening, no model calls with fixed (assistant.suggestions as they are) or off |
chat.followUps | list of string | optional | offered under every answer, in place of what the tool it used suggests |
chat.steps | boolean | true | the tool calls it made; approvals and result cards show either way |
chat.thinking | collapsed, open, hidden | collapsed | A model’s reasoning: folded away, open, or not shown. |
chat.send | enter, mod-enter | enter | mod-enter: ⌘/Ctrl+Enter sends and Enter starts a line |
chat.bubble | string | optional | your messages |
chat.beam | off, always, focus, thinking, once | off | a light travelling around the chat box: all the time, while typing, while it answers, or once: a lap on load, then while typing |
chat.beamColor | colorful, mono, ocean, sunset | mono | The colours of the light around the chat box. |
chat.beamStrength | number | 0.45 | how bright: a hint by default Range 0 to 1. |
chat.icon | string | optional | an icon name, or an image path or URL, on its buttons and header |
The tab buttons.
| Option | Type | Default | What it does |
|---|---|---|---|
nav | object | {} | The tab buttons. |
nav.side | left, right, top, bottom | left | where the tab buttons sit (wide screens; phones always bottom) |
nav.style | icons, labels, both | icons | wide screens; phones have room for icons |
nav.hide | list of string | [] | tabs to leave out, by name; also Overview, and the assistant’s name for its button |
nav.order | list of string | [] | tabs in this order, by name; the rest follow in layout order |
nav.size | number | 1 | Scale, where 1 is as designed. Range 0.6 to 2. |
Menubar
Section titled “Menubar”The rings in the top bar. A plain list of widget keys works too.
| Option | Type | Default | What it does |
|---|---|---|---|
menubar | list of string or object | [] | widget keys whose headline numbers sit as small rings in the top bar; or the object, to say more about the bar |
menubar.widgets | list of string | [] | Widget keys whose headline numbers sit as rings in the top bar. |
menubar.position | left, center, right | right | wide screens; below that it’s centred |
menubar.labels | boolean | true | the words on the page, for another language or your own: { overview: Accueil, thinking: Réflexion } |
menubar.clock | boolean | true | theme: the light/dark switch |
menubar.date | boolean | true | theme: the light/dark switch |
menubar.theme | boolean | true | theme: the light/dark switch |
menubar.size | number | 1 | Scale, where 1 is as designed. Range 0.6 to 2. |
Rings in the top bar for what’s happening.
| Option | Type | Default | What it does |
|---|---|---|---|
live | object | optional | Left out, nothing shows as live. |
live.activities | list of string | [] | Widget keys to show, most important first. |
live.notify | list of object | [] | short notices when things change, e.g. { widget: downloads.active, on: end, text: “{title} finished” }: start/end of a widget’s live activity, a service going down/up (status cards). {title}, {name} fill in. push: true sends it to your notification channels as well |
live.notify[].widget | string | required | The widget to watch. |
live.notify[].on | start, end, down, up | required | start and end of its live activity, or a service going down or up. |
live.notify[].text | string | optional | The notice. {title} and {name} fill in. |
live.notify[].push | boolean | false | short notices when things change, e.g. { widget: downloads.active, on: end, text: “{title} finished” }: start/end of a widget’s live activity, a service going down/up (status cards). {title}, {name} fill in. |
Format
Section titled “Format”How things are written. auto follows the browser.
| Option | Type | Default | What it does |
|---|---|---|---|
format | object | {} | How things are written. |
format.clock | auto, 12h, 24h | auto | 12 or 24 hours. |
format.date | short, long, numeric, iso | short | Sat, Oct 31 · Saturday, October 31 · 10/31/2026 · 2026-10-31 |
format.weekStart | auto, monday, sunday, saturday | auto | The first day of the week in calendars. |
format.temperature | auto, c, f | auto | converts what services report; auto leaves it alone |
format.distance | auto, km, mi | auto | Kilometres or miles. |
format.locale | string | auto | sets numbers (1.234,5) and month names |
Labels
Section titled “Labels”The words on the page. {name}, {year}, {n} and {scope} fill in.
| Option | Type | Default | What it does |
|---|---|---|---|
labels | object | {} | the words on the page, for another language or your own: { overview: Accueil, thinking: Réflexion } |
labels.homelab | string | optional | Default: “{name}‘s homelab” |
labels.yourHomelab | string | optional | Default: “Your homelab” |
labels.goodMorning | string | optional | Default: “Good morning” |
labels.goodAfternoon | string | optional | Default: “Good afternoon” |
labels.goodEvening | string | optional | Default: “Good evening” |
labels.goodNight | string | optional | Default: “Good night” |
labels.overview | string | optional | Default: “Overview” |
labels.search | string | optional | web search from ⌘K: a provider name, or a URL with {q} for the words |
labels.review | string | optional | Default: “Your {year} in review” |
labels.reviewMore | string | optional | Default: “What your homelab got up to this year, in a few screens” |
labels.reviewGo | string | optional | Default: “Play” |
labels.ask | string | optional | Default: “Ask {name}” |
labels.askAnything | string | optional | Default: “Ask {name} anything” |
labels.askAbout | string | optional | Default: “Ask about {scope}” |
labels.allOf | string | optional | Default: “All {n}” |
labels.dismiss | string | optional | Default: “Dismiss” |
labels.message | string | optional | Default: “Message {name}” |
labels.messageHint | string | optional | Default: “Message {name}, paste a link or drop a file” |
labels.chatTitle | string | optional | Default: “Talk to your homelab” |
labels.thinking | string | optional | Default: “Thinking” |
labels.writing | string | optional | Default: “Writing” |
labels.thought | string | optional | Default: “Thought” |
labels.thoughtFor | string | optional | Default: “Thought for {n}s” |
labels.steps | string | optional | Default: “{n} steps” |
labels.worked | string | optional | Default: “Worked for {n}s” |
labels.allow | string | optional | Default: “Allow” |
labels.notNow | string | optional | Default: “Not now” |
Notify
Section titled “Notify”Where “tell me when” reminders go when you’re not looking.
| Option | Type | Default | What it does |
|---|---|---|---|
notify | object | {"channels":[],"insights":false} | short notices when things change, e.g. { widget: downloads.active, on: end, text: “{title} finished” }: start/end of a widget’s live activity, a service going down/up (status cards). {title}, {name} fill in. push: true sends it to your notification channels as well |
notify.channels | list of object or object or object or object or object or object or object or object | [] | where “tell me when” reminders go when you’re not looking (ntfy, gotify, telegram, discord, slack, pushover, apprise, webhook). insights: also push the assistant’s shared-cause line. |
notify.channels[ntfy].url | URL | required | The full topic URL. |
notify.channels[ntfy].token | string | optional | An access token, if the topic needs one. |
notify.channels[gotify].url | URL | required | Your Gotify server. |
notify.channels[gotify].token | string | required | An application token. |
notify.channels[telegram].token | string | required | Your bot’s token. |
notify.channels[telegram].chat | string or number | required | The chat to send to. |
notify.channels[discord].url | URL | required | A Discord webhook URL. |
notify.channels[slack].url | URL | required | A Slack webhook URL. |
notify.channels[pushover].token | string | required | Your application’s token. |
notify.channels[pushover].user | string | required | Your user key. |
notify.channels[apprise].url | URL | required | An Apprise endpoint, for example http://apprise:8000/notify/mykey. |
notify.channels[webhook].url | URL | required | Where to send it. |
notify.channels[webhook].headers | map of string | optional | Headers to send with it. |
notify.insights | boolean | false | where “tell me when” reminders go when you’re not looking (ntfy, gotify, telegram, discord, slack, pushover, apprise, |
Sign-in, and the names Valet answers to. See sign-in and network safety.
| Option | Type | Default | What it does |
|---|---|---|---|
auth | object | {"mode":"none","sessionDays":30,"secureCookie":true,"trustNetwork":false,"proxy":{"header":"Remote-User","trusted":["127.0.0.1"]}} | Sign-in. Off by default, which leaves Valet read-only. |
auth.mode | none, local, proxy, oidc | none | like other dashboards, no sign-in by default: keep it on a private network/VPN or behind your proxy’s auth. local: built-in accounts · proxy: trust the user header from an auth proxy (Authelia, Authentik…) · none |
auth.sessionDays | whole number | 30 | how long “keep me signed in” lasts Range 1 to 365. |
auth.secureCookie | boolean | true | false only when serving over plain http |
auth.trustNetwork | boolean | false | with mode: none, Valet is read-only (no buttons, no assistant tools that add or change things) unless you say the network itself is the lock, e.g. it’s only reachable over your VPN/tailnet or behind a sign-in proxy |
auth.oidc | object | optional | mode: oidc. Register Valet at your provider with the redirect URL https://<valet>/api/auth/oidc/callback |
auth.oidc.issuer | URL | required | e.g. https://auth.example.com (it must serve /.well-known/openid-configuration) |
auth.oidc.clientId | string | required | The client id from your provider. |
auth.oidc.clientSecret | string | required | The client secret from your provider. Use ${VAR}. |
auth.oidc.name | string | SSO | on the sign-in button |
auth.oidc.claim | string | preferred_username | which claim is the person’s name here |
auth.oidc.scope | string | openid profile email | The scopes to ask for. |
auth.oidc.allow | list of string | [] | only these names or emails may sign in (empty: anyone the provider lets through) |
auth.oidc.redirectUrl | URL | optional | if Valet can’t tell its own address (an odd proxy setup) |
auth.proxy | object | {"header":"Remote-User","trusted":["127.0.0.1"]} | like other dashboards, no sign-in by default: keep it on a private network/VPN or behind your proxy’s auth. |
auth.proxy.header | string | Remote-User | the header your proxy puts the signed-in user in |
auth.proxy.trusted | list of string | ["127.0.0.1"] | proxy addresses whose header is believed |
allowedHosts | list of string | [] | the names Valet answers to besides localhost and IP addresses, e.g. [valet.example.com, “*.home.example.com”]. Anything else is refused, so a web page elsewhere can’t reach it through your browser (DNS rebinding). |
History
Section titled “History”Each widget’s headline numbers over time, so tiles can show a graph. Set to false to turn it off.
| Option | Type | Default | What it does |
|---|---|---|---|
history | false or object | {"hours":72,"every":5,"background":true} | remember each widget’s headline numbers so tiles can show a graph even for services that only report “now”. background keeps sampling while nobody has the page open (one quiet request per widget per every minutes). |
history.hours | number | 72 | How many hours of samples to keep. Range 1 to 336. |
history.every | number | 5 | minutes between samples Range 1 to 60. |
history.background | boolean | true | Keep sampling while nobody has the page open: one quiet request per widget per every minutes. |
Plugins
Section titled “Plugins”Community integrations. A plain list of npm packages works too. See the plugin toolkit.
| Option | Type | Default | What it does |
|---|---|---|---|
plugins | list of string or object | [] | community integrations: npm packages to load (files in data/integrations/ load on their own); replace lets a plugin take over a built-in type |
plugins.packages | list of string | [] | community integrations: npm packages to load (files in data/integrations/ load on their own); replace lets a plugin take over a built-in type |
plugins.replace | list of string | [] | community integrations: npm packages to load (files in data/integrations/ load on their own); replace lets a plugin take over a built-in type |
Assistant
Section titled “Assistant”Left out, there is no assistant and the page loses nothing. See models.
| Option | Type | Default | What it does |
|---|---|---|---|
assistant | object | optional | Left out, there is no assistant. |
assistant.provider | openai-compatible, anthropic | openai-compatible | any OpenAI-compatible server (Ollama, llama.cpp, vLLM, OpenRouter, OpenAI…) or Anthropic |
assistant.baseURL | string | optional | The model server. For openai-compatible, it defaults to http://localhost:11434/v1, which inside a container is the container itself. |
assistant.apiKey | string | optional | The model server’s key, if it wants one. Use ${VAR}. |
assistant.model | string | required | The model’s name on that server. |
assistant.name | string | Assistant | What the assistant is called on the page. |
assistant.instructions | string | optional | extra system prompt |
assistant.access | map of none, read, write, control | {"default":"read"} | what the assistant may use: a default plus per-integration overrides, e.g. { default: read, lights: control } |
assistant.maxSteps | whole number | 6 | Tool rounds per answer. The last is kept for answering. Range 1 to 20. |
assistant.agent | object | optional | hand the chat to another agent (Hermes, say): anything with an OpenAI-compatible API. It runs its own tools, so Valet’s access levels and approvals don’t apply to it, and a read-only page stays on Valet’s own assistant. |
assistant.agent.name | string | required | The agent’s name on the page. |
assistant.agent.baseURL | URL | required | The agent’s OpenAI-compatible address. |
assistant.agent.apiKey | string | optional | The agent’s key, if it wants one. Use ${VAR}. |
assistant.agent.model | string | required | The model name to send it. |
assistant.background | object | {"watch":true,"brief":true,"suggest":true,"perDay":100} | what it does on its own: a morning brief, and spotting a shared cause when several things break at once. These get no tools, so they can only say something. perDay caps the model calls they make. |
assistant.background.watch | boolean | true | what it does on its own: a morning brief, and spotting a shared cause when several things break at once. These get no tools, so they can only say something. perDay caps the model calls they make. |
assistant.background.brief | boolean | true | what it does on its own: a morning brief, and spotting a shared cause when several things break at once. These get no tools, so they can only say something. perDay caps the model calls they make. |
assistant.background.suggest | boolean | true | what it does on its own: a morning brief, and spotting a shared cause when several things break at once. These get no tools, so they can only say something. perDay caps the model calls they make. |
assistant.background.perDay | whole number | 100 | what it does on its own: a morning brief, and spotting a shared cause when several things break at once. These get no tools, so they can only say something. perDay caps the model calls they make. Range 1 to 500. |
assistant.suggestions | list of string | ["Is anything down?","How are my downloads doing?","What's playing on Plex?","Show me the GPU and Plex side by side"] | questions offered on an empty chat and in ⌘K |
MCP servers the assistant may use. See MCP.
| Option | Type | Default | What it does |
|---|---|---|---|
mcp | map of object | {} | MCP servers, by name. |
mcp.<name>.url | URL | required | The server’s address. |
mcp.<name>.transport | http, sse | http | How to reach it. |
mcp.<name>.headers | map of string | optional | Sent with each request. Use ${VAR} for secrets. |
mcp.<name>.access | none, read, write, control | read | read offers only read-only tools, as does write. control offers all of them, and anything not read-only asks first. |
mcp.<name>.readOnly | list of string | [] | Tool names to treat as read-only, when the server doesn’t mark them. |
mcp.<name>.name | string | optional | A friendly name, shown to the model. |
Layout
Section titled “Layout”The sections of the page, in order. See sections and layout.
| Option | Type | Default | What it does |
|---|---|---|---|
layout | list of object | optional | The sections of the page, in order. |
layout[].title | string | optional | The section’s heading, and its tab’s name unless tab says otherwise. |
layout[].tabColumns | whole number | optional | columns on its own tab, when that should differ from the overview Range 1 to 6. |
layout[].widgets | list of string or object | required | What’s in it: instance.widget, or an object with widget and the options below. |
layout[].widgets[].widget | string | required | The widget, as instance.widget. |
layout[].widgets[].size | small, wide, medium, tall, large | medium | small is its headline as a tile, wide that tile at twice the width, medium the card, tall the card over two rows, large the card across two columns of cards, with room for more. |
layout[].widgets[].every | number or string | optional | How often it refreshes while someone is looking, overriding the integration: seconds, or 30s, 5m, 1h. |
layout[].widgets[].rows | whole number | optional | how many rows of its section it spans, so smaller cards can stack beside it Range 1 to 6. |
layout[].widgets[].view | line, bars, stream, race, heatmap, ranked, pie, row, spotlight, coverflow, desk, windows, tickets, mosaic, flashback, album | optional | how to draw it: any chart for numbers, any gallery for pictures (src/shared/views.ts) |
layout[].widgets[].title | string | optional | Replaces the card’s title. |
layout[].widgets[].icon | string | optional | Replaces the card’s icon. |
layout[].widgets[].header | boolean | optional | “instance.widget”, or { widget, size } to pick a size: small (its headline as a tile), wide (that tile at twice the width), medium (the card), tall (the card over two rows), large (the card across two columns, with room for more) every overrides how often it refreshes while someone’s looking: seconds, or ”30s”, “5m”, “1h” |
layout[].widgets[].tint | string | optional | A colour for this card’s surface. |
layout[].widgets[].opacity | number | optional | How much of the tint shows, 0 to 1. Range 0 to 1. |
layout[].widgets[].blur | number | optional | How hard the page behind this card blurs, in px. Range 0 to 80. |
layout[].widgets[].background | string | optional | An image behind this card: https://… or /api/icon/name. |
layout[].columns | whole number or object | optional | a number (from tablet width up), or per screen: { phone: 1, tablet: 2, desktop: 3 } |
layout[].columns.phone | whole number | optional | Columns on a phone. Range 1 to 6. |
layout[].columns.tablet | whole number | optional | Columns from tablet width. Range 1 to 6. |
layout[].columns.desktop | whole number | optional | Columns on a wide screen. Range 1 to 6. |
layout[].gap | number | optional | px between its cards Range 0 to 64. |
layout[].display | cards, tiles, rings, media | cards | tiles: headline tiles with live graphs · rings: the same as ring gauges coloured by health · cards: full cards media: every item of the section’s media cards (streams, downloads…) as artwork tiles in one grid |
layout[].place | main, left, right | main | left / right: a sidebar beside the rest of the page (on wide screens), e.g. services as a tall list. columns then sets how many columns its list uses. |
layout[].overview | boolean | true | false: only on its own tab in the rail, not on the overview |
layout[].variant | full, minimal | full | minimal: smaller rows, just the essentials (e.g. services: icon, name, dot) |
layout[].header | boolean | true | “instance.widget”, or { widget, size } to pick a size: small (its headline as a tile), wide (that tile at twice the width), medium (the card), tall (the card over two rows), large (the card across two columns, with room for more) every overrides how often it refreshes while someone’s looking: seconds, or ”30s”, “5m”, “1h” |
layout[].heading | boolean | true | false: no section title above it on the page (it still names the tab) |
layout[].icon | string | optional | Its tab’s icon, when the first card’s isn’t right. |
layout[].tab | string | optional | which tab in the rail it belongs to (default: its title); sections can share one |
layout[].chat | boolean | false | its tab also holds the assistant, full page |
layout[].favorites | list of string | optional | on the overview, a status card shows only these services (and whatever’s down), with a link to the rest |
Integrations
Section titled “Integrations”One block per instance. The options depend on type: see integrations.
| Option | Type | Default | What it does |
|---|---|---|---|
integrations | map of object | {} | One block per instance. The key is the instance’s name, used in widget keys and in assistant.access. |
integrations.<name>.type | string | required | Which integration this is. The other options depend on it. |