feat!: prepare Homarr v2 release (#6746)

This commit is contained in:
Thomas authored and GitHub committed 2026-08-30 22:54:33 +02:00
1 parent 90c7493f15
commit 38f4b0e1f8
52 files changed
+3446 -4480

No files matched your search

@@ -40,6 +40,26 @@ Using the `PUID` and `PGID` will require you to set the correct permissions on t
Workshop service operators can find its server-only configuration in the
[Workshop operator guide](../../workshop/operator#environment-variables).
### Workshop service
These variables configure the separate Workshop service, not the Homarr application container.
| Environment Variable | Description | Default |
| ------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------- |
| `WORKSHOP_PUBLIC_ORIGIN` | Public origin used in PocketBase links and email | Required in production |
| `PB_ALLOWED_ORIGINS` | Browser origins allowed to call PocketBase | `*` for the public community |
| `WORKSHOP_IMAGE` | Workshop image used by the staging Compose file | `ghcr.io/homarr-labs/workshop:v2` |
| `WORKSHOP_EXPOSE_PORT` | Host port for the combined staging service | `8090` |
| `PB_EXPOSE_PORT` | Host port for local PocketBase development | `8090` |
| `DOCS_EXPOSE_PORT` | Host port for local documentation development | `3003` |
| `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` | Optional GitHub OAuth application credentials | - |
| `OPENROUTER_API_KEY` | Server-only upstream key that enables the Homarr Assistant provider | Provider disabled |
| `HOMARR_AI_DAILY_REQUEST_LIMIT` | Daily Assistant request units per user | `50` |
| `HOMARR_AI_GLOBAL_DAILY_REQUEST_LIMIT` | Daily Assistant request units shared by all users | `10000` |
| `HOMARR_AI_OPENROUTER_BASE_URL` | OpenRouter-compatible provider endpoint | `https://openrouter.ai/api/v1` |
| `HOMARR_AI_OPENROUTER_MODEL` | Upstream model exposed through the stable `homarr/model` alias | Selected by the Homarr team |
| `HOMARR_AI_ALLOW_INSECURE_UPSTREAM` | Allows an HTTP upstream for local provider tests; never use in production | `false` |
## Authentication
See [Single Sign-On](/docs/advanced/single-sign-on) for more informations.
@@ -2,9 +2,9 @@
<img src="https://raw.githubusercontent.com/homarr-labs/charts/refs/heads/main/charts/homarr/icon.svg" align="right" width="92" alt="homarr logo">
![Version: 8.26.0](https://img.shields.io/badge/Version-8.26.0-informational?style=flat)
![Version: 8.28.1](https://img.shields.io/badge/Version-8.28.1-informational?style=flat)
![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat)
![AppVersion: v1.74.0](https://img.shields.io/badge/AppVersion-v1.74.0-informational?style=flat)
![AppVersion: v1.76.1](https://img.shields.io/badge/AppVersion-v1.76.1-informational?style=flat)
A Helm chart to deploy homarr for Kubernetes
@@ -437,7 +437,7 @@ All available values are listed on the [artifacthub](https://artifacthub.io/pack
| httproute.rules[0].matches[0].path.value | string | `"/"` | Path value to match |
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
| image.repository | string | `"ghcr.io/homarr-labs/homarr"` | Image repository |
| image.tag | string | `"v1.74.0"` | Overrides the image tag whose default is the chart appVersion |
| image.tag | string | `"v1.76.1"` | Overrides the image tag whose default is the chart appVersion |
| imagePullSecrets | list | `[]` | Secrets for Docker registry |
| ingress.annotations | object | `{}` | Ingress annotations |
| ingress.enabled | bool | `false` | Enable ingress |
@@ -110,60 +110,3 @@ Agents can install existing widgets without regenerating them:
6. Test the installed definition, then use `board_addItem` with kind `customApi` and the returned definition ID.
Homarr encrypts the plaintext immediately. Homarr does not return the plaintext.
## Evaluate the copied prompt
Homarr maintainers can run the same self-contained prompt that the workbench copies through the live generation-quality suite:
```sh
OPENROUTER_API_KEY=... pnpm --filter @homarr/custom-widgets test:ai
```
The manifest suite covers nine complex and public-API prompts. Its deterministic fixture-backed cases include a fake
homelab health API, CoinMarketCap's keyless public API, Bored, Agify, and a multi-request Pokédex. Before the configured
model judges a result, the candidate must pass Homarr's real clipboard parser, canonical schema, and deterministic
checks for documented endpoints, authentication, request triggers, query bindings, response fields, and essential
runtime components.
To exercise the real Assistant lifecycle instead of accepting a returned JSON block, run:
```sh
OPENROUTER_API_KEY=... pnpm --filter @homarr/custom-widgets test:assistant-ai
```
The same suites can target the Homarr provider with a Workshop PocketBase token instead of an OpenRouter key:
```sh
AI_PROVIDER_BASE_URL=https://homarr.dev/api/ai/v1 \
AI_PROVIDER_API_KEY=<workshop-token> \
AI_PROVIDER_MODEL=homarr/model \
pnpm --filter @homarr/custom-widgets test:assistant-ai
```
`AI_PROVIDER_JUDGE_MODEL` can select a separate judge when the configured endpoint offers one. The Homarr provider
offers exactly one model, so generation and review both use `homarr/model`.
The assistant test exposes the same lazy skill, live schema, component catalog, targeted component references,
validation, preview-query, and create-from-preview tools as the product. It refuses to save a definition that was not
validated exactly or whose preview queries were not all tested. Multi-request fixtures return a different payload for
each documented path and require manual-query parameters. Use `--case=pokedex`, `--case=coinmarketcap-keyless`,
`--case=bored-activity`, or `--case=agify-name` for a focused run.
Both suites use the configured provider models for generation and harsh review. They save tool traces, validated
manifests, raw reviews, and detailed category scores under
`packages/custom-widgets/.ai-evaluations/`. These live tests are maintainer-invoked rather than CI-required because
they consume a configured provider account and exercise a nondeterministic model. The repository's ordinary tests
cover the lifecycle enforcement, fixture routing, score calculation, and failure behavior without network access.
Each scenario stops as soon as it passes and has a hard limit of ten generation attempts. Override the endpoint,
models, or per-scenario limit with `AI_PROVIDER_BASE_URL`, `AI_PROVIDER_API_KEY`, `AI_PROVIDER_MODEL`,
`AI_PROVIDER_JUDGE_MODEL`, and
`CUSTOM_WIDGET_AI_MAX_LOOPS`. Generated artifacts are local and ignored by Git; promote a reviewed, validated result
into the bundled-widget source deliberately.
The reference API check uses a real PokeAPI list/detail request and a read-only Portainer container query when `PORTAINER_BASE_URL` and `PORTAINER_API_KEY` are set. Use `pnpm --filter @homarr/custom-widgets test:reference-apis -- --pokeapi-only` for the public PokeAPI check alone. To exercise start, restart, and stop against a disposable container, also set `PORTAINER_TEST_CONTAINER_ID` and the explicit safeguard `PORTAINER_ALLOW_MUTATIONS=1`; the script restores the container's original running state.
Use `pnpm --filter @homarr/custom-widgets test:reference-apis -- --public-ai-apis-only` to verify the live Bored and
CoinMarketCap response shapes used by the assistant fixtures. The same command also verifies Agify when
`AGIFY_API_KEY` is configured; otherwise it reports the authenticated check as skipped without weakening the
deterministic Agify fixture tests.
@@ -21,7 +21,8 @@ export const completeManifest = `{
:::warning Experimental feature
This feature is experimental and will probably change. If you encounter bugs, need more capabilities, or find something confusing, tell us in the [official discussion thread](https://github.com/homarr-labs/homarr/discussions/categories/custom-widgets). Your feedback directly shapes what comes next. Happy hacking!
This feature is experimental and may change. Share feedback in the
[Custom Widgets discussion](https://github.com/homarr-labs/homarr/discussions/categories/custom-widgets).
:::
@@ -84,54 +85,35 @@ Mutations remain authenticated and enforce each request's view, modify, or full
- [Use MCP and the skills.sh skill](./agent-authoring).
- [Correct problems](./troubleshooting).
The workbench keeps one continuous layout with visual source, request, and option editors. Advanced raw JSON remains available for experts and AI paste. Invalid values stay inline and disable both preview and Save/Create until they are corrected.
The workbench provides visual source, request, and option editors, with raw JSON available for advanced use. Invalid
values remain marked and disable preview and saving until corrected.
Exports, imports, the official Workshop client, and MCP validate the same schema. PocketBase stores community
submissions without interpreting their content, and Homarr validates them before installation. Widgets saved in the v1
format remain visible to administrators with a **Migration required** badge after upgrading. Use **Copy migration
prompt**, give the redacted prompt to an LLM, copy its single complete JSON block, then select **Paste migrated widget**
from that widget's action menu. Homarr reads the `template` value into the JSX editor automatically. The prompt keeps safe
request metadata but deliberately omits the v1 display configuration because legacy free-text and JSX can contain
hard-coded credentials. Add a credential-free description of the widget's visible layout and behavior when sending the
prompt.
Exports, imports, Workshop, and MCP validate the same schema. Homarr also validates community content before
installation.
Homarr keeps the original v1 definition and encrypted secret as a compatibility backup even after the validated v2
replacement is created. The v2 record uses the same stable ID and takes precedence. The archived v1 copy stays hidden
while the replacement exists; deleting the v2 record surfaces the v1 entry as migration-required again. Automatic
legacy deletion is intentionally unavailable during v2 stabilization. Credentials are never included in prompts,
logs, or exports.
For administrators adding widgets while editing a board, the Custom Widgets section includes **Import from
Workshop**. Non-administrators do not see the Custom Widget picker or Workshop actions. Selecting **Install widget**
opens a two-step modal flow: first review the Workshop entry and its complete source in the read-only editor, then
confirm API sources and credentials. After import, the new definition appears in the picker. Its tile waits for the board
to be saved before server requests run.
Legacy definitions show a **Migration required** badge. Use **Copy migration prompt**, describe the visible layout
without secrets, and import the returned v2 JSON with **Paste migrated widget**. Homarr preserves the v1 definition and
encrypted secret for rollback; credentials never appear in prompts, logs, or exports.
## Import and Workshop
The install flow always links to the corresponding public Workshop entry under the configured Workshop origin. Import confirmation shows origins, authentication modes, network scopes, methods, actions, and permissions. Every source has a suggested URL, but private and loopback sources must be confirmed or replaced for the current Homarr installation. The same screen accepts API keys, tokens, usernames, and passwords; credentials remain optional at install time and missing values stay visible on the Custom Widgets page.
For example, installing **Tautulli Activity** asks for both the user's Tautulli URL and API key. A stable public source such as PokéAPI keeps its published URL without creating unnecessary setup work.
Administrators can select **Import from Workshop** while adding a board item. Review the complete source and requested
permissions, then confirm deployment-specific URLs and credentials. Private and loopback URLs must be confirmed or
replaced for the current Homarr installation.
Workshop publishes technically valid widgets immediately; users can vote or report them, and Workshop moderators can
delete submissions. The report form expands within the Workshop detail page instead of interrupting the review with a
dialog. Community moderation is separate from Homarr administration and is not a security boundary.
Publishers should replace personal private-network hosts with a clear service placeholder before publishing. Homarr
performs definitive validation again at install time.
delete submissions. Community moderation is separate from Homarr administration and is not a security boundary. Review
the source before installation and replace personal private-network hosts before publishing.
Connected agents can search, inspect, and install Workshop widgets with `customWidget_workshopSearch`, `customWidget_workshopGet`, and `customWidget_workshopInstall`. After installation they can set a source URL and credentials with `customWidget_sourceConfigure`, or create a secure user-facing setup URL with `customWidget_configurationRequestUser`. Secret values are never returned through MCP.
These tools are only exposed to API keys owned by Homarr administrators. Opening and completing a source-configuration
URL also requires the same authenticated administrator; the URL is short-lived, single-use, and redacted.
Connected agents can also search and install Workshop widgets. See [agent authoring](./agent-authoring) for the secure
configuration flow; secret values are never returned through MCP.
## Reliability and rollback
Custom Widget response caches and action invalidation use shared Redis state, so multiple Homarr replicas see the same
versioned cache generation. Deployments must point every replica at the same Redis service.
Multiple Homarr replicas must use the same Redis service so Custom Widget response caches invalidate consistently.
Before upgrading, back up the database and `SECRET_ENCRYPTION_KEY`; a v1 binary can still read preserved v1 tables
during application rollback. Widgets created only in v2 may be unavailable to the rolled-back binary, but do not
replace the v1 backup.
Before upgrading, back up the database and `SECRET_ENCRYPTION_KEY`. A v1 rollback can read preserved v1 definitions,
but widgets created only in v2 may be unavailable.
## Bundled examples
@@ -39,7 +39,7 @@ In the configuration section you can select between two options.
#### Search template
The template must be a valid URL that incldues the `%s` placeholder.
The template must be a valid URL that includes the `%s` placeholder.
Homarr will replace the `%s` with the term that the user entered.
##### Examples
+10 -43
View File
@@ -11,14 +11,11 @@ import assistantWidgetImage from "../../management/img/assistant-widget.png";
<WidgetHeader widget={assistantWidget} categories={["Productivity"]} />
The Assistant widget brings the same conversation available from the user menu and command menu onto a
board. Messages, conversations, tool calls, approvals, files, mentions, model controls, reasoning controls,
usage details, and response actions remain synchronized with the app-wide assistant.
The Assistant widget puts the app-wide assistant on a board. It uses the same active conversation as the user-menu and
command-menu surfaces, so messages and approvals stay synchronized.
The assistant must be enabled by an administrator in **Management → Assistant**. Signed-out users and users
on an instance without an enabled model see an unavailable state instead of a broken composer. Signed-out users see
**Sign in**; a configured provider without a saved model or with **Enable Assistant** off remains unavailable until
the administrator saves a working connection, selects a model, and enables the assistant.
An administrator must first configure and enable Assistant in **Management → Assistant**. Signed-out users are prompted
to sign in, while an incomplete or disabled configuration shows an unavailable state.
<img
src={assistantWidgetImage}
@@ -26,44 +23,14 @@ the administrator saves a working connection, selects a model, and enables the a
style={{ maxWidth: "100%" }}
/>
The current preview capture labels the board-embedded surface: its header exposes conversation history, refresh,
automatic approvals, and new-conversation controls, while the footer exposes the model, thinking mode, and composer.
### Adding the widget
<AddingWidget />
The default widget size is ten columns by four rows and automatically fits boards with fewer columns. Controls
and labels condense as the widget becomes narrower, while the conversation remains scrollable and usable on
touch devices. When a board layout does not provide enough height for conversation controls, the widget shows
a compact **Open assistant** action instead of squeezing an unusable composer into the card. Increase its height
in that layout to keep the complete conversation inline.
The default size is ten columns by four rows and shrinks to fit narrower boards. Short layouts show an **Open
assistant** action instead of compressing the conversation controls; increase the widget height to keep the complete
conversation inline.
The widget follows whichever conversation is active in the user-menu panel, `Shift + A`, or the command menu. This
keeps every assistant entry point synchronized without storing a user's private conversation details in board settings.
### Conversation features
- Create, switch, rename, and delete conversations.
- Send text and supported file or image attachments by picker, drag and drop, or image paste.
- Mention permitted apps, integrations, boards, widgets, and tools with `@`.
- Use `/` shortcuts for common dashboard tasks.
- Review tool calls, approve mutations, and answer structured assistant questions inline.
- Answer pending structured questions from the dock above the composer without scrolling through the conversation.
- Enable conversation-scoped automatic approvals from the shield control to approve protected mutations, use presented form details, and choose answers categorized as affirmative without individual clicks.
- Edit user messages and copy, regenerate, branch, quote, export, or rate responses.
- Inspect syntax-highlighted Markdown, sources, generated files, tool results, streaming chain-of-thought,
cumulative conversation usage, per-turn token and context use, timing, speed, provider routing, and cost.
- Change the model and reasoning effort for the active conversation. A model choice is saved per persisted thread,
does not change the administrator's default, and is restored when that thread is selected again; a new thread
starts from the configured default.
- Refresh the current board from the conversation header. Approved mutations also refresh visible board data automatically.
The expanded assistant and board widget use the same persisted thread runtime. A response continues in the
background when the widget leaves the viewport or the expanded panel is minimized. Each visible surface keeps an
independent draft, attachments, selection, and focus, so typing in a board widget cannot move or overwrite the
floating panel composer.
Automatic approvals are off by default, reset when switching conversations, and never select a neutral choice. A
structured question shows **Other** only when the tool allows it (`allowOther` is not `false`); selecting **Other**
opens a manual freeform response even when automatic approvals are enabled.
Responses continue when the widget leaves the viewport. Drafts and attachments remain local to each visible Assistant
surface, and private conversation data is never stored in board settings. Normal Homarr permissions and approval rules
still apply to every action.
+7 -22
View File
@@ -13,35 +13,20 @@ import { timerWidget } from ".";
<WidgetHeader widget={timerWidget} categories={["Productivity", "Time"]} />
The Timer / Pomodoro widget runs entirely in your browser and does not require an integration. The compact widget
always keeps its start or pause button and reset button visible. A running timer uses a saved deadline, so reopening
the board or returning to a background tab restores the correct remaining time instead of restarting the countdown.
The Timer / Pomodoro widget runs in your browser and does not require an integration. A running timer uses a saved
deadline, so reopening the board or returning from a background tab restores the correct remaining time.
Timer mode uses one configured duration. Pomodoro mode starts with a focus session, follows most completed focus
sessions with a short break, and uses a long break after the configured number of focus sessions. Automatic break and
focus starts are separate options and are disabled by default. When automatic starts are enabled, elapsed phases are
reconciled from their original deadlines after the board returns to the foreground.
Advanced view shows the current sequence, configured durations, and local alert preferences. Browser notifications are
requested only when enabled. Notification and sound preferences, live progress, deadlines, and cycle position are
stored locally using the board and widget identifiers. They are separate for each browser or device, are available to
guest viewers, and are removed by clearing site data. Widget previews are ephemeral and do not write local timer state.
Advanced view shows the sequence, durations, and local alert preferences. Sounds are enabled by default, while browser
notifications are requested only after you enable them. Progress and alert preferences are stored per board and widget
in the current browser; clearing site data resets them, and widget previews do not persist timer state.
Timer sounds are enabled by default and use the same low-volume Tiks sound system as onboarding: start and pause use
rising and falling soft-theme cues, reset uses a short click, and phase completion uses the glass theme's resonant
notification ding. You can disable them in advanced view. The cues respect the device's reduced-motion preference and
supplement the visible timer state rather than replacing it.
When automatic start is disabled, a completed phase labels the next step as ready and flashes the widget edges with the
board's secondary color. The border glow and completion chime run together as a five-beat alarm burst at
300-millisecond intervals, then stop. Reduced-motion and forced-colors modes replace the flash with a static border.
The current phase stays pinned to the compact widget's top-left corner and wraps when needed. Timer text and controls
use the dashboard theme's responsive size scale, with secondary details hidden when the widget preview is narrow or
short.
Alerts can fire while Homarr is open. Browser and operating-system notification settings may still suppress them.
The timer's mode, durations, session count, and automatic-start behavior are permanent board settings and are edited
from the widget context menu.
Alerts can fire while Homarr is open, but browser or operating-system settings may suppress notifications. Reduced-motion
and forced-colors preferences replace the animated completion effect with a static border.
<WidgetInteractionGuide />
+28 -15
View File
@@ -92,10 +92,6 @@ pnpm test:workshop-live-provider
This optional check consumes one provider request. The test client needs neither the OpenRouter key nor the private
upstream model ID. Do not run it in CI or with a normal user's Workshop token.
The Custom Widget AI tests can use this proxy. Set `AI_PROVIDER_BASE_URL` to the `/api/ai/v1` endpoint and
`AI_PROVIDER_API_KEY` to a temporary Workshop user token. See
[Agent authoring](../management/custom-widgets/agent-authoring#evaluate-the-copied-prompt) for the command.
## Staging and demo deployment
Use `apps/workshop/docker-compose.staging.yml` with the published `ghcr.io/homarr-labs/workshop:v2` image for a v2
@@ -131,17 +127,34 @@ GitHub OAuth requires both credentials and the callback `https://v2.preview.homa
after changing environment values, then verify the health endpoint, `/workshop`, a public listing request, GitHub
sign-in, and an installation from the Homarr preview.
Back up application and PocketBase data before deployment. Publish the combined image by immutable digest, test the
exact digest in staging, rehearse application rollback and PocketBase restore, then promote that same digest.
## Backup and restore
## Client architecture
PocketBase data lives in `/pb_data`. Stop writes and copy it to a new, empty backup directory:
Workshop access is centralized in `@homarr/workshop/backend`. It exports the typed `WorkshopBackend`, its typed
PocketBase instance, stable TanStack Query option factories, and the `useWorkshop…` query and mutation hooks used by
Homarr. The documentation site uses the same backend rather than maintaining a second API contract.
```sh
docker compose --env-file apps/workshop/.env.staging \
-f apps/workshop/docker-compose.staging.yml stop workshop
docker compose --env-file apps/workshop/.env.staging \
-f apps/workshop/docker-compose.staging.yml cp workshop:/pb_data/. ./workshop-backup
docker compose --env-file apps/workshop/.env.staging \
-f apps/workshop/docker-compose.staging.yml start workshop
```
Listing fields derived by the `workshop_listings` view—such as submission type, score, and counts—are normalized by the
shared client. Healthy deployments keep filtering, sorting, and pagination in PocketBase. If a persistent development
volume still has an older view that rejects those fields, the client retries without them and applies the same
operations locally; it can also fall back to the base submissions collection. Production deployments should
nevertheless apply the current migration so the view exposes the complete metadata.
Restore only after preserving the current volume and confirming the backup contains `data.db`:
```sh
test -f ./workshop-backup/data.db
docker compose --env-file apps/workshop/.env.staging \
-f apps/workshop/docker-compose.staging.yml stop workshop
docker compose --env-file apps/workshop/.env.staging \
-f apps/workshop/docker-compose.staging.yml run --rm --no-deps --entrypoint sh workshop \
-c 'find /pb_data -mindepth 1 -maxdepth 1 -exec rm -rf {} +'
docker compose --env-file apps/workshop/.env.staging \
-f apps/workshop/docker-compose.staging.yml cp ./workshop-backup/. workshop:/pb_data
docker compose --env-file apps/workshop/.env.staging \
-f apps/workshop/docker-compose.staging.yml start workshop
curl --fail http://127.0.0.1:8090/api/health
```
The restore empties the selected volume. Rehearse it in staging, then verify sign-in and one installation. Publish by
immutable digest, validate that exact digest in staging, and promote the same digest.