Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf60c9e4cf | ||
|
|
a29ec26c27 | ||
|
|
9ff1019a3d | ||
|
|
84058544a4 | ||
|
|
29c94be821 | ||
|
|
48042a08ff | ||
|
|
9a52a035aa | ||
|
|
505032bd21 | ||
|
|
19aa92ec25 | ||
|
|
de75c5f29d | ||
|
|
8f152f1929 | ||
|
|
6cca757493 | ||
|
|
1102ad8342 | ||
|
|
7c00c18a42 | ||
|
|
ea3dac752f | ||
|
|
f994d665d2 | ||
|
|
3358b6337f | ||
|
|
61d3fd5f08 | ||
|
|
3aac106156 | ||
|
|
9d857377c1 | ||
|
|
923905c529 | ||
|
|
a4a903d7c3 | ||
|
|
925b0fb801 | ||
|
|
6db26a0d99 | ||
|
|
bbcbd031fe | ||
|
|
7024a57f56 | ||
|
|
5121356113 | ||
|
|
4f2ab59345 | ||
|
|
73bf68c9a8 | ||
|
|
dea1de5fee | ||
|
|
2bd538e685 | ||
|
|
f0c39ea0d2 | ||
|
|
8a0cf15bf7 | ||
|
|
f1c898efe2 | ||
|
|
724dee55c5 | ||
|
|
50a3bcce69 | ||
|
|
ca7be8f3ad | ||
|
|
134ef48848 | ||
|
|
98aa6f01f2 | ||
|
|
4c88030b0f | ||
|
|
0c11c2785e | ||
|
|
96a6926b2b | ||
|
|
f89487231f | ||
|
|
a62d11b5cd | ||
|
|
28e242417d | ||
|
|
0ffa95c505 | ||
|
|
b708f687fa | ||
|
|
4fd9b04c61 | ||
|
|
378332c8da | ||
|
|
3d9b8e8ae6 | ||
|
|
f7b194fc9f | ||
|
|
eb20b97d5f |
No files matched your search
@@ -32,24 +32,23 @@ Import only from an exported package entrypoint. Do not deep-import `@homarr/com
|
||||
|
||||
## Root exports
|
||||
|
||||
| Area | Public exports | Purpose |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||
| Objects | `objectKeys`, `objectEntries`, `hashObjectBase64` | Typed key/entry iteration and stable query-key hashing |
|
||||
| Strings | `capitalize`, `isNullOrWhitespace`, `bestMatch`, `normalizeImageName`, `getImageMatchRank` | Text normalization and matching |
|
||||
| Cookies | `parseCookies`, `setClientCookie` | Cookie parsing and browser cookie serialization |
|
||||
| Arrays | `splitToNChunks`, `splitToChunksWithNItems` | Split by number of groups or maximum group size |
|
||||
| Dates | `formatDuration`, `toValidDate`, `isDateWithin` | Duration formatting, safe date coercion, relative-window checks |
|
||||
| Timing | `Stopwatch` | Elapsed-time measurement and formatting |
|
||||
| React hooks | `useTimeAgo`, `useIntegrationConnected` | Relative time and integration freshness state |
|
||||
| URLs | `removeTrailingSlash`, `extractBaseUrlFromHeaders`, `getPortFromUrl`, `isAbsoluteUrl`, `SAFE_NEW_TAB_REL`, `getSafeApplicationUrl`, `getSafeAppHref` | URL normalization, safe navigation, ports, and new-tab policy |
|
||||
| Numbers and bytes | `formatNumber`, `randomInt`, `formatBytes`, `formatBytesPair`, `formatByteRate`, `metricToImperial`, `imperialToMetric` | Compact numbers, byte displays, rates, and unit conversion |
|
||||
| Legacy bytes | `humanFileSize` | Deprecated; use the `formatBytes*` helpers in new code |
|
||||
| Byte types | `ByteUnitSystem`, `FormatBytesOptions` | Options for byte formatting |
|
||||
| Errors | `extractErrorMessage`, `FlattenError` | Unknown-error messages and flattenable domain errors |
|
||||
| Theme | `getMantineColor` | Resolve a Mantine color and shade |
|
||||
| Functions | `isFunction` | Unknown-value function guard |
|
||||
| Grid | `GridAlgorithmItem`, `generateResponsiveGridFor` | Responsive nested-section grid placement |
|
||||
| IDs | `createId` | CUID2 ID creation |
|
||||
| Area | Public exports | Purpose |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
||||
| Objects | `objectKeys`, `objectEntries`, `isRecord`, `hashObjectBase64` | Typed key/entry iteration, record guards, and stable query-key hashing |
|
||||
| Strings | `capitalize`, `isNullOrWhitespace`, `bestMatch`, `normalizeImageName`, `getImageMatchRank` | Text normalization and matching |
|
||||
| Cookies | `parseCookies`, `setClientCookie` | Cookie parsing and browser cookie serialization |
|
||||
| Arrays | `splitToNChunks`, `splitToChunksWithNItems` | Split by number of groups or maximum group size |
|
||||
| Dates | `formatDuration`, `toValidDate`, `isDateWithin` | Duration formatting, safe date coercion, relative-window checks |
|
||||
| Timing | `Stopwatch` | Elapsed-time measurement and formatting |
|
||||
| React hooks | `useTimeAgo`, `useIntegrationConnected` | Relative time and integration freshness state |
|
||||
| URLs | `removeTrailingSlash`, `extractBaseUrlFromHeaders`, `getPortFromUrl`, `isAbsoluteUrl`, `SAFE_NEW_TAB_REL`, `getSafeApplicationUrl`, `getSafeAppHref` | URL normalization, safe navigation, ports, and new-tab policy |
|
||||
| Numbers and bytes | `formatNumber`, `formatBytes`, `formatBytesPair`, `formatByteRate`, `formatBitRate`, `defaultByteUnitSystem`, `metricToImperial` | Compact numbers, byte displays, byte/bit rates, and unit conversion |
|
||||
| Byte types | `ByteUnitSystem`, `FormatBytesOptions`, `FormatBitRateOptions` | Options for byte and bit-rate formatting |
|
||||
| Errors | `extractErrorMessage`, `FlattenError` | Unknown-error messages and flattenable domain errors |
|
||||
| Theme | `getMantineColor` | Resolve a Mantine color and shade |
|
||||
| Functions | `isFunction` | Unknown-value function guard |
|
||||
| Grid | `GridAlgorithmItem`, `generateResponsiveGridFor` | Responsive nested-section grid placement |
|
||||
| IDs | `createId` | CUID2 ID creation |
|
||||
|
||||
The root deliberately does not re-export `types`, `server`, `client`, or `env`; use their named entrypoints.
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
---
|
||||
name: homarr-custom-widget
|
||||
description: Author, validate, preview, test, install, or configure API-backed Homarr Custom JSX v2 widgets.
|
||||
description: Author, validate, preview, test, install, or configure static or API-backed Homarr Custom JSX v2 widgets.
|
||||
---
|
||||
|
||||
# Homarr Custom Widget
|
||||
|
||||
Author one widget or a coordinated set of widgets. Load only needed release-matched context. Run lifecycle tools alone; independent reads may run together. For a set, research once and finish each widget's validation, evidence, and persistence before the next.
|
||||
Author one widget or a coordinated set. Load release-matched context on demand. Run lifecycle tools alone; independent reads may run together. Finish each widget's validation, evidence, and persistence before the next.
|
||||
|
||||
1. Read primary API documentation. Use web search when documentation is not supplied or may have changed.
|
||||
2. Create credential-free definitions with keyed `sources`, `requests`, optional `options`, and safe JSX `template`.
|
||||
1. For connected widgets, read primary API documentation; use web search when it is missing or may have changed.
|
||||
2. Create credential-free definitions with keyed `sources`, `requests`, optional `options`, and safe JSX `template`. Static widgets use empty `sources` and `requests`; an API is optional.
|
||||
3. While drafting, use `customWidget_validateTemplate` for focused JSX diagnostics without resending the manifest.
|
||||
4. Send the definition once to `customWidget_previewCreate` and test its queries/actions. For a JSX-only fix, validate, call `customWidget_previewReviseTemplate` with its session, and retest; it inherits the manifest and resets evidence. Create a preview only for source/request/option changes.
|
||||
5. Configure deployment-specific source URLs and credentials through Homarr; never repeat plaintext.
|
||||
6. Persist each exact final tested preview with `customWidget_createFromPreview`. Do not resend a large definition through `customWidget_create` when a preview session is available.
|
||||
|
||||
Treat a supplied sample or successful preview response as the binding contract. Render every core requested field, guard optional arrays and nested values before indexing, and do not silently drop returned items. Humanize numeric enums with indexed literal label arrays, omit absent numeric values instead of inventing zero, and label timestamp timezones. Give recoverable load errors and empty states a clear refresh or retry path.
|
||||
Use supplied samples or preview responses as the data contract. Render requested fields and all returned items; guard optional nested values. Humanize numeric enums with label arrays, omit absent numbers, and label timestamp timezones. Give load errors and empty states a refresh or retry path.
|
||||
|
||||
Use `{option:name}` or `$option` for saved options. Use `{param:name}` or `$param` for values supplied by `SubFetch`, `ActionButton`, or `ToggleSwitch`. Load queries cannot use invocation parameters. Render load queries from `data` and `status` with `RefreshButton`; reserve `SubFetch` for manual parameterized queries. Templates read `data`, `status`, `options`, and temporary `inputs`.
|
||||
|
||||
@@ -26,7 +26,7 @@ Plan capabilities. Use one `customWidget_findComponents` search per job. Batch s
|
||||
|
||||
Do not simplify a useful workflow merely because the template is interpreted. Freely compose any supported installed components with multiple sources, requests, options, `choicesFrom`, bound filters, charts, responsive detail areas, manual queries, and safe actions when they improve the user's job. Complexity must remain purposeful rather than decorative.
|
||||
|
||||
Polish with a divided list or responsive media grid. Make one metric/action asymmetrically primary; keep headers compact, identity/state clear, metadata quiet, and one badge. At base artwork fills its row and caps above xs; never combine full-width media and nowrap.
|
||||
Use divided lists or responsive media grids, one primary metric/action, compact headers, quiet metadata, and one badge. Full-width artwork wraps at base and caps above xs.
|
||||
|
||||
Make initial states actionable and wrap variable labels/values on narrow tiles. Do not use an unlabeled decorative icon as an empty state.
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ interface HomarrCustomWidgetV2 {
|
||||
}
|
||||
```
|
||||
|
||||
The object key `default` is the required source ID; `default` is not a property on a source. Source properties are `name?`, `baseUrl`, `networkScope`, and `auth?`:
|
||||
Static widgets use empty `sources` and `requests`. Each request must resolve to a defined source; omitting `source` uses `default` only when that ID exists. Source properties are `name?`, `baseUrl`, `networkScope`, and `auth?`:
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Security
|
||||
|
||||
Private access includes LAN, Docker DNS, and Tailscale CGNAT addresses. Loopback means the Homarr server or container, not the viewer's browser.
|
||||
|
||||
All requests use Homarr's protected server executor. Source origin, network scope, DNS, redirects, SSRF, rate limits, permissions, size limits, timeouts, and encrypted credential injection remain enforced.
|
||||
|
||||
The JSX interpreter blocks imports, hooks, refs, raw event callbacks, browser requests, eval, arbitrary functions, prototype access, unsafe URLs, global CSS escape, arbitrary portals, bigint, statement blocks, IIFEs, and recursion. Regex literals must be bounded and reject backreferences, lookbehind, nested quantifiers, excessive length, and unsupported flags.
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
# Since .env is gitignored, you can use .env.example to build a new `.env` file when you clone the repo.
|
||||
# Keep this file up-to-date when you add new variables to \`.env\`.
|
||||
# Keep this file up-to-date when you add new variables to `.env`.
|
||||
|
||||
# This file will be committed to version control, so make sure not to have any secrets in it.
|
||||
# If you are cloning this repo, create a copy of this file named `.env` and populate it with your secrets.
|
||||
|
||||
# The below secret is not used anywhere but required for Auth.js (Would encrypt JWTs and Mail hashes, both not used)
|
||||
AUTH_SECRET="supersecret"
|
||||
# Generate two different 64-character hex values with `openssl rand -hex 32` before starting Homarr.
|
||||
# Never reuse these values or commit the populated `.env` file.
|
||||
AUTH_SECRET=
|
||||
|
||||
# The below secret is used to encrypt integration secrets in the database.
|
||||
# It should be a 32-byte string, generated by running `openssl rand -hex 32` on Unix
|
||||
# or starting the project without any (which will show a randomly generated one).
|
||||
SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
# Homarr will reject an empty or invalid value and print a randomly generated candidate.
|
||||
SECRET_ENCRYPTION_KEY=
|
||||
|
||||
LOG_LEVEL='info'
|
||||
|
||||
# This is how you can use the sqlite driver:
|
||||
DB_DRIVER='better-sqlite3'
|
||||
DB_URL='FULL_PATH_TO_YOUR_SQLITE_DB_FILE'
|
||||
# Safe local source-development path. SQLite files are ignored by Git.
|
||||
# Container images use /appdata/db/db.sqlite by default instead.
|
||||
DB_URL='./db.sqlite'
|
||||
|
||||
# Those are the two ways to use the mysql2 driver:
|
||||
# 1. Using the URL format:
|
||||
@@ -63,6 +65,8 @@ UNSAFE_ENABLE_MOCK_INTEGRATION=true
|
||||
|
||||
# Public URL contract. Values must be absolute HTTP(S) URLs without credentials,
|
||||
# query strings, or fragments. Trailing slashes are normalized at startup.
|
||||
# Public Homarr origin used by MCP OAuth metadata and redirects behind proxies.
|
||||
# BASE_URL=https://homarr.example.com
|
||||
HOMARR_WEBSITE_URL=https://homarr.dev
|
||||
# PocketBase API used by Homarr and by the standalone documentation site.
|
||||
# Defaults to HOMARR_WEBSITE_URL.
|
||||
@@ -89,3 +93,9 @@ HOMARR_WEBSITE_URL=https://homarr.dev
|
||||
# Whether demo mode is read-only. Defaults to true (the public demo blocks all mutations).
|
||||
# Set to false for writable preview deployments so the demo user can modify the dashboard.
|
||||
# DEMO_READ_ONLY=false
|
||||
|
||||
# Persistent trusted-widget build cache; activated artifacts are also retained in the database.
|
||||
# Defaults to /appdata/custom-widgets in production and ./appdata/custom-widgets in development.
|
||||
# CUSTOM_WIDGET_PACKAGE_PATH=/appdata/custom-widgets
|
||||
# Maximum concurrently active trusted widget server artifacts per Homarr process.
|
||||
# CUSTOM_WIDGET_MAX_SERVER_PROCESSES=16
|
||||
@@ -4,14 +4,14 @@
|
||||
<h3>Homarr</h3>
|
||||
</div>
|
||||
|
||||
**Thank you for your contribution. Please ensure that your pull request meets the following pull request:**
|
||||
**Thank you for your contribution. Please ensure that your pull request meets the following requirements:**
|
||||
|
||||
- [ ] Builds without warnings or errors (`pnpm build`, autofix with `pnpm format:fix`)
|
||||
- [ ] Pull request targets `dev` branch
|
||||
- [ ] Relevant checks pass without warnings or errors
|
||||
- [ ] Pull request targets the intended base branch (`dev` by default; `release/v2` for v2 release work)
|
||||
- [ ] Commits follow the [conventional commits guideline](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
- [ ] No shorthand variable names are used (eg. `x`, `y`, `i` or any abbrevation)
|
||||
- [ ] Documentation is up to date. Create a pull request [here](https://github.com/homarr-labs/documentation/).
|
||||
- [ ] When using AI; No temp files are checked in, the code style follows the rest of the project
|
||||
- [ ] No shorthand variable names are used (for example, `x`, `y`, `i`, or abbreviations)
|
||||
- [ ] User-facing changes include matching documentation updates under `apps/docs`
|
||||
- [ ] When using AI, no temporary files are checked in and the code style follows the rest of the project
|
||||
|
||||
**When contributing a new integration or widget:**
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||
client-id: ${{ vars.RENOVATE_APPROVE_CLIENT_ID }}
|
||||
permission-pull-requests: write # required to enable auto-merge
|
||||
|
||||
- name: Install GitHub CLI
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||
client-id: ${{ vars.RENOVATE_APPROVE_CLIENT_ID }}
|
||||
permission-pull-requests: write # required to approve a pull request and enable auto-merge
|
||||
|
||||
- name: Install GitHub CLI
|
||||
|
||||
@@ -97,17 +97,16 @@ jobs:
|
||||
|
||||
- name: Validate Custom Widget bundle budget
|
||||
run: |
|
||||
pnpm --filter nextjs build
|
||||
pnpm turbo run build --filter=@homarr/nextjs
|
||||
pnpm check:custom-widget-bundle
|
||||
|
||||
- name: Run unit tests
|
||||
run: pnpm test
|
||||
|
||||
container:
|
||||
name: Container and E2E
|
||||
name: Container build
|
||||
# Same-repository previews already build amd64; only build a fallback when no digest is available.
|
||||
needs: [fast-gate, preview-amd64]
|
||||
if: |
|
||||
!cancelled() && needs.fast-gate.result == 'success' &&
|
||||
needs.preview-amd64.result != 'failure' && needs.preview-amd64.outputs.digest == '' &&
|
||||
(
|
||||
github.event_name == 'merge_group' ||
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/release/v2') ||
|
||||
@@ -115,10 +114,9 @@ jobs:
|
||||
(github.event_name == 'workflow_dispatch' && (inputs.operation == 'validate' || inputs.operation == 'publish'))
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
@@ -126,23 +124,10 @@ jobs:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }}
|
||||
|
||||
- name: Setup
|
||||
uses: ./tooling/github/setup
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
if: "!env.ACT"
|
||||
if: needs.preview-amd64.outputs.digest == '' && !env.ACT
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Reuse amd64 preview image
|
||||
if: needs.preview-amd64.outputs.digest != '' && !env.ACT
|
||||
env:
|
||||
DIGEST: ${{ needs.preview-amd64.outputs.digest }}
|
||||
run: |
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
|
||||
docker pull "${PREVIEW_IMAGE}@${DIGEST}"
|
||||
docker tag "${PREVIEW_IMAGE}@${DIGEST}" homarr-ci
|
||||
docker tag "${PREVIEW_IMAGE}@${DIGEST}" homarr-e2e
|
||||
|
||||
- name: Build container once
|
||||
if: needs.preview-amd64.outputs.digest == '' && !env.ACT
|
||||
uses: docker/build-push-action@v7
|
||||
@@ -152,9 +137,7 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
load: true
|
||||
push: false
|
||||
tags: |
|
||||
homarr-ci
|
||||
homarr-e2e
|
||||
tags: homarr-ci
|
||||
labels: |
|
||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
@@ -182,37 +165,7 @@ jobs:
|
||||
fi
|
||||
done
|
||||
DOCKER_BUILDKIT=1 docker build --platform linux/amd64 \
|
||||
--tag homarr-ci --tag homarr-e2e "${secret_args[@]}" .
|
||||
|
||||
- name: Cache Playwright browsers
|
||||
if: "!env.ACT"
|
||||
id: playwright-cache
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install Playwright Chromium
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true' || env.ACT
|
||||
run: pnpm exec playwright install --with-deps chromium
|
||||
|
||||
- name: Install Playwright system deps
|
||||
if: steps.playwright-cache.outputs.cache-hit == 'true'
|
||||
run: pnpm exec playwright install-deps chromium
|
||||
|
||||
- name: Run E2E tests
|
||||
env:
|
||||
NODE_ENV: development
|
||||
CI: "true"
|
||||
run: pnpm exec vitest e2e --retry 1 --maxWorkers 4
|
||||
|
||||
- name: Upload E2E artifacts
|
||||
if: failure() && !env.ACT
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: playwright-report
|
||||
path: e2e/test-results/
|
||||
retention-days: 7
|
||||
--tag homarr-ci "${secret_args[@]}" .
|
||||
|
||||
preview-amd64:
|
||||
name: Build amd64 preview image
|
||||
@@ -320,10 +273,10 @@ jobs:
|
||||
|
||||
publish-preview:
|
||||
name: Publish multi-platform preview image
|
||||
needs: [container, preview-amd64, preview-arm64]
|
||||
needs: [fast-gate, preview-amd64, preview-arm64]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.container.result == 'success' &&
|
||||
needs.fast-gate.result == 'success' &&
|
||||
needs.preview-amd64.result == 'success' &&
|
||||
needs.preview-arm64.result == 'success' &&
|
||||
(
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.CROWDIN_APP_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.CROWDIN_APP_ID }}
|
||||
client-id: ${{ vars.CROWDIN_CLIENT_ID }}
|
||||
permission-contents: write # required to commit to crowdin branch
|
||||
permission-pull-requests: write # required to create pull request
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||
client-id: ${{ vars.RENOVATE_APPROVE_CLIENT_ID }}
|
||||
permission-pull-requests: write
|
||||
- name: Approve PR
|
||||
env:
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
|
||||
permission-contents: write # write to beta branch (due to merge)
|
||||
permission-pull-requests: write # merge pull request
|
||||
- id: automerge
|
||||
|
||||
@@ -40,6 +40,7 @@ jobs:
|
||||
version: ${{ steps.read-semver.outputs.version || steps.version-fallback.outputs.version }}
|
||||
git_ref: ${{ steps.read-git-ref.outputs.ref || steps.read-semver.outputs.version || github.ref }}
|
||||
skipped: ${{ env.SKIP_RELEASE }}
|
||||
upstream_sync: ${{ steps.update-upstream.outcome }}
|
||||
steps:
|
||||
- run: echo "Manual dispatch redeploys the latest existing release without creating a new one"
|
||||
if: env.SKIP_RELEASE == 'true'
|
||||
@@ -54,7 +55,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
|
||||
permission-contents: write # required to commit package.json & changelog changes, merge them to dev / next and publish the release
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
@@ -115,8 +116,10 @@ jobs:
|
||||
run: |
|
||||
echo "ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
- name: Update dev / next branch
|
||||
id: update-upstream
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
continue-on-error: true # Prevent pipeline from failing when merge fails
|
||||
# Finish publishing the already-created release, then fail in the publish job if synchronization did not work.
|
||||
continue-on-error: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||
UPSTREAM_BRANCH: ${{ github.ref_name == 'main' && 'dev' || 'next' }}
|
||||
@@ -274,7 +277,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
|
||||
permission-contents: write # required to publish the release
|
||||
|
||||
- name: Log in to the Container registry
|
||||
@@ -311,6 +314,12 @@ jobs:
|
||||
run: |
|
||||
gh release edit --repo ${{ github.repository }} ${{ needs.release.outputs.version }} --draft=false
|
||||
|
||||
- name: Verify dev / next synchronization
|
||||
if: needs.release.outputs.skipped == 'false' && needs.release.outputs.upstream_sync != 'success'
|
||||
run: |
|
||||
echo "::error::The released commit was not synchronized back to the upstream development branch."
|
||||
exit 1
|
||||
|
||||
- name: Discord notification
|
||||
if: github.event_name != 'workflow_dispatch' || inputs['send-notifications']
|
||||
env:
|
||||
@@ -318,3 +327,17 @@ jobs:
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: "Successfully deployed images for branch **${{ github.ref_name }}**. Tagged as **${{env.NEXT_VERSION}}**."
|
||||
|
||||
notify-failure:
|
||||
name: Notify deployment failure
|
||||
if: ${{ always() && (failure() || cancelled()) && (github.event_name != 'workflow_dispatch' || inputs['send-notifications']) }}
|
||||
needs: [release, build-amd64, build-arm64, extract-asset-amd64, extract-asset-arm64, publish]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Discord failure notification
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: "The deployment for branch **${{ github.ref_name }}** failed or was cancelled: [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
||||
@@ -19,10 +19,10 @@ permissions:
|
||||
jobs:
|
||||
create-and-merge-pr:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Discord notification
|
||||
if: ${{ github.event.inputs.send-notifications }}
|
||||
if: ${{ github.event_name == 'schedule' || inputs.send-notifications }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
@@ -38,13 +38,30 @@ jobs:
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
branch: dev
|
||||
noNewCommitBehavior: silent
|
||||
noVersionBumpBehavior: silent
|
||||
- name: No release needed
|
||||
if: ${{ steps.semver.outputs.bump == '' || steps.semver.outputs.bump == 'none' }}
|
||||
run: echo "No release-worthy commits were found since ${{ steps.semver.outputs.current }}." >> "$GITHUB_STEP_SUMMARY"
|
||||
- name: Create pull request
|
||||
id: create-pull-request
|
||||
run: 'gh pr create --title "chore(release): automatic release ${{ steps.semver.outputs.next }}" --body "**This is an automatic release**.<br/>Manual action may be required for major bumps.<br/>Detected change to be ``${{ steps.semver.outputs.bump }}``<br/>Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}``" --base main --head dev --label automerge'
|
||||
if: ${{ steps.semver.outputs.bump != '' && steps.semver.outputs.bump != 'none' }}
|
||||
run: |
|
||||
pr_url="$(gh pr list --base main --head dev --state open --json url --jq '.[0].url // empty')"
|
||||
if [[ -z "$pr_url" ]]; then
|
||||
pr_url="$(gh pr create \
|
||||
--title "chore(release): automatic release ${{ steps.semver.outputs.next }}" \
|
||||
--body "**This is an automatic release**.<br/>Manual action may be required for major bumps.<br/>Detected change to be ``${{ steps.semver.outputs.bump }}``<br/>Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}``" \
|
||||
--base main \
|
||||
--head dev \
|
||||
--label automerge)"
|
||||
fi
|
||||
echo "url=$pr_url" >> "$GITHUB_OUTPUT"
|
||||
echo "pr_number=${pr_url##*/}" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Discord notification
|
||||
if: ${{ github.event.inputs.send-notifications }}
|
||||
if: ${{ steps.create-pull-request.outcome == 'success' && (github.event_name == 'schedule' || inputs.send-notifications) }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
@@ -52,26 +69,29 @@ jobs:
|
||||
args: "Created a release PR ${{ steps.create-pull-request.outputs.url }} for version ${{ steps.semver.outputs.next }} (new behaviour: ${{ steps.semver.outputs.bump }})"
|
||||
- name: Obtain token
|
||||
id: obtainApprovalToken
|
||||
if: ${{ steps.create-pull-request.outcome == 'success' }}
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||
client-id: ${{ vars.RENOVATE_APPROVE_CLIENT_ID }}
|
||||
permission-pull-requests: write
|
||||
- name: Approve PR
|
||||
if: ${{ steps.create-pull-request.outcome == 'success' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainApprovalToken.outputs.token }}
|
||||
run: |
|
||||
gh pr review --approve --body "Automatically approved by GitHub Action"
|
||||
gh pr review "${{ steps.create-pull-request.outputs.url }}" --approve --body "Automatically approved by GitHub Action"
|
||||
- name: Obtain token
|
||||
id: obtainMergeToken
|
||||
if: ${{ steps.create-pull-request.outcome == 'success' }}
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
|
||||
permission-contents: write # write to main branch (due to merge)
|
||||
permission-pull-requests: write # merge pull request
|
||||
- id: automerge
|
||||
if: ${{ steps.semver.outputs.bump != 'major' }}
|
||||
if: ${{ steps.create-pull-request.outcome == 'success' && steps.semver.outputs.bump != 'major' }}
|
||||
name: automerge
|
||||
uses: "pascalgn/automerge-action@v0.16.4"
|
||||
env:
|
||||
@@ -85,21 +105,21 @@ jobs:
|
||||
MERGE_REQUIRED_APPROVALS: 0 # do not require approvals
|
||||
|
||||
- name: Merged Discord notification
|
||||
if: ${{ steps.automerge.outputs.mergeResult == 'merged' && github.event.inputs.send-notifications }}
|
||||
if: ${{ steps.automerge.outputs.mergeResult == 'merged' && (github.event_name == 'schedule' || inputs.send-notifications) }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: "Merged PR ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
|
||||
- name: Major Bump Discord notification
|
||||
if: ${{ steps.semver.outputs.bump == 'major' && github.event.inputs.send-notifications }}
|
||||
if: ${{ steps.semver.outputs.bump == 'major' && (github.event_name == 'schedule' || inputs.send-notifications) }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: "The release PR must be manually merged because the next version is a major version: ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
|
||||
- name: Discord Fail Notification
|
||||
if: failure() && github.event.inputs.send-notifications
|
||||
if: ${{ failure() && (github.event_name == 'schedule' || inputs.send-notifications) }}
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
|
||||
@@ -26,36 +26,8 @@ jobs:
|
||||
- name: Copy env
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Start dev server
|
||||
run: pnpm --filter @homarr/nextjs dev &
|
||||
env:
|
||||
TURBO_TELEMETRY_DISABLED: 1
|
||||
DISABLE_TELEMETRY: 1
|
||||
|
||||
- name: Wait for server and fetch spec
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for i in $(seq 1 30); do
|
||||
if curl -fsS http://localhost:3000/api/openapi -o /tmp/openapi.json 2>/dev/null; then
|
||||
echo "Server is up, spec fetched"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for server... ($i/30)"
|
||||
sleep 3
|
||||
done
|
||||
|
||||
if [ ! -f /tmp/openapi.json ]; then
|
||||
echo "::error::Server failed to start within timeout"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
jq -e '.openapi and .paths' /tmp/openapi.json > /dev/null || {
|
||||
echo "::error::Fetched file is not a valid OpenAPI spec"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cp /tmp/openapi.json apps/docs/static/api/open-api-schema.json
|
||||
echo "OpenAPI spec updated ($(jq '.paths | length' /tmp/openapi.json) paths)"
|
||||
- name: Generate and validate OpenAPI spec
|
||||
run: pnpm scripts:update-openapi
|
||||
|
||||
- name: Commit updated spec
|
||||
uses: EndBug/add-and-commit@v10
|
||||
|
||||
@@ -5,39 +5,6 @@ on:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
trigger-docs-release:
|
||||
name: Trigger Documentation Release
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Obtain token
|
||||
id: obtainToken
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.HOMARR_DOCS_RELEASE_APP_PRIVATE_KEY }}
|
||||
client-id: ${{ vars.HOMARR_DOCS_RELEASE_APP_ID }}
|
||||
owner: homarr-labs
|
||||
repositories: |
|
||||
documentation
|
||||
permission-contents: write # required to dispatch repository workflow
|
||||
- name: Trigger documentation release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||
SOURCE_TAG: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
https://api.github.com/repos/homarr-labs/documentation/dispatches \
|
||||
-d @- <<EOF
|
||||
{
|
||||
"event_type": "trigger-release",
|
||||
"client_payload": {
|
||||
"tag": "${SOURCE_TAG}"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
update-bug-report-template:
|
||||
name: Update Bug Report Template
|
||||
runs-on: ubuntu-latest
|
||||
@@ -48,7 +15,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||
client-id: ${{ vars.RENOVATE_MERGE_CLIENT_ID }}
|
||||
permission-contents: write # required to commit to branch
|
||||
permission-pull-requests: write # required to create pr & enable automerge
|
||||
- name: Checkout code
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
|
||||
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
||||
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_CLIENT_ID }}
|
||||
permission-contents: write # required to commit to branch
|
||||
permission-pull-requests: write # required to create pr & enable automerge
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
|
||||
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
||||
client-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_CLIENT_ID }}
|
||||
permission-contents: write # required to commit to branch
|
||||
permission-pull-requests: write # required to create pr & enable automerge
|
||||
- name: Checkout code
|
||||
|
||||
@@ -2,8 +2,36 @@ name: Workshop
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/workshop.yml"
|
||||
- "apps/workshop/**"
|
||||
- "apps/docs/**"
|
||||
- "packages/custom-widgets/**"
|
||||
- "packages/definitions/**"
|
||||
- "packages/workshop/**"
|
||||
- "tooling/typescript/**"
|
||||
- "patches/**"
|
||||
- "package.json"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "turbo.json"
|
||||
- ".npmrc"
|
||||
push:
|
||||
branches: [dev, v2, release/v2, main]
|
||||
paths:
|
||||
- ".github/workflows/workshop.yml"
|
||||
- "apps/workshop/**"
|
||||
- "apps/docs/**"
|
||||
- "packages/custom-widgets/**"
|
||||
- "packages/definitions/**"
|
||||
- "packages/workshop/**"
|
||||
- "tooling/typescript/**"
|
||||
- "patches/**"
|
||||
- "package.json"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "turbo.json"
|
||||
- ".npmrc"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -15,21 +43,10 @@ concurrency:
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24.18.0
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
run_install: false
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm --filter @homarr/workshop typecheck
|
||||
- run: pnpm --filter @homarr/docs typecheck
|
||||
- run: docker compose --env-file apps/workshop/.env.staging.example -f apps/workshop/docker-compose.staging.yml config --quiet
|
||||
- run: pnpm test:workshop
|
||||
- run: pnpm test:workshop-image
|
||||
|
||||
publish-amd64:
|
||||
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
|
||||
|
||||
@@ -69,6 +69,7 @@ e2e/shared/tmp
|
||||
|
||||
#personal backgrounds
|
||||
apps/nextjs/public/images/background.png
|
||||
apps/nextjs/public/__widget-editor/
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
"CHANGELOG.md",
|
||||
"apps/docs/.docusaurus/**",
|
||||
"apps/docs/build/**",
|
||||
"apps/docs/blog/**",
|
||||
"apps/docs/blog/2022/**",
|
||||
"apps/docs/blog/2023/**",
|
||||
"apps/docs/blog/2024/**",
|
||||
"apps/docs/blog/2025/**",
|
||||
"apps/docs/blog/authors.yml",
|
||||
"apps/docs/static/api/open-api-schema.json",
|
||||
"packages/custom-widgets/src/core/component-catalog.generated.json",
|
||||
"apps/docs/static/custom-widgets/component-catalog-v1.json"
|
||||
]
|
||||
|
||||
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 618 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,167 @@
|
||||
---
|
||||
authors:
|
||||
- ajnart
|
||||
tags: [homarr, update, version, assistant, custom widgets, dashboards, docker]
|
||||
description: Homarr 2.0 introduces Custom Widgets and Workshop, rebuilt board editing, Assistant tooling, responsive layouts, onboarding, and more.
|
||||
image: ./img/homarr-v2-dashboard.webp
|
||||
draft: false
|
||||
---
|
||||
|
||||
# Homarr 2.0.0 - Workshop, Custom Widgets and a rebuilt board
|
||||
|
||||
First, thank you.
|
||||
|
||||
Many of you used the v2 demo when it was still rough, found bugs and came back to test the next build. Others translated Homarr, answered questions, contributed code or simply kept using it. We would not be shipping this without you.
|
||||
|
||||
We are also planning a small batch of **free, limited-edition Homarr swag** for Workshop contributors and a few randomly picked community members. It is not for sale and it is not for profit. No Homarr merch empire, sorry.
|
||||
|
||||
Homarr 2.0 is an upgrade, not a brand-new app wearing the same logo. Here is what actually changed.
|
||||
|
||||

|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
:::warning Back up before upgrading
|
||||
|
||||
Database migrations run automatically when Homarr starts. Create a
|
||||
[Homarr backup](/docs/management/backup) first. Do not test a prerelease image on your only production volume.
|
||||
Backups are boring. Restores are worse.
|
||||
|
||||
:::
|
||||
|
||||
## Custom Widgets v2 and Workshop
|
||||
|
||||
### Build almost any widget
|
||||
|
||||
**This is the big one.** We think it is the most important Homarr update we have made.
|
||||
|
||||
Custom Widgets v2 lets you build widgets from JSX, API requests, actions and typed settings. Write one yourself or describe it to Assistant, then test it in the workbench. We think this is enough to make almost any dashboard widget—even that oddly specific component only three people need. Those three people no longer have to wait for us.
|
||||
|
||||
This was partly inspired by [Google's upcoming Create My Widget feature](https://blog.google/products-and-platforms/platforms/android/gemini-intelligence/): describe a widget in normal language and let AI make it. Coding knowledge is optional. Knowing what the API does still helps; AI has not fixed bad documentation. Yet.
|
||||
|
||||

|
||||
|
||||
A `homarr-custom-widget-v2` definition can use several fixed-origin API sources. Credentials are encrypted separately and stay out of exports and Workshop submissions. Actions support `GET`, `POST`, `PUT`, `PATCH` and `DELETE`; requests have host checks, timeouts, size limits, permissions and rate limits. Custom Widgets remain **Beta** in 2.0.
|
||||
|
||||
### Share widgets and Custom CSS
|
||||
|
||||
**Workshop is the other half.** Publish and install Custom Widgets, then update, vote on, comment on and report submissions. Workshop is not only for widgets: you can share your **Custom CSS** there too. Deployment URLs and credentials stay on your Homarr instance.
|
||||
|
||||

|
||||
|
||||
:::warning Workshop is moderated
|
||||
|
||||
AI makes widgets easy to produce. It does not make the first thing it coughs up worth publishing. **Do not dump untested slop into Workshop.** Test it, add a useful screenshot and explain its setup. Workshop is moderated.
|
||||
|
||||
:::
|
||||
|
||||
### The ultimate Homarr update
|
||||
|
||||
We have called this the **"ultimate Homarr update"**. Not because work stops, but because every new service or widget idea no longer needs a full Homarr release; the community can build and share it instead.
|
||||
|
||||
## Board editing, rebuilt
|
||||
|
||||
### Drag and drop
|
||||
|
||||
We threw out the old drag-and-drop system and rebuilt it on [`dnd-kit`](https://dndkit.com/). Moves and eight-direction resizes are transactional: Homarr previews the result, checks collisions and only saves a valid layout. Bad moves roll back instead of wrecking the board. In short: less fighting with your own dashboard.
|
||||
|
||||

|
||||
|
||||
### Goodbye Groups and sections, bonjour Containers
|
||||
|
||||
Groups and dynamic sections did nearly the same job, so **Containers replace both**. They hold apps, widgets and other Containers. Hold <kbd>Ctrl</kbd> or <kbd>Command</kbd> to move several items at once.
|
||||
|
||||
### One board, several layouts
|
||||
|
||||
Boards also have Base and Mobile layouts, plus optional breakpoints. **Reset from Base** creates a Mobile starting point without duplicating the board.
|
||||
|
||||
## Homarr Assistant
|
||||
|
||||
### Work with live Homarr data
|
||||
|
||||
Assistant reads live Homarr data and has tools for boards, apps, integrations, widgets, Docker and supported services. It can answer questions, make approved changes and build Custom Widgets. It follows your permissions: reads can run automatically, while changes require approval by default.
|
||||
|
||||

|
||||
|
||||
Open it with <kbd>Shift</kbd> + <kbd>A</kbd>. <kbd>Cmd</kbd>/<kbd>Ctrl</kbd> + <kbd>/</kbd> searches it.
|
||||
|
||||
### The Homarr provider
|
||||
|
||||
:::note
|
||||
|
||||
Assistant ships with a **"Homarr provider"**. It is free, will never offer paid plans, and is paid for by [Ajnart](https://github.com/ajnart) using the ad revenue from this website.
|
||||
|
||||
It only uses zero-data-retention (ZDR) providers. [The provider is open source](https://github.com/homarr-labs/homarr/blob/release/v2/apps/workshop/homarr_provider.go), and neither prompts nor responses are logged. We are not selling your data. Pinky promise.
|
||||
|
||||
:::
|
||||
|
||||
### External assistants through MCP
|
||||
|
||||
External assistants can use the same permission-checked tools through `/api/mcp`, with an API key or OAuth 2.1 with PKCE.
|
||||
|
||||
## The rest (there is a lot)
|
||||
|
||||
### Brand-new onboarding
|
||||
|
||||
A six-stage setup studio handles the first admin, core settings, Docker discovery, apps, integrations and the first board.
|
||||
|
||||

|
||||
|
||||
### Board switcher
|
||||
|
||||
Boards now live in a searchable gallery with keyboard navigation. Open it from the header or press <kbd>Shift</kbd> + <kbd>C</kbd>.
|
||||
|
||||

|
||||
|
||||
### Command menu and keybinds
|
||||
|
||||
<kbd>Cmd</kbd>/<kbd>Ctrl</kbd> + <kbd>K</kbd> finds resources, pages, settings, users, groups and commands. More
|
||||
keybinds cover Assistant, editing, selection and advanced widget views.
|
||||
|
||||
### Customizable header
|
||||
|
||||
Pick which buttons appear, put them on the left, center or right, and change their order.
|
||||
|
||||

|
||||
|
||||
### Instance branding
|
||||
|
||||
Change the name, logo, favicon, colors, corner styles and login page. Global CSS applies instance-wide; board CSS can override it. Only give that power to admins you trust. CSS remains CSS.
|
||||
|
||||

|
||||
|
||||
### Widgets
|
||||
|
||||
Add content without leaving the board. Settings have live previews, and supported widgets have advanced views. Weather and Clock were redesigned; Air Quality, Countdown and Timer are new.
|
||||
|
||||
### Docker and Podman
|
||||
|
||||
`DOCKER_ENDPOINTS` supports several hosts. Assisted setup and container labels can create apps, integrations, Containers, widgets and health checks.
|
||||
|
||||

|
||||
|
||||
Homepage-compatible labels work too.
|
||||
|
||||
### Permissions
|
||||
|
||||
Groups use a new Viewer, Editor and Admin matrix. The API, Assistant and MCP follow the same rules. LDAP can create users on first sign-in; OIDC can link verified accounts and synchronize groups.
|
||||
|
||||
### Performance
|
||||
|
||||
Board startup work runs in parallel, heavy screens load when opened, and identical integration requests share work instead of doing it twice.
|
||||
|
||||
## Before upgrading
|
||||
|
||||
- Keep a backup and use the final release image, not a prerelease tag.
|
||||
- Legacy Custom JSX definitions enter a migration state. Homarr keeps encrypted credentials and an archived copy until the v2 definition validates.
|
||||
- Mobile layouts place rail content in the main flow instead of displaying rails.
|
||||
- Custom Widgets remain Beta.
|
||||
- Assistant and MCP do not bypass permissions.
|
||||
|
||||
## Links
|
||||
|
||||
- Try the [Homarr 2.0 preview](https://app-v2.preview.homarr.dev).
|
||||
- Follow the [v2 beta setup guide](/docs/getting-started/upgrade-v2).
|
||||
- Follow the [v2 release pull request](https://github.com/homarr-labs/homarr/pull/6545) and [public beta thread](https://github.com/homarr-labs/homarr/issues/6600).
|
||||
- Compare [v1.76.2 with the release/v2 branch](https://github.com/homarr-labs/homarr/compare/v1.76.2...release/v2).
|
||||
- Read the guides for [Boards](/docs/management/boards), [Custom Widgets](/docs/management/custom-widgets), [Assistant](/docs/management/assistant), [MCP](/docs/management/mcp) and [Docker labels](/docs/integrations/docker-labels).
|
||||
@@ -148,13 +148,18 @@ homarr images rm feature
|
||||
# List data volumes with owner PR/tag and in-use status
|
||||
homarr data
|
||||
|
||||
# Delete data volume (safely stops and removes owning container first)
|
||||
# Preview deleting a data volume and its owning container
|
||||
homarr data rm homarr_feature_data
|
||||
|
||||
# Permanently delete the data after reviewing the preview
|
||||
homarr data rm homarr_feature_data --force
|
||||
|
||||
# Remove stopped containers (prompts without --force)
|
||||
homarr prune --force
|
||||
```
|
||||
|
||||
Forced data deletion removes the derived owner container only when it mounts the exact selected volume. A same-named container that does not own that volume stops the operation.
|
||||
|
||||
### Instance Operations & Diagnostics
|
||||
|
||||
```bash
|
||||
|
||||
@@ -14,4 +14,4 @@ homarr fix-usernames
|
||||
|
||||
This will convert all credential usernames to lowercase and update the database accordingly.
|
||||
|
||||

|
||||

|
||||
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 393 KiB |
|
After Width: | Height: | Size: 348 KiB |
|
After Width: | Height: | Size: 414 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 24 KiB |
@@ -3,7 +3,9 @@
|
||||
The Homarr image includes a recovery CLI for operations that cannot be completed in the web interface. It is not an
|
||||
application API; use the [API](/docs/management/api) or [MCP endpoint](/docs/management/mcp) for automation.
|
||||
|
||||
Back up the database and stop Homarr before running a command that changes data.
|
||||
Back up the database and stop Homarr before running a recovery command that changes database records directly.
|
||||
Widget project commands and authenticated preview requests can run while Homarr is online.
|
||||
Failed commands print an error and return a non-zero exit code, so shell scripts can stop safely.
|
||||
|
||||
## Run the recovery CLI
|
||||
|
||||
@@ -14,10 +16,175 @@ docker exec -it <container-id> /bin/bash
|
||||
homarr <command>
|
||||
```
|
||||
|
||||
In QNAP Container Station, open the Homarr container's **Execute** action and select `/bin/bash`.
|
||||
### QNAP Container Station
|
||||
|
||||
1. Open **Container Station**.
|
||||
|
||||

|
||||
|
||||
2. Select **Containers**.
|
||||
|
||||

|
||||
|
||||
3. Select the Homarr container.
|
||||
|
||||

|
||||
|
||||
4. Select **Execute**.
|
||||
|
||||

|
||||
|
||||
5. Select `/bin/bash`, then select **Execute**.
|
||||
|
||||

|
||||
|
||||
6. Run `homarr <command>` in the console.
|
||||
|
||||

|
||||
|
||||
See the pages in this section for available commands.
|
||||
|
||||
## Build and share custom widget packages
|
||||
|
||||
The `widgets` commands accept a project directory, a v3 package JSON file, or an offline archive. Scaffolding,
|
||||
validation, compilation and packing do not need a running Homarr database.
|
||||
|
||||
Create a project to edit in your own IDE:
|
||||
|
||||
```sh
|
||||
homarr widgets init my-clock --example clock --id example.my-clock --name "My clock"
|
||||
homarr widgets validate my-clock
|
||||
homarr widgets pack my-clock
|
||||
```
|
||||
|
||||
`init` (also `scaffold`) creates a new directory with `homarr-widget.json` and normal TSX, TypeScript, CSS and asset
|
||||
files. It refuses to overwrite an existing directory. Choose `clock`, `timer`, `beszel`, `checklist`, `downloads`,
|
||||
`sonarr`, `rack`, `backup`, `household`, `overview`, `media`, `sqlite` or `mqtt` with `--example`; leaving out `--id` generates a unique
|
||||
local package identity. Edit the manifest and files, then repeat validation and packing. The manifest carries entrypoints,
|
||||
handlers, connection requirements, options and exact dependency versions; it does not embed the files map in this layout.
|
||||
|
||||
Directory loading skips hidden files, symlinks, `node_modules`, `dist`, `coverage`, generated archives and root-level
|
||||
`package.json`, `package-lock.json` and `tsconfig.json`. Keep all widget source under supported TS/JS, JSON, CSS, SVG,
|
||||
text, Markdown, image or font files. Build output defaults to `dist/widget.artifact.json`; packing defaults to
|
||||
`dist/widget.homarr-widget.json`. Remove or rename an existing output before rebuilding, or pass a new `--output`.
|
||||
|
||||
The same commands also accept the single-file format:
|
||||
|
||||
```sh
|
||||
homarr widgets validate widget.json
|
||||
homarr widgets build widget.json --output widget.artifact.json
|
||||
homarr widgets pack widget.json --output widget.homarr-widget.json
|
||||
homarr widgets validate widget.homarr-widget.json
|
||||
```
|
||||
|
||||
`validate` checks the package structure or an archive's source and artifact checksums. It does not execute widget code.
|
||||
`build` compiles JSX/TSX, local modules, CSS and assets into browser and optional server output. `pack` includes the
|
||||
source, compiled output and dependency lock in one JSON archive for offline installation. Output files must not already exist.
|
||||
|
||||
Packages with third-party dependencies need `--trust` to install their exact declared versions:
|
||||
|
||||
```sh
|
||||
homarr widgets pack widget.json --trust
|
||||
```
|
||||
|
||||
Dependency installations are cached in `.homarr-widgets` beside the input file. Installation scripts are disabled;
|
||||
packages requiring native build steps are not supported by these commands. Compilation does not start the widget's
|
||||
server handlers. Installed trusted widgets execute with Homarr's privileges; source and artifact checksums establish
|
||||
integrity, not publisher authenticity. Local connection bindings, credentials and widget state are separate from the archive.
|
||||
|
||||
### Preview a local project in Homarr
|
||||
|
||||
Set `HOMARR_API_KEY` to an admin API key from your own Homarr instance, then run:
|
||||
|
||||
```sh
|
||||
homarr widgets preview my-clock --url http://homarr.local:7575 --trust
|
||||
```
|
||||
|
||||
The command creates a disabled installation on first use, compiles the current local source on that Homarr instance,
|
||||
and prints a link to its exact preview session. Open the link while signed in as the API key owner. Repeating the command
|
||||
replaces the previous preview; it does not activate or overwrite the installation's saved draft. Use `--save-draft` to
|
||||
also save your local source. Use `--installation-id ID` to target an existing installation. The local
|
||||
`.homarr-widget.local.json` file remembers installation and preview IDs and is excluded from scaffolded Git projects.
|
||||
|
||||
Pass `--bindings bindings.json` for a JSON object mapping declared connection names to existing Homarr connection IDs,
|
||||
and `--options options.json` for preview option values. Credentials remain in Homarr. `--live-actions` explicitly allows
|
||||
real mutations during this preview; queries and subscriptions can already execute trusted server code. `--trust` is
|
||||
required on every invocation. Set `HOMARR_URL` instead of `--url`, or select a different secret variable with
|
||||
`--api-key-env VARIABLE_NAME`. Preview compilation may install declared dependencies after this explicit trust choice.
|
||||
|
||||
Use `--scenario NAME` to select synthetic query data declared in the package's `previewScenarios`. This skips server
|
||||
startup and configured service requests; unprovided query fixtures fail explicitly and subscriptions are unavailable.
|
||||
SDK actions stay simulated, so `--scenario` cannot be combined with `--live-actions`. Trusted browser code and dependency
|
||||
compilation keep their normal privileges. See [synthetic preview scenarios](../../management/custom-widgets/trusted-packages.mdx#synthetic-preview-scenarios).
|
||||
|
||||
### Publish the exact offline archive
|
||||
|
||||
Set `HOMARR_WORKSHOP_TOKEN` to an authenticated Workshop token, then publish the archive you reviewed:
|
||||
|
||||
```sh
|
||||
homarr widgets publish my-clock/dist/widget.homarr-widget.json
|
||||
homarr widgets publish next-release.homarr-widget.json --submission-id LISTING_ID --expected-revision 1
|
||||
```
|
||||
|
||||
Use `--url` (or `WORKSHOP_API_URL`) for your own Workshop service, `--token-env` for another token variable,
|
||||
`--changelog` for release notes, and `--forked-from RELEASE_ID` to attribute a new fork. Existing listings require their
|
||||
current revision and a new package version. Forks need their own package identity. Published releases are immutable;
|
||||
publication sends the archive without rebuilding it. You can also import the archive into Homarr and publish through
|
||||
the package workspace.
|
||||
|
||||
### Run a package on another machine
|
||||
|
||||
Run an already packed archive on another machine with the optional widget runner:
|
||||
|
||||
```sh
|
||||
export HOMARR_WIDGET_RUNNER_TOKEN="$(openssl rand -hex 32)"
|
||||
homarr widgets serve widget.homarr-widget.json --trust --host 127.0.0.1 --port 8787
|
||||
```
|
||||
|
||||
`serve` executes the installed package with that machine's operating system privileges. It never downloads packages
|
||||
or accepts source uploads. Set a custom token variable with `--token-env VARIABLE_NAME`. The token must have at least
|
||||
32 characters. Interrupting the command stops its worker and subscriptions. See [remote widget runners](../../management/custom-widgets/remote-runners.mdx)
|
||||
for connection setup, local files, SQL, MQTT, and browser-service adapters.
|
||||
|
||||
### Share several widgets as a collection
|
||||
|
||||
A collection contains independent compiled widget packages and a shared connection blueprint. Write a collection
|
||||
manifest with paths to already packed archives:
|
||||
|
||||
```json
|
||||
{
|
||||
"manifest": { "id": "example.home", "version": "1.0.0", "name": "Home dashboard" },
|
||||
"connections": { "monitor": { "label": "Monitoring server", "kind": "integration" } },
|
||||
"widgets": [
|
||||
{ "key": "clock", "archive": "clock/dist/widget.homarr-widget.json" },
|
||||
{
|
||||
"key": "servers",
|
||||
"archive": "beszel/dist/widget.homarr-widget.json",
|
||||
"connections": { "beszel": "monitor" }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Pack and verify it without compiling or executing the member packages:
|
||||
|
||||
```sh
|
||||
homarr widgets collection pack collection.json --output home.homarr-collection.json
|
||||
homarr widgets collection validate home.homarr-collection.json
|
||||
homarr widgets collection import home.homarr-collection.json --url http://homarr.local:7575
|
||||
```
|
||||
|
||||
`import` uses `HOMARR_API_KEY`, or the variable selected with `--api-key-env`. Pass `--bindings bindings.json` to map
|
||||
each collection slot to an existing local Homarr connection once; all members that reference that slot receive the
|
||||
binding. Omitted connections can be configured later. Imports create every member as a disabled draft in one database
|
||||
transaction and print their review links. Trust and activation are separate, explicit steps.
|
||||
|
||||
Collections support 1–32 members and up to 128 MiB. Package identities, compiled artifacts, dependency locks and
|
||||
documented Workshop/fork attribution travel with the archive. The collection and each nested artifact have integrity
|
||||
checksums. Local connection URLs, IDs and credentials, board placements, stored widget data and local configuration
|
||||
are excluded. Authored `options` on a member are portable defaults: include only values you intend to share. Package
|
||||
source is included verbatim, so embedded literals are still part of the archive.
|
||||
|
||||
## Build a local branch harness
|
||||
|
||||
The repository's host-side CLI can build the current checkout and run it as a named Docker harness:
|
||||
|
||||
@@ -10,10 +10,10 @@ Using this command, you can forcefully reset the password of a user.
|
||||
It will be set to a random value and the hash for the user will be updated.
|
||||
All sessions for that user will be terminated and the user must re-login.
|
||||
|
||||

|
||||

|
||||
|
||||
Using the `--username` parameter, you can specify who you want to reset the password for.
|
||||
Executing the command will create a new random password, update the database and display the new password.
|
||||
All sessions for this user will be terminated and they must re-login.
|
||||
|
||||

|
||||

|
||||
@@ -82,3 +82,25 @@ Workshop contributors can set up PocketBase and Docusaurus with the dedicated
|
||||
- `pnpm build` — production monorepo build.
|
||||
- `docker build -t homarr .` — build a production Docker image.
|
||||
- `docker run -p 7575:7575 -e SECRET_ENCRYPTION_KEY='your_64_character_hex_string' homarr:<name>` — run it.
|
||||
|
||||
## CI and focused validation
|
||||
|
||||
The main CI workflow runs lint, typechecking, affected workspace builds, workspace configuration checks, OpenAPI validation, and Custom Widget architecture and bundle checks. Unit and E2E suites do not run in CI. Preview images publish after the Fast gate and both architecture builds succeed; browser tests do not block publication. The container job builds an amd64 image when no preview image was built.
|
||||
|
||||
Workshop CI runs only when its workflow or Docker build inputs change. It validates Compose configuration and publishes production images on its configured release branches. Its Docker integration and image tests are manual; workspace typechecks run in the main Fast gate.
|
||||
|
||||
Choose the smallest relevant suite when changing behavior:
|
||||
|
||||
| Command | Scope | Requirements |
|
||||
| ------------------------------------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| `pnpm test <file>` | Focused unit or contract tests | Workspace dependencies |
|
||||
| `pnpm test` | All unit and contract tests, without Docker integration suites | Workspace dependencies |
|
||||
| `pnpm test:coverage` | Unit and contract tests with coverage | Workspace dependencies |
|
||||
| `pnpm test:integration <file>` | Docker-backed service and database compatibility tests | Docker |
|
||||
| `pnpm test:e2e <file>` | Browser and container scenarios | Docker; Playwright Chromium and system dependencies for browser tests |
|
||||
| `pnpm test:docs-screenshots` | Regenerate Assistant documentation screenshots | Same browser prerequisites |
|
||||
| `pnpm test:workshop` / `pnpm test:workshop-image` | Workshop integration / production-image validation | Docker |
|
||||
|
||||
Omit `<file>` to run all tests in that command's scope. E2E and screenshot commands use a local image tagged `homarr-e2e`, or an image selected with `HOMARR_E2E_IMAGE`. The default commands run once without automatic retries; coverage is opt-in. The screenshot generator is excluded from both unit and E2E commands.
|
||||
|
||||
Keep behavioral coverage for authorization, migrations, persistence, and widget execution. Avoid tests that only search source code for particular component names, JSX, CSS, or documentation wording.
|
||||
@@ -30,7 +30,8 @@ Using the `PUID` and `PGID` will require you to set the correct permissions on t
|
||||
| `PUID` | User ID to run the container as | Any valid user ID | `0` |
|
||||
| `PGID` | Group ID to run the container as | Any valid group ID | `0` |
|
||||
| `LOG_LEVEL` | Log level to use | `debug` / `info` / `warn` / `error` | `info` |
|
||||
| `NO_EXTERNAL_CONNECTION` | Disables Homarr's internet-backed requests, including update checks, weather, and RSS feeds | `true` or `false` | `false` |
|
||||
| `BASE_URL` | Public Homarr origin used by MCP OAuth metadata and redirects. Set this when a reverse proxy does not preserve the public host and protocol. | HTTP(S) origin without a path | - |
|
||||
| `NO_EXTERNAL_CONNECTION` | Disables Homarr's internet-backed requests, including update checks, weather, location search, and RSS feeds | `true` or `false` | `false` |
|
||||
| `ENABLE_DNS_CACHING` | Enables dns caching. Enabled by default, but if you experience issues with IPv6 or static IPs, see [#4006](https://github.com/homarr-labs/homarr/issues/4006) or disable this flag | `true` or `false` | `true` |
|
||||
| `HOMARR_DISABLE_IPV6` | Disables the IPv6 listener of the internal nginx proxy, even when the host supports IPv6. IPv6 is normally disabled automatically when the host has no IPv6 configured. See [#4596](https://github.com/homarr-labs/homarr/issues/4596) | `true` or `false` | `false` |
|
||||
| `HOMARR_WEBSITE_URL` | Main Homarr documentation/site base used by runtime links. Must be an absolute HTTP(S) URL. | HTTP(S) URL | `https://homarr.dev` |
|
||||
@@ -139,3 +140,14 @@ The advanced deployments environment variables should only be used if you know w
|
||||
|
||||
All public URL variables reject non-HTTP protocols, embedded credentials, query strings, and fragments, and normalize
|
||||
trailing slashes when Homarr or Docusaurus starts.
|
||||
|
||||
## Trusted widget packages
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| `CUSTOM_WIDGET_PACKAGE_PATH` | Persistent build and runtime artifact cache. Activated browser/server artifacts and their source are also retained in the database. | `/appdata/custom-widgets` in production; `./appdata/custom-widgets` in development |
|
||||
| `CUSTOM_WIDGET_MAX_SERVER_PROCESSES` | Maximum active trusted widget server artifacts per Homarr process, from 1 to 128. Source-free packages do not start a server process. | `16` |
|
||||
|
||||
Each replica materializes an authorized installed artifact from the database into its own cache. Dashboard requests do
|
||||
not install npm dependencies. Package dependencies are resolved and bundled when an owner previews or activates a new
|
||||
source draft; importing a complete compiled archive supports offline activation. See [trusted widget packages](/docs/management/custom-widgets/trusted-packages).
|
||||
@@ -53,20 +53,6 @@ Commands at a grid boundary or at the tile's minimum size are ignored and announ
|
||||
|
||||
On touch screens, press and hold a tile before moving it; a quick tap does not start a drag. Resize handles accept direct touch input. During a pointer or touch move or resize, `Esc` cancels the interaction and restores the layout from before it started.
|
||||
|
||||
## Board editing
|
||||
|
||||
When a tile is focused in edit mode:
|
||||
|
||||
| Shortcut | Description |
|
||||
| -------------------- | --------------------------------------------------------------------------- |
|
||||
| `Enter` or `Space` | Starts or stops keyboard editing |
|
||||
| Arrow keys | Moves the focused tile within its current canvas, rail, or Container |
|
||||
| `Shift` + Arrow keys | Resizes from the tile's lower-right corner: Left/Up shrink; Right/Down grow |
|
||||
| `Esc` | Stops keyboard editing without undoing its changes |
|
||||
|
||||
Keyboard moves and resizes apply immediately. To move an item between a canvas, rail, or Container, drag it there or use **Move / resize item** for precise numeric placement.
|
||||
Commands at a grid boundary or at the tile's minimum size are ignored and announced instead of moving or shrinking the tile.
|
||||
|
||||
:::tip
|
||||
|
||||
Here `MOD` refers to the modifier key, which is `Ctrl` on Windows and `Cmd` on macOS.
|
||||
|
||||
@@ -40,7 +40,7 @@ services:
|
||||
volumes:
|
||||
- ./homarr/appdata:/appdata
|
||||
environment:
|
||||
- BASE_URL=your.internal.dns.address.here.com
|
||||
- BASE_URL=https://your.internal.dns.address.here.com
|
||||
- SECRET_ENCRYPTION_KEY=your_64_character_hex_string # <--- can be generated with `openssl rand -hex 32`
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
@@ -202,9 +202,9 @@ Homarr supports multiple authentication options, from internal userbase (credent
|
||||
| ``AUTH_OIDC_CLIENT_NAME`` | Display name of provider (in login screen) | OIDC |
|
||||
| ``AUTH_OIDC_AUTO_LOGIN`` | Automatically redirect to OIDC login | false |
|
||||
| ``AUTH_OIDC_SCOPE_OVERWRITE`` | Overwrite default scopes (openid, profile, email) | openid email profile groups |
|
||||
| ``AUTH_OIDC_GROUPS_ATTRIBUTE`` | Attribute used for groups (roles) claim | groups |
|
||||
| ``AUTH_OIDC_GROUPS_ATTRIBUTE`` | Attribute used for groups (roles) claim. Dot-separated paths such as `resource_access.homarr.roles` are supported. | groups |
|
||||
| ``AUTH_OIDC_GROUPS_LOCAL_MANAGEMENT`` | Stop syncing group memberships of OIDC users from the groups claim and manage them locally via the group members page instead | false |
|
||||
| ``AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE`` | Overwrite name attribute. By default it will use preferred_username if it does not contain a `@` and otherwise name. | --- |
|
||||
| ``AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE`` | Overwrite name attribute. Dot-separated paths are supported. By default it will use preferred_username if it does not contain a `@` and otherwise name. | --- |
|
||||
| ``AUTH_OIDC_FORCE_USERINFO`` | Force userinfo endpoint to be used for user information. | false |
|
||||
| ``AUTH_OIDC_ENABLE_DANGEROUS_ACCOUNT_LINKING`` | Enable account linking for OIDC provider. This will link the OIDC accounts by email. Be sure that you have verified emails to prevent stealing accounts. | false |
|
||||
| ``AUTH_OIDC_ENABLE_DANGEROUS_CREDENTIALS_LINKING`` | Link an OIDC identity to an existing credentials user with the same email. The credentials login and existing user data are preserved. Requires the OIDC profile to contain `email_verified: true`. | false |
|
||||
@@ -244,18 +244,21 @@ Homarr supports multiple authentication options, from internal userbase (credent
|
||||
- Create a group (e.g., `homarr-admins`) and assign users who require admin access.
|
||||
|
||||
**2. Example .env file:**
|
||||
|
||||
Generate `SECRET_ENCRYPTION_KEY` with `openssl rand -hex 32`; do not copy an example key between installs.
|
||||
|
||||
```env
|
||||
OIDC_CLIENT_ID=identificationid #OIDC client ID
|
||||
OIDC_CLIENT_SECRET=secretsecretsecret #OIDC client secret
|
||||
OIDC_CLIENT_ID=<REPLACE_WITH_OIDC_CLIENT_ID>
|
||||
OIDC_CLIENT_SECRET=<REPLACE_WITH_OIDC_CLIENT_SECRET>
|
||||
OIDC_SLUG=homarr #Application slug in Authentik
|
||||
AUTH_DOMAIN=auth.example.com #Authentik FQDN
|
||||
ADMIN_GROUP=homarr-admins #Authentik group for Homarr admins
|
||||
HOMARR_FQDN=homarr.example.com #Homarr FQDN
|
||||
SECRET_ENCRYPTION_KEY=encryptencryptencrypt #Homarr encryption key
|
||||
SECRET_ENCRYPTION_KEY=<REPLACE_WITH_64_HEX_CHARACTERS>
|
||||
```
|
||||
|
||||
**3. Example Docker Compose configuration:**
|
||||
```yaml {14-26}
|
||||
```yaml {14-24}
|
||||
services:
|
||||
homarr:
|
||||
image: ghcr.io/homarr-labs/homarr:latest
|
||||
@@ -269,8 +272,6 @@ Homarr supports multiple authentication options, from internal userbase (credent
|
||||
environment:
|
||||
- TZ=America/Los_Angeles
|
||||
- SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY}
|
||||
- BASE_URL=https://${HOMARR_FQDN}
|
||||
- NEXTAUTH_URL=https://${HOMARR_FQDN}
|
||||
- AUTH_PROVIDERS=oidc #(optional: include ',credentials' to keep local accounts as fallback)
|
||||
- AUTH_OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
|
||||
- AUTH_OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
|
||||
@@ -322,15 +323,18 @@ Homarr supports multiple authentication options, from internal userbase (credent
|
||||
- Record the `Client-ID` and `Client Key` (secret).
|
||||
|
||||
**2. Example .env file (Homarr):**
|
||||
|
||||
Generate `SECRET_ENCRYPTION_KEY` with `openssl rand -hex 32`; do not copy an example key between installs.
|
||||
|
||||
```env
|
||||
OIDC_CLIENT_ID=identificationid #Google Client-ID
|
||||
OIDC_CLIENT_SECRET=secretsecretsecret #Google Client Key (secret)
|
||||
OIDC_CLIENT_ID=<REPLACE_WITH_GOOGLE_CLIENT_ID>
|
||||
OIDC_CLIENT_SECRET=<REPLACE_WITH_GOOGLE_CLIENT_SECRET>
|
||||
HOMARR_FQDN=homarr.example.com #Homarr FQDN
|
||||
SECRET_ENCRYPTION_KEY=encryptencryptencrypt #Homarr encryption key
|
||||
SECRET_ENCRYPTION_KEY=<REPLACE_WITH_64_HEX_CHARACTERS>
|
||||
```
|
||||
|
||||
**3. Example Docker Compose configuration (Homarr):**
|
||||
```yaml {16-22}
|
||||
```yaml {14-20}
|
||||
services:
|
||||
homarr:
|
||||
image: ghcr.io/homarr-labs/homarr:latest
|
||||
@@ -344,8 +348,6 @@ Homarr supports multiple authentication options, from internal userbase (credent
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY}
|
||||
- BASE_URL=https://${HOMARR_FQDN}
|
||||
- NEXTAUTH_URL=https://${HOMARR_FQDN}
|
||||
- AUTH_PROVIDERS=oidc #(optional: include ',credentials' to keep local accounts as fallback)
|
||||
- AUTH_OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
|
||||
- AUTH_OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
---
|
||||
title: Frequently Asked Questions
|
||||
description: Answers to common Homarr questions about installation, customization, connectivity, support, and commercial use.
|
||||
tags:
|
||||
- Community
|
||||
- Help
|
||||
---
|
||||
|
||||
### Can Homarr run on a Raspberry Pi?
|
||||
## Can Homarr run on a Raspberry Pi?
|
||||
|
||||
Yes, when the device uses a supported `linux/arm64` operating system. `linux/arm/v7` is not supported. See
|
||||
[Prerequisites](/docs/getting-started).
|
||||
|
||||
### Can I use custom app icons?
|
||||
## Can I use custom app icons?
|
||||
|
||||
Yes. Use a direct image URL, one of Homarr's icon sources, or upload an image. See [Icons](/docs/advanced/icons).
|
||||
|
||||
### Can I create widgets?
|
||||
## Can I create widgets?
|
||||
|
||||
Yes. [Custom Widgets](/docs/management/custom-widgets) use server-side API requests and safe JSX. They can be written in
|
||||
the workbench or created with an MCP-connected agent.
|
||||
|
||||
### Can Homarr be exposed to the internet?
|
||||
## Can Homarr be exposed to the internet?
|
||||
|
||||
Homarr supports authenticated and public deployments. Terminate HTTPS at a reverse proxy, keep Homarr updated, and grant
|
||||
public board access only when anonymous viewing is intended. See [Board access control](/docs/management/boards#access-control)
|
||||
and [Single sign-on](/docs/advanced/single-sign-on).
|
||||
|
||||
### Where are the logs?
|
||||
## Where are the logs?
|
||||
|
||||
Use **Management → Tools → Logs** for retained and live application logs. Container startup failures and older events are
|
||||
available through the deployment platform, for example `docker container logs homarr`.
|
||||
@@ -33,18 +34,18 @@ available through the deployment platform, for example `docker container logs ho
|
||||
Use the browser developer console only for client-side failures. Review logs before sharing them because URLs and other
|
||||
deployment details may be present.
|
||||
|
||||
### Why can Homarr not reach a service behind Gluetun?
|
||||
## Why can Homarr not reach a service behind Gluetun?
|
||||
|
||||
Gluetun's firewall blocks traffic that is not explicitly allowed. Configure its
|
||||
[`FIREWALL_OUTBOUND_SUBNETS`](https://github.com/qdm12/gluetun-wiki/blob/main/setup/options/firewall.md) for the relevant
|
||||
LAN or Docker subnet, and use an address reachable from the Homarr container.
|
||||
|
||||
### Where do I report a bug or request a feature?
|
||||
## Where do I report a bug or request a feature?
|
||||
|
||||
Use the [GitHub issue templates](https://github.com/homarr-labs/homarr/issues/new/choose). For community support, see
|
||||
[Get in touch](/docs/community/get-in-touch).
|
||||
|
||||
### Can Homarr be used commercially?
|
||||
## Can Homarr be used commercially?
|
||||
|
||||
Yes. Homarr is licensed under the [Apache License 2.0](/docs/community/license). Commercial users can support development
|
||||
through [OpenCollective](/docs/community/donate).
|
||||
@@ -19,7 +19,7 @@ If onboarding returns after a restart, check that `/appdata` is writable and per
|
||||
A **board** is a dashboard page containing apps, widgets, and Containers. Boards can be public or restricted to users
|
||||
and groups. Each user can choose a home board.
|
||||
|
||||

|
||||

|
||||
|
||||
See [Boards](/docs/management/boards) for responsive layouts, access control, appearance, and home-board settings.
|
||||
|
||||
@@ -28,7 +28,7 @@ See [Boards](/docs/management/boards) for responsive layouts, access control, ap
|
||||
An **app** is a saved shortcut: a name, URL, icon, and optional status check. Add an App tile or a Bookmarks widget to
|
||||
show apps on a board.
|
||||
|
||||

|
||||

|
||||
|
||||
See [Apps](/docs/management/apps) or [Icons](/docs/advanced/icons).
|
||||
|
||||
@@ -37,7 +37,7 @@ See [Apps](/docs/management/apps) or [Icons](/docs/advanced/icons).
|
||||
An **integration** is a server-side connection from Homarr to a supported service such as Sonarr, Home Assistant, or
|
||||
Docker. Integrations provide data and actions to widgets. They can also be linked to apps.
|
||||
|
||||

|
||||

|
||||
|
||||
See [Managing integrations](/docs/management/integrations) and the
|
||||
[integration catalog](/docs/category/integrations).
|
||||
@@ -47,7 +47,7 @@ See [Managing integrations](/docs/management/integrations) and the
|
||||
A **widget** is a board tile. Some widgets work on their own; others require one or more integrations. Configuration,
|
||||
supported integrations, and non-obvious limitations are documented on each widget page.
|
||||
|
||||

|
||||

|
||||
|
||||
See the [widget catalog](/docs/category/widgets).
|
||||
|
||||
@@ -56,7 +56,7 @@ See the [widget catalog](/docs/category/widgets).
|
||||
A **Container** groups board items and can be nested. A **rail** reserves a fixed area at the left or right of a
|
||||
responsive layout. Both are configured in board edit mode.
|
||||
|
||||

|
||||

|
||||
|
||||
See [Boards](/docs/management/boards#containers-and-rails).
|
||||
|
||||
@@ -65,7 +65,7 @@ See [Boards](/docs/management/boards#containers-and-rails).
|
||||
Users inherit permissions and default boards from their groups. Resource-specific access can further restrict boards
|
||||
and integrations.
|
||||
|
||||

|
||||

|
||||
|
||||
See [Users and groups](/docs/management/users) and [single sign-on](/docs/advanced/single-sign-on).
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 527 KiB |
|
After Width: | Height: | Size: 87 KiB |
@@ -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">
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
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.76.1"` | Overrides the image tag whose default is the chart appVersion |
|
||||
| image.tag | string | `"v1.76.2"` | 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 |
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
Pika Pods is a hosting platform for open source apps. You can deploy Homarr on Pika Pods with just a few clicks:
|
||||
|
||||
[__](https://www.pikapods.com/pods?run=homarr-v1)
|
||||
[__](https://www.pikapods.com/pods?run=homarr)
|
||||
@@ -38,13 +38,14 @@ This method is only useful, when you want to develop on Homarr or extend it with
|
||||
Install all dependencies using <code>pnpm install</code>
|
||||
</li>
|
||||
<li>
|
||||
Copy the `.env.example` file to `.env` and replace `DB_URL` with the full path you want to use for the sqlite
|
||||
database file. For example `DB_URL='/home/username/homarr/db.sqlite`
|
||||
Copy the `.env.example` file to `.env`. Its `DB_URL='./db.sqlite'` value is ready for local use and stores the
|
||||
database in the cloned repository. Replace it with an absolute writable path such as
|
||||
`DB_URL='/home/username/homarr/db.sqlite'` if you want to store the database elsewhere.
|
||||
</li>
|
||||
<li>
|
||||
Replace `AUTH_SECRET` and `SECRET_ENCRYPTION_KEY` both with a secure random string by using `openssl rand -hex 32`
|
||||
Run `openssl rand -hex 32` twice and set `AUTH_SECRET` and `SECRET_ENCRYPTION_KEY` to the two different generated
|
||||
values. Keep both values secret and preserve `SECRET_ENCRYPTION_KEY` with your backups.
|
||||
</li>
|
||||
<li>Replace `CRON_JOB_API_KEY` with a secure random string by using `openssl rand -base64 32`</li>
|
||||
<li>Run `pnpm run db:migration:sqlite:run` and wait that it completes</li>
|
||||
<li>Build the source using `pnpm build`</li>
|
||||
<li>
|
||||
|
||||
@@ -30,7 +30,7 @@ You can install Homarr directly from your Unraid Dashboard, no terminal required
|
||||
|
||||
Don't know whether you have the plugin installed or not? Search for this tab in the navigation:
|
||||
|
||||

|
||||

|
||||
:::
|
||||
|
||||
#### Installing
|
||||
@@ -41,7 +41,7 @@ After you've installed the Community Apps Plugin, you can install Homarr from th
|
||||
2. Search for "Homarr" and click on the result.
|
||||
3. Click on "Install" and adjust the settings to your liking.
|
||||
|
||||

|
||||

|
||||
|
||||
After you've installed Homarr, you can find it under the tab "Docker".
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Try the v2 beta
|
||||
sidebar_position: 2
|
||||
tags:
|
||||
- Getting started
|
||||
- Upgrade
|
||||
- Migration
|
||||
- Backup
|
||||
---
|
||||
|
||||
This page covers the temporary Homarr v2 public beta. Try the [live demo](https://app-v2.preview.homarr.dev/)
|
||||
without installing anything, or run the beta image published in the
|
||||
[public beta announcement](https://github.com/homarr-labs/homarr/issues/6600):
|
||||
|
||||
```sh
|
||||
docker pull ghcr.io/homarr-labs/homarr-test:v2
|
||||
```
|
||||
|
||||
For a fresh beta installation, follow the [Docker installation guide](/docs/getting-started/installation/docker),
|
||||
use `ghcr.io/homarr-labs/homarr-test:v2` as the image, and use a separate `/appdata` volume.
|
||||
Set `WORKSHOP_API_URL=https://v2.preview.homarr.dev` to use the preview Workshop.
|
||||
|
||||
Homarr runs database migrations automatically at startup; no manual database migration command is needed.
|
||||
Once v2 is released, existing installations can use their normal update process. The beta image below is temporary.
|
||||
|
||||
:::danger Back up before starting v2
|
||||
|
||||
In Homarr v1, go to **Management → Tools → Backup**, export a backup, and keep the downloaded file somewhere safe
|
||||
before starting v2. See [Backup and restore](/docs/management/backup) for details.
|
||||
|
||||
:::
|
||||
|
||||
## Try the beta with existing v1 data
|
||||
|
||||
1. Change the Homarr image to `ghcr.io/homarr-labs/homarr-test:v2`.
|
||||
2. Keep the existing `/appdata` mount, database variables, Redis configuration, and `SECRET_ENCRYPTION_KEY` unchanged.
|
||||
3. Start one Homarr replica and follow its logs. Startup must reach `Migration complete`; Homarr aborts startup when a
|
||||
database migration fails.
|
||||
4. Open Homarr and sign in. Confirm **Management → About** reports v2 before starting additional replicas.
|
||||
5. Keep using the beta image for beta updates. To return to v1, restore the pre-beta backup as described below.
|
||||
|
||||
Keep your other existing Compose settings, including environment variables. For Docker Compose, update the image and preview Workshop URL before the usual update:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
homarr:
|
||||
image: ghcr.io/homarr-labs/homarr-test:v2
|
||||
environment:
|
||||
WORKSHOP_API_URL: https://v2.preview.homarr.dev
|
||||
```
|
||||
|
||||
```sh
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
docker compose logs --follow homarr
|
||||
```
|
||||
|
||||
Do not add `-v` when stopping the Compose deployment; that can remove named volumes.
|
||||
|
||||
## What v2 migrates
|
||||
|
||||
The startup migration:
|
||||
|
||||
- creates the Custom Widget v2, Assistant, header preference, and branding storage;
|
||||
- adds Base/Mobile layout roles and left/right board gutters;
|
||||
- converts legacy Categories and dynamic sections into Containers and rails;
|
||||
- preserves legacy Custom Widget definitions, encrypted secrets, and board references for explicit migration.
|
||||
|
||||
Legacy Custom Widgets are not silently rewritten. In **Management → Custom Widgets**, entries marked
|
||||
**Migration required** provide a credential-free migration prompt. Review and test the resulting v2 definition before
|
||||
removing the archived v1 version. See [Custom Widgets: Import and Workshop](/docs/management/custom-widgets#import-and-workshop).
|
||||
|
||||
## Verify the upgraded instance
|
||||
|
||||
- Sign in with each enabled authentication method and verify administrator access.
|
||||
- Open every important board on desktop and mobile. Check Containers, former Categories, rails, item placement, and
|
||||
home-board assignments.
|
||||
- Test at least one read and one permitted action for each critical integration. Recheck integration and board access
|
||||
for representative Viewer, Editor, and Admin groups.
|
||||
- Check Docker or Podman endpoints, search engines, uploaded media, trusted certificates, scheduled tasks, WebSockets,
|
||||
and external Redis when used.
|
||||
- Review every legacy Custom Widget and migrate it only after its v2 preview and requests work.
|
||||
- Restart Homarr once more and confirm the logs contain no migration, database, Redis, authentication, or task errors.
|
||||
|
||||
## Roll back
|
||||
|
||||
Use the backup exported before the upgrade if you need to restore your v1 installation. Do not reuse a database that
|
||||
v2 has already migrated with v1.
|
||||
@@ -5,6 +5,6 @@
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"title": "Integrations",
|
||||
"description": "Integration are a principal part of homarr, it's where you can connect your other apps to interact with them with widgets or other capabilities, Homarr currently integrates with the following applications:"
|
||||
"description": "Connect Homarr to supported services so widgets can display data and run actions. Choose an integration to see its setup requirements and compatible widgets."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "llama.cpp"
|
||||
description: "Connect Homarr to a llama.cpp llama-server to monitor your local LLM: health, loaded model, generation speed and request status."
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
import { IntegrationHeader } from "@site/src/components/integrations/header";
|
||||
import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
|
||||
import { AddingIntegration } from "@site/src/components/integrations/adding";
|
||||
import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
|
||||
import { llamacppIntegration } from ".";
|
||||
import { llamacppWidget } from "@site/docs/widgets/llama-cpp";
|
||||
|
||||
<IntegrationHeader integration={llamacppIntegration} categories={["System Monitoring"]} />
|
||||
|
||||
The llama.cpp integration lets Homarr connect to a [llama-server](https://github.com/ggml-org/llama.cpp) instance so you can see the health of your local LLM, which model is loaded, and how fast it is generating — all from your dashboard.
|
||||
|
||||
### Widgets & Capabilities
|
||||
|
||||
<IntegrationCapabilites items={[{ widget: llamacppWidget }]} />
|
||||
|
||||
### Adding the integration
|
||||
|
||||
<AddingIntegration />
|
||||
|
||||
:::info URL configuration
|
||||
Use the root URL of your `llama-server`, for example `http://192.168.1.50:8080`. Homarr calls the `/health`, `/v1/models`, `/metrics` and `/slots` endpoints under that URL automatically.
|
||||
:::
|
||||
|
||||
:::tip Metrics
|
||||
Generation speed and token counters are read from the Prometheus endpoint at `/metrics`. Start `llama-server` with the `--metrics` flag so these values are available:
|
||||
|
||||
```shell
|
||||
llama-server --model /path/to/model.gguf --metrics
|
||||
```
|
||||
|
||||
Without `--metrics` the widget still shows health, model information, and active-request speed from `/slots`; metrics-backed counters and aggregate speed stay empty.
|
||||
:::
|
||||
|
||||
:::info Context and per-request speed
|
||||
Context (KV cache) usage and the in-flight request data are read from the `/slots` endpoint, which is available on every recent llama-server build and requires no extra flags. The per-request generation speed shown while a request is running is derived by tracking the request's decoded token count across polls, so it reflects the average speed of the request that is currently being generated.
|
||||
:::
|
||||
|
||||
### Secrets
|
||||
|
||||
llama-server does not require authentication by default. The integration only needs the URL of your server.
|
||||
|
||||
<IntegrationSecrets
|
||||
secrets={[
|
||||
{
|
||||
credentials: [],
|
||||
steps: [],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -0,0 +1,9 @@
|
||||
import { IntegrationDefinition } from "@site/src/types";
|
||||
|
||||
export const llamacppIntegration: IntegrationDefinition = {
|
||||
name: "llama.cpp",
|
||||
description:
|
||||
"Connect to a local llama.cpp llama-server to show model, throughput and request status on your dashboard.",
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/llama-cpp.svg",
|
||||
path: "../../integrations/llama-cpp",
|
||||
};
|
||||
@@ -32,3 +32,10 @@ import { calendarWidget } from "@site/docs/widgets/calendar";
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
### Trusted widget packages
|
||||
|
||||
The [Sonarr operations reference package](/docs/management/custom-widgets/trusted-packages) can combine two Sonarr
|
||||
integrations, display upcoming and missing episodes and queue progress, and submit a confirmed search for a missing
|
||||
episode. Search actions use the existing integration API key on Homarr's server and require interaction access.
|
||||
A successful command submission means Sonarr queued the search, not that it found or downloaded a release.
|
||||
@@ -16,7 +16,7 @@ import { networkControllerStatusWidget } from "@site/docs/widgets/network-contro
|
||||
<IntegrationHeader integration={unifiControllerIntegration} categories={["Network controller"]} />
|
||||
|
||||
This integration is using the [node-unifi](https://www.npmjs.com/package/node-unifi) library under the hood to connect to the Unifi Controller.
|
||||
Therfore it should support CloudKey Gen1, CloudKey Gen2, UnifiOS-based UDM-Pro controllers as well as self-hosted UniFi controllers.
|
||||
Therefore it should support CloudKey Gen1, CloudKey Gen2, UnifiOS-based UDM-Pro controllers as well as self-hosted UniFi controllers.
|
||||
|
||||
### Widgets & Capabilities
|
||||
|
||||
@@ -28,6 +28,14 @@ Therfore it should support CloudKey Gen1, CloudKey Gen2, UnifiOS-based UDM-Pro c
|
||||
|
||||
<AddingIntegration />
|
||||
|
||||
Use the local HTTPS address of your controller:
|
||||
|
||||
- UniFi OS consoles such as Dream Router, Dream Machine, Cloud Gateway, and CloudKey use `https://<host>` (port 443).
|
||||
- Self-hosted UniFi Network Servers normally use `https://<host>:8443`.
|
||||
- If you omit the port, Homarr tries port 443 first and then port 8443 when the first port cannot be reached.
|
||||
|
||||
Use a local administrator account. Cloud-only accounts and accounts with multi-factor authentication are not supported by the underlying client.
|
||||
|
||||
### Secrets
|
||||
|
||||
<IntegrationSecrets
|
||||
|
||||
@@ -18,7 +18,7 @@ Create an API key from the **Authentication** tab. Its value has the form `<id>.
|
||||
Send it in the `ApiKey` header:
|
||||
|
||||
```sh
|
||||
curl -H 'ApiKey: <id>.<token>' https://homarr.example.com/api/health/live
|
||||
curl -H 'ApiKey: <id>.<token>' https://homarr.example.com/api/info
|
||||
```
|
||||
|
||||
The same header authenticates tRPC requests under `/api/trpc`. Without a key, browser requests can use the Homarr
|
||||
@@ -27,6 +27,27 @@ session cookie.
|
||||
API keys do not expire automatically and act as the user that created them. Every request uses that user's group and
|
||||
resource permissions.
|
||||
|
||||
## Invitation dates
|
||||
|
||||
`POST /api/invites` and the tRPC `invite.createInvite` procedure accept `expirationDate` as an ISO 8601 timestamp with a
|
||||
timezone, for example `2026-12-01T18:00:00Z`. Homarr validates the timestamp and stores it as a date. tRPC callers should
|
||||
send the ISO string rather than a JavaScript `Date` object.
|
||||
|
||||
## Health probes
|
||||
|
||||
The liveness endpoint does not require authentication:
|
||||
|
||||
```sh
|
||||
curl https://homarr.example.com/api/health/live
|
||||
```
|
||||
|
||||
It returns JSON with overall and per-component status and latency. HTTP `200` means every dependency is healthy; HTTP
|
||||
`500` identifies an unhealthy component without exposing its internal error. Health responses are not cached.
|
||||
|
||||
## Pagination
|
||||
|
||||
General paginated endpoints accept at most 100 records per page, and shared search endpoints accept a limit of at most 100. Request subsequent pages instead of using a larger value.
|
||||
|
||||
## Permission behavior
|
||||
|
||||
Unauthorized operations return `FORBIDDEN`. List endpoints filter records on the server when resource-specific access
|
||||
@@ -77,3 +98,41 @@ and focused references under `/api/custom-widgets/`.
|
||||
|
||||
Use [Custom Widget agent authoring](../custom-widgets/agent-authoring) for the lifecycle and
|
||||
[MCP](../mcp) when the client supports Model Context Protocol.
|
||||
|
||||
## Trusted widget package lifecycle
|
||||
|
||||
The `customWidget.package` tRPC namespace separates authoring from activation. Administrators can save incomplete
|
||||
source with `saveDraft`, inspect `draftChanges`, run `preview`, and call `activate` after acknowledging trusted code.
|
||||
`getRaw` and `repair` on `customWidget` recover existing v2 definitions without rendering their code.
|
||||
|
||||
The package API also manages named local connections, per-placement guest grants, retained collectors, immutable
|
||||
archive import/export, forks, Workshop releases, staged updates, and rollback. `get` and `list` return management data
|
||||
without evaluating submitted package modules. Connection secret values are accepted separately by `saveConnection`
|
||||
and are not returned by connection listing or package export.
|
||||
For an existing connection, `secretUpdates` patches only supplied credential keys while retaining the others;
|
||||
`secrets` replaces the complete credential map. Supply only one. Credential patches require the saved connection
|
||||
kind and authentication method to match the submitted configuration.
|
||||
|
||||
`previewStorageGet` and `previewStorageSet` use the owning administrator's preview session and the same temporary
|
||||
store as its server handlers. They accept `previewId`, `scope`, and `key`; writes also accept `value`.
|
||||
Expired, discarded, and other users' sessions cannot read or write preview storage.
|
||||
|
||||
Connection configuration accepts `kind: "http" | "integration" | "service"`. A service connection supplies a
|
||||
`serviceType` and a JSON `settings` object without requiring a URL; encrypted `secrets` remain a separate input.
|
||||
Package requirements match both `kind` and `serviceType`. Server handlers access these settings through
|
||||
`getWidgetConnection` in `@homarr/widget-sdk/server`. Service diagnostics distinguish file readability, TCP
|
||||
reachability and settings-only checks; preview runs the adapter's actual database or protocol operation.
|
||||
|
||||
Installed widgets use `widget.customApi.packageQuery`, `packageAction`, `packageSubscription`, `packageStorageGet`,
|
||||
and `packageStorageSet`. Each invocation is bound to an existing board item and a manifest-declared handler; the host
|
||||
checks the current board and integration permissions. A public board does not grant action access automatically.
|
||||
Guest grants name a handler and exact allowed inputs, and are invalidated by changes to server behavior, dependencies,
|
||||
connection identity, or resource-selecting options.
|
||||
|
||||
Compiled surfaces are served through `/api/custom-widgets/artifacts/{itemId}/{digest}/{surface}` after board access
|
||||
checks. The optional `?asset=css` selects the corresponding stylesheet. Preview surfaces use
|
||||
`/api/custom-widgets/previews/{previewId}/{surface}` and require the administrator who created the preview.
|
||||
These routes do not expose server source or connection credentials.
|
||||
|
||||
The full package format, SDK examples, trust boundary, and activation behavior are described in
|
||||
[trusted widget packages](../custom-widgets/trusted-packages).
|
||||
@@ -30,10 +30,25 @@ needed to restore encrypted integration credentials.
|
||||
Select a Homarr backup ZIP. The browser previews entity counts, board names, and required database migrations before
|
||||
the archive is uploaded.
|
||||
|
||||
Restoring replaces the current SQLite database and cannot be undone. After a successful restore, the Next.js process
|
||||
restarts and the page reloads when Homarr is ready.
|
||||
Restoring replaces the current SQLite database and cannot be undone. It also invalidates the current database session.
|
||||
After a successful management restore, Homarr waits for the restarted server and sends the administrator to sign in
|
||||
again. The first-run onboarding restore keeps its existing board destination.
|
||||
|
||||
A backup can be restored on another Homarr instance. When its `SECRET_ENCRYPTION_KEY` differs, Homarr re-encrypts stored
|
||||
integration secrets for the target instance. Version metadata is used to apply compatible migrations.
|
||||
|
||||
The same restore flow is available during first-run onboarding, before the target database contains user data.
|
||||
|
||||
## Custom widget packages
|
||||
|
||||
Database backups include installed v2 definitions and trusted v3 source, compiled artifacts, current and previous
|
||||
activation metadata, local connections, placement bindings, guest grants, and managed widget storage/history.
|
||||
Restoring an encrypted backup re-encrypts widget credentials with the destination instance's key alongside native
|
||||
integration credentials. Compiled artifacts can be materialized again from the restored database without Workshop.
|
||||
|
||||
Keep `SECRET_ENCRYPTION_KEY` with database backups made using MySQL or PostgreSQL administration tools. The built-in
|
||||
ZIP export/restore remains specific to SQLite. A package export is portable distribution material and deliberately
|
||||
excludes local connections, credentials, stored values, and collected history; it is not a complete instance backup.
|
||||
|
||||
Files created directly by trusted Node code, external databases, remote-runner state, and changes already made to
|
||||
other applications are outside Homarr's managed widget storage. Include those resources in your own backup process.
|
||||
@@ -36,6 +36,11 @@ current Base layout.
|
||||
A **Container** groups apps, widgets, and nested Containers. It can have a label, collapse control, app-opening action,
|
||||
border color, and CSS classes.
|
||||
|
||||
Items can occupy the outermost rows and columns of a Container. The drop preview shows the position where the item
|
||||
will remain when released.
|
||||
Dragging an item onto another item of the same size swaps their positions. Displaced items snap directly to their
|
||||
previewed positions without a movement animation and stay there when you release the pointer.
|
||||
|
||||
A **rail** reserves one to three columns at the left or right of a non-Mobile layout. Rails stay fixed while the page
|
||||
scrolls and store their contents separately for each layout.
|
||||
|
||||
@@ -51,6 +56,8 @@ layout.
|
||||
- Press Escape during a pointer or touch interaction to restore the item to its previous position.
|
||||
|
||||
The add-content menu can create an app, integration, widget, or Container without leaving the board.
|
||||
When adding apps, click one app to add it immediately, or choose **Select multiple** to add several at once. `Ctrl`/`Cmd`
|
||||
click remains available as a shortcut.
|
||||
|
||||
## Board settings
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ The color of the icon indicates how long it is valid.
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
| Color | Meaning |
|
||||
| --------- | --------------------------- |
|
||||
@@ -34,7 +34,7 @@ Navigate to `Management` > `Tools` > `Certificates`.
|
||||
|
||||
To add a certificate click on the "Add certificate" button. The upload form expands directly on the certificates page.
|
||||
|
||||

|
||||

|
||||
|
||||
Then select the certificate file you want to add and click the "Add" button. You can cancel to collapse the form without
|
||||
leaving the page.
|
||||
@@ -43,7 +43,7 @@ leaving the page.
|
||||
|
||||
To delete a certificate you can click on the trash icon in the certificate card.
|
||||
|
||||

|
||||

|
||||
|
||||
The first click arms the action and the second click confirms the deletion in place; no confirmation dialog is opened.
|
||||
|
||||
|
||||
@@ -5,15 +5,27 @@ JSON is available when direct editing is faster.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Configure each API source, network scope, authentication method, and credential.
|
||||
2. Add named queries and actions. Values can reference widget options or invocation parameters.
|
||||
1. Start with a static widget, or add API sources, network access, authentication, and credentials when needed.
|
||||
2. Add named queries and actions for connected services. Values can reference widget options or invocation parameters.
|
||||
3. Define widget options and their defaults.
|
||||
4. Write JSX using `data`, `status`, `options`, and temporary `inputs`.
|
||||
5. Run **Test and preview**, inspect the response data and redacted request journal, then save.
|
||||
|
||||
Changing the definition, options, or credentials invalidates the previous preview. Preview actions are simulated unless
|
||||
Changing the definition, options, or credentials ends the previous API preview session. The last response remains visible
|
||||
as a labelled snapshot while you edit JSX. Run **Test and preview** again to refresh data and enable API interactions.
|
||||
Preview actions are simulated unless
|
||||
**Run actions against the configured API** is enabled for that preview session.
|
||||
|
||||
Use the workbench **Undo** and **Redo** buttons or `Ctrl/Cmd+Z` and `Ctrl/Cmd+Shift+Z` for changes across JSX, sources,
|
||||
requests, options, and metadata. History is bounded to 100 entries and about two million characters. **Reset to saved**
|
||||
restores the last saved definition. Credentials are excluded from this history and reset clears unsaved credential values.
|
||||
Undo and Redo retain pending credentials only when the source origin, network scope, and authentication destination
|
||||
are unchanged. Otherwise, enter the credentials again for the restored source.
|
||||
|
||||
Homarr keeps one recovery draft per signed-in user in this browser, replacing the previous draft as you edit. On returning
|
||||
to that widget, choose **Restore draft** or **Discard draft**. Saved credentials stay on the server; unsaved credentials must
|
||||
be entered again. Browser storage is optional, and drafts larger than one million characters are not stored for recovery.
|
||||
|
||||
Widget options support text, textarea, number, switch, select, multi-select, slider, date, time, color, icon, URL,
|
||||
duration, time zone, and JSON controls. Dynamic choices can come from a load query.
|
||||
|
||||
|
||||
@@ -6,8 +6,14 @@ Templates are JSX expressions interpreted inside a widget-local safety and rende
|
||||
- `status.requestId` — `{ loading, ok, status, statusText, error }`
|
||||
- `options.name` — saved widget configuration
|
||||
- `inputs.name` — temporary values from declarative `bind`
|
||||
- `host` — the widget's board context, dimensions, scale, and compact or advanced display mode
|
||||
|
||||
The v2 manifest requires a `default` source and supports up to eight sources, 64 requests, and 64 options. Source IDs are
|
||||
Existing callbacks may still use `host` as their parameter name, such as `data.hosts.map(host => host.name)`.
|
||||
Inside that callback, `host` refers to the current item; outside it, `host` refers to the widget context.
|
||||
|
||||
The v2 manifest supports empty `sources` and `requests` for widgets that do not need an API. Connected widgets support
|
||||
up to eight sources, 64 requests, and 64 options. Each request must reference a defined source; omitting `source` uses
|
||||
`default` only when a source with that ID exists. Source IDs are
|
||||
case-insensitively unique. Each request allows up to 32 static headers and 32 invalidation targets; a widget can contain
|
||||
up to 24 secrets, each up to 8,192 characters, and templates are limited to 50,000 characters. Validation reports the
|
||||
exact field when a limit is exceeded.
|
||||
|
||||
@@ -15,7 +15,10 @@ Custom Widgets may change during the v2 release cycle. Share feedback in the
|
||||
|
||||
:::
|
||||
|
||||
A **Custom Widget** is a validated `homarr-custom-widget-v2` definition containing API sources, queries and actions,
|
||||
Custom Widgets supports [trusted widget packages](./trusted-packages) with full TSX, CSS, modules, optional Node
|
||||
handlers, and advanced views, alongside the restricted v2 authoring format. Both can work without an API.
|
||||
|
||||
A **v2 Custom Widget** is a validated `homarr-custom-widget-v2` definition containing optional API sources, queries and actions,
|
||||
optional settings, and a safe JSX template.
|
||||
|
||||
Homarr executes requests on the server and renders the template in a restricted runtime. Source credentials are
|
||||
@@ -29,12 +32,16 @@ encrypted separately and are not included in exports, prompts, browser data, or
|
||||
|
||||
Administrators can create, import, duplicate, enable, export, publish, and delete definitions under
|
||||
**Management → Custom Widgets**. Adding or configuring one on a board also requires permission to modify that board.
|
||||
Use **Actions → Configure sources** to update deployment-specific URLs, network access, and encrypted credentials
|
||||
without reopening the authoring workbench.
|
||||
|
||||
Once placed, normal board permissions control rendering and requests. Queries can run for anonymous viewers on public
|
||||
boards; actions require authentication and the permission declared by the request.
|
||||
|
||||
## Authoring
|
||||
|
||||
- [Build a trusted widget package](./trusted-packages), configure local connections, and manage its Workshop releases.
|
||||
|
||||
- [Create a Custom Widget](./authoring) in the workbench or from a copied AI prompt.
|
||||
- [Use the safe JSX runtime](./custom-jsx) for data bindings, temporary inputs, and components.
|
||||
- [Browse the component reference](./component-reference).
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
title: Remote widget runners
|
||||
sidebar_position: 8
|
||||
---
|
||||
|
||||
A trusted widget server normally runs beside Homarr. The optional runner executes one installed widget package on
|
||||
another machine. Use it when the data lives on a NAS filesystem, a separate Docker network, or a host with a database
|
||||
or browser service that Homarr cannot reach. The runner has the privileges of the account starting it; its child
|
||||
process provides cancellation and restart, not security isolation.
|
||||
|
||||
## Install a runner
|
||||
|
||||
Build an archive using `homarr widgets pack widget.json --trust`, copy the archive to the target machine, and run:
|
||||
|
||||
```sh
|
||||
export HOMARR_WIDGET_RUNNER_TOKEN="$(openssl rand -hex 32)"
|
||||
homarr widgets serve widget.homarr-widget.json --trust --host 127.0.0.1 --port 8787
|
||||
```
|
||||
|
||||
The runner uses the compiled artifact and bundled JavaScript dependencies. It does not contact an npm registry or
|
||||
accept package uploads. Restart it with a different archive to update its installed version. Keep the token in the
|
||||
runner environment and Homarr's encrypted connection store. Use a private interface or an HTTPS reverse proxy when
|
||||
Homarr runs on another machine. `127.0.0.1` identifies the machine or container running the runner.
|
||||
|
||||
In Homarr, create an HTTP connection with the runner's base URL and bearer token. Bind it to the package's named
|
||||
`runner` connection. Only declared query, action, and subscription handlers are remotely callable. Migration
|
||||
handlers remain local lifecycle operations. Homarr still authorizes the dashboard placement and its named handler
|
||||
before calling the runner; the runner token represents the trusted Homarr caller.
|
||||
|
||||
## Call a remote handler
|
||||
|
||||
The local package's `server.ts` can forward to a handler installed on the runner:
|
||||
|
||||
```ts
|
||||
import { callWidgetRunner, defineWidgetServer, subscribeWidgetRunner } from "@homarr/widget-sdk/server";
|
||||
|
||||
export default defineWidgetServer({
|
||||
status: (input, context) => callWidgetRunner(context, { connection: "runner", handler: "status", input }),
|
||||
activity: async function* (input, context) {
|
||||
yield* subscribeWidgetRunner(context, { connection: "runner", handler: "activity", input });
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Declare `status` as a query and `activity` as a subscription in both manifests. The remote package accesses its own
|
||||
environment and filesystem. It has no automatic backchannel to Homarr's storage, native integrations, or connection
|
||||
credentials. Put those operations in the local forwarding handler when needed.
|
||||
|
||||
The pull subscription transport waits up to ten seconds per read, buffers at most 32 values or 8 MiB, and closes
|
||||
subscriptions that are not read for sixty seconds. Returning from an async generator completes the stream. Use
|
||||
`context.signal` to release connections and stop work on cancellation.
|
||||
|
||||
## Apps without an HTTP API
|
||||
|
||||
An API request is optional. A widget may use only JSX, local browser state, a timer, or static information. Trusted
|
||||
server handlers can read files, speak other protocols, or query a database using the runner's location and account.
|
||||
|
||||
For an application that writes a JSON status file, a remote handler can read that file:
|
||||
|
||||
```ts
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { defineWidgetServer } from "@homarr/widget-sdk/server";
|
||||
|
||||
export default defineWidgetServer({
|
||||
status: async (_input, context) => {
|
||||
const path = process.env.APP_STATUS_FILE;
|
||||
if (!path) throw new Error("Set APP_STATUS_FILE on the runner");
|
||||
return JSON.parse(await readFile(path, { encoding: "utf8", signal: context.signal }));
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
For an application's SQLite database, Node 24 provides a built-in driver:
|
||||
|
||||
```ts
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { defineWidgetServer } from "@homarr/widget-sdk/server";
|
||||
|
||||
export default defineWidgetServer({
|
||||
recentJobs: () => {
|
||||
const path = process.env.APP_DATABASE;
|
||||
if (!path) throw new Error("Set APP_DATABASE on the runner");
|
||||
const database = new DatabaseSync(path, { readOnly: true });
|
||||
try {
|
||||
return database.prepare("SELECT name, status FROM jobs ORDER BY id DESC LIMIT 20").all();
|
||||
} finally {
|
||||
database.close();
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Adapt the query to the application's actual schema. For PostgreSQL, MySQL, or MQTT, declare an exact version of a
|
||||
JavaScript client dependency, bundle it when packing, and use the runner environment for service credentials. MQTT
|
||||
subscriptions fit async generator handlers; close the broker client when the handler's signal aborts. Persistent
|
||||
history belongs in Homarr's collection and history APIs or the application's database.
|
||||
|
||||
A browser-only application can be adapted through an existing browser automation service reachable from the runner.
|
||||
Call that service from a server handler and return structured data to the tile. Browser binaries and native addons
|
||||
are not shipped inside portable widget archives. Directly bundling a library that requires native addon loading is
|
||||
rejected; use an independently installed service for that runtime dependency.
|
||||
|
||||
These paths enable adapters for many self-hosted applications. The application must still expose something the
|
||||
runner can read or control, such as HTTP, a socket, a file, a database, or a browser session.
|
||||
@@ -1,15 +1,21 @@
|
||||
# Configure requests and security
|
||||
|
||||
Most widgets use a single source named `default`; the manifest must contain it. Extra keyed sources are available for widgets
|
||||
Widgets can use empty `sources` and `requests` objects when they do not need an API. Connected widgets may use a source
|
||||
named `default` or explicitly name the source on every request. Extra keyed sources are available for widgets
|
||||
that combine APIs. Source IDs are case-insensitively unique. Base URLs must use HTTP(S), contain no embedded credentials,
|
||||
query string, or fragment, and sources can be `public`, `private`, or `loopback`. Authentication is `none`, `bearer`,
|
||||
`basic`, an API-key header, or an API-key query parameter. Credentials belong to the widget but remain outside its manifest.
|
||||
Source origins are fixed by the definition and source-setup flow; widget options and invocation parameters cannot redirect a
|
||||
request to a different origin.
|
||||
|
||||
Use `private` for LAN addresses, Docker service names, and Tailscale's `100.64.0.0/10` range. Use `loopback` for
|
||||
`127.0.0.1` or `::1`. Requests run from Homarr: in Docker, loopback addresses point to the Homarr container, while a
|
||||
service name such as `http://sonarr:8989` must resolve on its Docker network. Public sources cannot reach private or
|
||||
loopback addresses. Cloud metadata, link-local, multicast, and other reserved targets remain blocked.
|
||||
|
||||
## Named requests
|
||||
|
||||
Requests are keyed by ID. `source`, `kind`, `method`, `trigger`, `auth`, and `permission` have sensible defaults, so a load query can be as small as:
|
||||
Requests are keyed by ID. With a source named `default`, a load query can be as small as:
|
||||
|
||||
```json
|
||||
{ "status": { "path": "/api/status" } }
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
---
|
||||
title: Trusted widget packages
|
||||
sidebar_position: 2
|
||||
tags: [Custom Widgets, JSX, Workshop]
|
||||
---
|
||||
|
||||
# Trusted widget packages
|
||||
|
||||
Trusted packages let administrators write complete React/TSX modules, import CSS and dependencies, and optionally run
|
||||
Node server handlers. A package can provide a dashboard tile, an advanced widget view, and a custom configuration
|
||||
view. Static content, clocks, interactive local state, and other source-free widgets need no API connection.
|
||||
|
||||
Open **Management → Custom Widgets → Packages**. The source-free world clock is the default starter. Thirteen reference starters cover clocks, timers, checklists,
|
||||
downloads, Sonarr, racks, backups, household controls, service overview, media, Beszel, SQLite, and MQTT. Each new widget receives
|
||||
its own local package identity. The Beszel
|
||||
reference package demonstrates a connected widget using Homarr's shared SDK and integration data.
|
||||
|
||||
## Trust and execution
|
||||
|
||||
Packages are trusted installed code. Browser modules run within Homarr; Node handlers have Homarr's server
|
||||
privileges. Review the complete source and dependency list before checking the trust acknowledgement. Both
|
||||
**Run preview** and **Activate saved draft** execute package code. An action simulation is not a security sandbox:
|
||||
trusted code can make direct network requests and external changes outside the SDK.
|
||||
|
||||
Workshop browsing displays screenshots, source text, dependencies, and immutable release metadata. It does not run
|
||||
package code. Importing a package creates a disabled draft. Connection credentials are entered separately on your
|
||||
Homarr installation.
|
||||
|
||||
A supplied archive can include compiled browser and server code. Its digest verifies that those bytes are intact;
|
||||
it does not prove they were built from the displayed source. Inspect **Compiled artifact** and the dependency lock
|
||||
as well as the source before activating a supplied build. Rebuilding from reviewed source produces a new local
|
||||
artifact; dependency installation happens during that build, never while rendering a dashboard.
|
||||
|
||||
## Package, installation, and placement
|
||||
|
||||
| Concept | Contains |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------------- |
|
||||
| Package | Portable manifest, source files, exact dependency versions, named connection requirements, option definitions |
|
||||
| Installation | Saved source draft, active compiled release, previous activation, local connection bindings, enabled state |
|
||||
| Placement | A tile on a board, its option values, connection overrides, board permissions, widget state |
|
||||
|
||||
One installation can appear on several boards. Activating it updates all its placements. A placement's saved option
|
||||
values survive updates; changing package defaults does not overwrite those saved values.
|
||||
|
||||
## Customize a native widget
|
||||
|
||||
Administrators can choose **Customize as a widget package** from supported native widget menus. Homarr prepares a
|
||||
reference package and reuses its existing integration connections. The original native widget remains on the board.
|
||||
Save the new package, preview and activate it, then add its placement to the board. The package has its own identity
|
||||
and can be edited independently of the native widget.
|
||||
|
||||
## Author a package
|
||||
|
||||
The workspace has a file explorer, TSX/CSS/JSON editor, a connection panel, activation and board controls, and a
|
||||
preview canvas with adjustable width and height. Use `widget.json` to edit package metadata. This is a virtual view
|
||||
of the package envelope excluding `files`; each source file is edited separately in the explorer.
|
||||
|
||||
Use **Add**, **Rename**, and **Remove** to organize source files. When renaming a file, update its imports and any
|
||||
manifest entrypoints that reference the old path. JSX imports and ordinary React hooks are supported by the trusted
|
||||
package compiler; the restricted v2 interpreter does not process these files.
|
||||
|
||||
The package envelope uses `$schema: "homarr-widget-package-v3"` and contains:
|
||||
|
||||
| Field | Purpose |
|
||||
| -------------- | ------------------------------------------------------------------------------------ |
|
||||
| `manifest` | Package identity, release version, SDK version, entrypoints, handlers, and metadata |
|
||||
| `files` | Relative file paths mapped to source text |
|
||||
| `dependencies` | Additional npm dependencies pinned to exact versions |
|
||||
| `connections` | Named HTTP, Homarr integration, or service requirements; local bindings are separate |
|
||||
| `options` | Controls, labels, defaults, and constraints for placement settings |
|
||||
|
||||
`manifest.entrypoints.tile` is required. `advanced`, `configuration`, and `server` are optional. Entrypoints must
|
||||
refer to files in the package. A package with named server handlers needs a server entrypoint. The `manifest.id`
|
||||
identifies the package across releases; `manifest.version` uses a complete semantic version such as `1.0.0`.
|
||||
|
||||
Homarr provides React, Mantine, and `@homarr/widget-sdk` as shared host modules. Referenced Tabler icons are bundled
|
||||
from Homarr's installed version, which is recorded in the artifact lock. The reference packages show how to use the SDK for host dimensions, theme and locale, options, state, commands, data queries, and advanced
|
||||
views. Put additional dependencies in the package's dependency map rather than copying Homarr's internal source tree.
|
||||
|
||||
Production images include the widget compiler's JavaScript, native esbuild executable, and bundled icon sources.
|
||||
When customizing Homarr's standalone build, retain these runtime assets and scope tracing includes to their files;
|
||||
whole-package recursive globs can fail on pnpm dependency directory symlinks.
|
||||
|
||||
Use `WidgetScope` to compose components with independent state and command IDs. Commands registered through
|
||||
`useWidgetCommands` remain registered across unrelated host updates and are removed when their component unmounts.
|
||||
`useWidgetServices().confirm(...)` resolves to `true` on confirmation and `false` on cancellation or dismissal,
|
||||
including Escape, a backdrop click, or the close button.
|
||||
|
||||
## Convert an existing Custom JSX widget
|
||||
|
||||
Choose **Convert to a widget package** from a valid Custom JSX widget's list row or editor. Conversion uses its saved
|
||||
definition and creates a separate disabled package. The original definition and its board placements remain in
|
||||
place. Local connections and encrypted credentials are copied on the server, keeping them out of portable source.
|
||||
|
||||
The initial package retains a compatible JSX surface and options. Converted dynamic options remain available through
|
||||
the authored configuration surface. Extend the package with TSX modules, CSS, or an advanced
|
||||
view in the workspace. Review the generated files, preview, and explicitly activate the package before adding it
|
||||
to a board. Conversion itself does not execute or replace the original widget. After activation, **Replace an
|
||||
existing Custom JSX tile** lets you choose one original placement and explicitly replace it. Position, size, refresh
|
||||
interval and configured values are retained; the original definition and other placements stay available. Use
|
||||
**Add to board** when you want a separate tile instead.
|
||||
|
||||
## Assistant authoring
|
||||
|
||||
Choose **Ask Assistant** in the workspace to describe a widget or request changes. Shortcuts help improve the
|
||||
current widget, add an advanced view, or fix current errors. This opens Homarr's existing Assistant; it uses the
|
||||
visible unsaved draft, public SDK guidance, reference packages, and source validation diagnostics.
|
||||
|
||||
The Assistant reads source on demand. Local connection settings, bindings, credential fields, preview option
|
||||
values, and runtime response data are excluded. Recognized credential literals are redacted from source context;
|
||||
keep secrets in local connections. Files with redacted credentials must be edited directly before the Assistant can
|
||||
replace them. Files omitted because a response reached its context budget must be read separately before proposing changes.
|
||||
Individual files over 180,000 characters and manifests over 60,000 characters require direct editing.
|
||||
|
||||
Proposals display each changed file before and after in the Assistant. **Apply to draft** applies the complete
|
||||
proposal as one Undo step. If the draft changes while a proposal is waiting, the Assistant must read it again and
|
||||
propose against the new revision. Applying a proposal does not save, trust, preview, activate, or publish it.
|
||||
|
||||
Preview evidence identifies only the exact current candidate that compiled and received a preview session.
|
||||
Compilation does not prove successful browser rendering or live actions. Source, option, and binding changes
|
||||
invalidate that evidence; use the preview canvas to verify the final result before activation.
|
||||
|
||||
## Save, preview, and activate
|
||||
|
||||
Drag the divider between source and preview to resize the workspace. The divider also supports arrow keys,
|
||||
Home/End, and double-click to reset. The preference is saved in your browser. On smaller screens, switch between
|
||||
Code and Preview without unmounting the working preview.
|
||||
|
||||
The preview offers compact, wide, phone, and tablet viewport presets, independent width/height controls, and a
|
||||
25–200% display scale. Scaling changes both the visual surface and the SDK's visible dimensions. Light and dark
|
||||
preview modes stay local to the preview; they do not change your Homarr theme. These presentation controls do not
|
||||
rebuild code, clear query data, or require another preview session.
|
||||
|
||||
The source editor understands the package's TypeScript and TSX files together. Press **Ctrl+Space** for typed
|
||||
completions, hover a symbol for its type and documentation, or press **F12** to open its definition. Definitions
|
||||
inside the installed SDK or its dependencies open as read-only excerpts. **F2** renames a symbol across package
|
||||
files; the complete rename is one Undo operation. **Format** uses TypeScript formatting for TS/TSX files.
|
||||
|
||||
Semantic diagnostics appear in the editor and its expandable issue list. They use declarations generated from this
|
||||
Homarr installation's actual SDK, React, Mantine, and Node dependencies. Include `.d.ts` files when another package
|
||||
needs declarations that are not installed with Homarr. These editor diagnostics do not block saving a draft or
|
||||
replace the preview compiler's runtime compatibility checks. Analysis runs in a local browser worker, without
|
||||
executing your source or downloading code or types from a CDN.
|
||||
|
||||
For Homarr development, `pnpm dev` and the Next.js build regenerate the worker and installed declarations. After
|
||||
editing SDK source while the dev server is already running, run `pnpm --filter @homarr/custom-widgets generate:editor`
|
||||
and reopen the workspace. Generated assets are included in the standalone image's public directory.
|
||||
|
||||
1. **Save draft** persists the current source without building or executing it. Incomplete TSX and metadata JSON can
|
||||
be saved for later repair. Saving a draft leaves the active dashboard release running.
|
||||
2. Review the code and dependencies, acknowledge trust, and choose **Run preview**. The preview builds the current
|
||||
editor contents and runs them in a temporary session. Select tile, advanced, or configuration view when provided.
|
||||
**Preview options** controls the configuration used for this session.
|
||||
3. Save your final changes, review the activation's placement count, then choose **Activate saved draft**. The server
|
||||
validates and builds the saved source before switching the installation. Activation enables the installation.
|
||||
4. Choose a board and configure the new placement under **Activation & boards → Add to board**.
|
||||
|
||||
Changing source, options, local preview bindings, or the live-action mode invalidates execution evidence for the previous candidate.
|
||||
The last successful view remains visible with a stale label and paused SDK activity while you edit or fix a failed
|
||||
build. Its session is released after a replacement loads successfully or the workspace closes. Re-run preview to
|
||||
inspect the changed inputs. Pausing SDK activity does not sandbox arbitrary effects inside trusted code. Preview widget storage is temporary:
|
||||
frontend storage hooks and server handlers share the same preview-session values, isolated from dashboard storage
|
||||
and other preview sessions. Completing an action refreshes queries and managed storage in that preview.
|
||||
SDK named actions are simulated by default. Enable **Allow live SDK actions for this preview session** and run
|
||||
preview again to operate the real service through SDK actions. The session label shows its mode. Changing source,
|
||||
bindings, or connection settings turns this option off. Direct behavior inside trusted code remains the owner's responsibility.
|
||||
|
||||
## Synthetic preview scenarios
|
||||
|
||||
Authors can include `previewScenarios` in package source to review success, loading and partial failures with
|
||||
synthetic data. A scenario maps declared query handler names to JSON results or errors:
|
||||
|
||||
```json
|
||||
{
|
||||
"previewScenarios": {
|
||||
"partialFailure": {
|
||||
"label": "Storage unavailable",
|
||||
"queries": {
|
||||
"memory": { "data": { "usedPercent": 38 } },
|
||||
"storage": {
|
||||
"error": { "message": "Synthetic storage collector timeout", "code": "TIMEOUT" },
|
||||
"delayMs": 700
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Select a scenario when creating a preview. Its ID and the exact compiled source are stored in that preview session;
|
||||
changing the scenario requires a new preview. Required local connections can remain unbound. Fixture queries return
|
||||
the authored JSON without starting the package's server module or calling a configured service. A query without a
|
||||
fixture fails explicitly; it never falls back to live data. Each handler receives the same fixture regardless of its
|
||||
input. Declared input validation still applies.
|
||||
|
||||
SDK actions are simulated in fixture mode, and enabling live actions together with a scenario is rejected.
|
||||
Subscriptions are unavailable in this bounded query-fixture mode. The SDK capability helper remains available so
|
||||
action controls can display their simulated state. Production dashboard queries always execute the active package's
|
||||
normal handlers; a preview scenario never changes an installation or its placements.
|
||||
|
||||
[Download the complete example](/custom-widgets/preview-scenarios.json) for two independent local metrics with ready,
|
||||
delayed and partial-failure scenarios. It needs no API or local connection. Import the source, review trust, and
|
||||
preview its named scenarios. Its ordinary handlers read local memory and filesystem metrics when used without a scenario.
|
||||
|
||||
Scenarios support up to 16 names, 64 query fixtures each, 2 MiB of total fixture JSON, and delays of up to 30 seconds.
|
||||
Errors can use `BAD_REQUEST`, `NOT_FOUND`, `TIMEOUT` or `INTERNAL_SERVER_ERROR`; the latter is the default. Request
|
||||
cancellation and discarding the session cancel pending fixture delays.
|
||||
|
||||
Fixtures are explicitly authored package source and are included in source exports, offline archives and Workshop
|
||||
releases. Homarr does not capture live responses into scenarios. Use invented values and review fixture contents
|
||||
before sharing. This is SDK simulation, not a security sandbox: trusted browser code can still make direct requests,
|
||||
and compiling uncached dependencies can require registry access. Review source and acknowledge trust as usual.
|
||||
|
||||
## Local connections
|
||||
|
||||
The **Unified downloads** starter accepts a primary and optional second download integration. Each source has its
|
||||
own queue, loading state, error and retry control, so a failed client leaves the other queue usable. Advanced view
|
||||
adds filtering, sorting, per-client and per-item controls, and pause/resume for all or selected clients. Batch
|
||||
results identify successful and failed clients individually. Per-item actions resolve the current item from the
|
||||
selected client, and confirmed removal keeps downloaded files. The composed overview reuses the same modules.
|
||||
Controls consult the current viewer's capabilities. Guests need an explicit grant for an exact input such as
|
||||
`{"connection":"secondary","operation":"pause"}` on `control`; batch grants additionally list every target in
|
||||
`connections`. No guest action is enabled merely by installing the starter.
|
||||
|
||||
Choose **Service** for MQTT, SQLite, SSH, local files or another protocol. Service connections require a
|
||||
`serviceType` matching the package requirement, arbitrary nonsecret JSON settings, and optional encrypted
|
||||
credentials. They do not require an HTTP URL. For example, declare
|
||||
`{kind: "service", serviceType: "sqlite", label: "Database"}` in a package and bind a local connection with
|
||||
settings such as `{"path":"/data/app.sqlite"}`. MQTT commonly uses `{"url":"mqtt://mosquitto:1883","topics":["sensors/#"]}`;
|
||||
put its username and password in the encrypted credentials field. Leave that field blank to keep existing
|
||||
credentials, or select **Replace saved credentials** to clear them explicitly.
|
||||
|
||||
Server handlers call `getWidgetConnection(context, {connection: "database", serviceType: "sqlite"})` from
|
||||
`@homarr/widget-sdk/server` to obtain the local settings and secrets for a Node library. These credentials are
|
||||
never returned by the browser connection API. Authors must keep this object server-side and return only the
|
||||
data their widget needs. File paths resolve inside Homarr's container/process; mount the required files there
|
||||
and give the Homarr process access. Use a remote runner when the adapter must execute on another machine.
|
||||
|
||||
Diagnostics report what they actually verify: SQLite/file connections check regular-file readability, and MQTT
|
||||
connections check TCP reachability. Neither check claims database-query validity or MQTT authentication. Run
|
||||
preview to exercise the real query or authenticated subscription. Other service types report configured settings
|
||||
and require their adapter's preview for a protocol check. The **SQLite and local status** and **MQTT live topics**
|
||||
starters include complete adapters; MQTT pins its reviewed `mqtt` dependency, while SQLite uses Node's built-in
|
||||
module. Their package READMEs document connection settings, limits, cancellation and deployment details.
|
||||
|
||||
Declare named requirements in the package and bind them to reusable local connections in **Connections**. A
|
||||
connection can point to an existing Homarr integration or an HTTP endpoint. HTTP setup supports a separate server
|
||||
and browser URL, Basic authentication, bearer tokens, secret headers or query parameters, cookies, TLS verification, and a timeout.
|
||||
|
||||
Server requests originate from Homarr's server or container. Examples include `http://sonarr:8989`,
|
||||
`http://192.168.1.20:8080`, and a Tailscale address. `127.0.0.1` addresses the network namespace running Homarr; it does
|
||||
not address the dashboard viewer's browser or a different container. Enter full URLs including the protocol. For a bare address such as `sonarr:7573`, the connection form offers an
|
||||
explicit **Use http://sonarr:7573** suggestion. Choosing it adds the protocol while preserving the supplied host,
|
||||
port, and path; use HTTPS when required by the service.
|
||||
|
||||
**Diagnose** shows the reached stage, resolved addresses, HTTP status and timing, and top-level JSON field names
|
||||
without response values. The primary result distinguishes name resolution, network, TLS, HTTP and authentication
|
||||
failures; expand **Technical details** for the full diagnostic. **Save installation bindings** changes the defaults inherited by every placement.
|
||||
Individual placements can override connections in their board configuration. **Edit connection** updates a reusable
|
||||
connection for every widget that uses it. Empty credential fields retain stored credentials; changing authentication
|
||||
requires entering the credentials for the new method.
|
||||
For Basic authentication, you can update the username or password independently: leaving the other field blank
|
||||
preserves its saved value. Updating a bearer token, cookie, username, or password also preserves a saved TLS private key.
|
||||
|
||||
Connection credentials are encrypted in Homarr's database. Connection forms and their credential fields are not
|
||||
included in the package editor's Undo/Redo or browser recovery. Keep credentials out of source files: source files
|
||||
are portable and can be exported or published.
|
||||
|
||||
## Recovery and operational rollback
|
||||
|
||||
Disabling an enabled guest action does not depend on the validity of its edited input JSON. You can revoke access
|
||||
immediately from the action's Disable control while repairing those inputs.
|
||||
|
||||
Activation also coordinates with widget settings, board saves, additions, and board duplication. If a package
|
||||
changes while one of these saves is waiting, Homarr asks you to reload before saving again so an older
|
||||
configuration cannot overwrite its migration. Changing a placement's connection overrides invalidates guest
|
||||
action approvals tied to the previous bindings.
|
||||
|
||||
**Undo/Redo** covers the whole source document: metadata, files, and installation name. History is bounded to 100
|
||||
entries and 2 MB in the current editor session. **Reset to saved** restores the saved draft. Homarr keeps one browser
|
||||
recovery draft per signed-in owner, up to 1 MB, and offers to restore it when opening the matching package.
|
||||
Recognized credential literals are redacted from recovery source; use connection fields for secrets.
|
||||
|
||||
Package release history is separate from editor Undo/Redo. **Roll back previous activation** restores the previous
|
||||
compiled release and its activation snapshot of local bindings, placement options, and widget data. You must
|
||||
acknowledge discarding newer widget data. Calls already made to an external service cannot be reversed by rollback.
|
||||
Your source draft remains available for further work.
|
||||
|
||||
**Disable installation** stops that installation across all placements. It is independent of saving a source draft.
|
||||
|
||||
## Import, export, fork, and update
|
||||
|
||||
**Import package** accepts source JSON or a `homarr-widget-archive-v3` archive. Archives contain source plus a compiled
|
||||
artifact; compatible matching artifacts can be activated without rebuilding dependencies. Imports create disabled
|
||||
drafts for review. **Export source draft** exports current editor contents, including incomplete work.
|
||||
**Export active archive** exports the activated release and its compiled artifact. Local connection credentials and
|
||||
board placements are not part of these exports.
|
||||
|
||||
**Stage archive update** replaces the installation's saved draft after explicit acknowledgement and keeps its active
|
||||
release running. **Review saved draft changes** shows changed files before and after, dependency changes, connection
|
||||
requirements, and configuration changes. **Fork as a new package** copies saved source to a new disabled installation
|
||||
with a new package identity; it does not change the original installation's board placements.
|
||||
|
||||
## Portable widget collections
|
||||
|
||||
A `homarr-widget-collection-v1` archive groups several independent packages with named shared connection slots.
|
||||
Import binds a slot once and fans it out to every member that needs it. All imported packages start as disabled
|
||||
drafts; importing does not install dependencies or execute widget code. Each package keeps its own identity,
|
||||
activation, updates, settings and dashboard placements.
|
||||
|
||||
The complete collection is validated before creating installations. Invalid member code checksums, mismatched
|
||||
connection types or invalid portable option defaults reject the import with no new installations. Installation
|
||||
records are committed together; immutable artifact cache entries from a database failure may remain until cleanup.
|
||||
|
||||
Export uses the selected installations' activated archives. Shared local bindings become named requirements, while
|
||||
their connection IDs, URLs and credentials stay local. Export also excludes placement settings and widget storage.
|
||||
Only explicit authored defaults belong in the portable blueprint. Workshop and fork attribution is retained, and
|
||||
normal package updates retain the collection's membership and connection mapping. Review code literals before
|
||||
sharing, since package source is exported verbatim.
|
||||
|
||||
From the installed package library, choose **Collections** to export selected active packages or resume an imported
|
||||
collection. Import its JSON archive using **Import**. The setup page asks for shared connections once, lets you
|
||||
choose widgets and their options, and activates the selected packages after you acknowledge trust. **Activate and
|
||||
add missing widgets** places them on the selected board. Each member reports its own result, and retrying skips
|
||||
placements already present on that board. You can stop after the current member finishes.
|
||||
|
||||
See the [collection CLI commands](../../advanced/command-line/index.mdx#share-several-widgets-as-a-collection) for
|
||||
packing, validation and importing through an admin API key. Collections support up to 32 widgets and 128 MiB.
|
||||
|
||||
## Workshop lifecycle
|
||||
|
||||
In Workshop, select a release, inspect source files and dependencies, and optionally bind its named connections.
|
||||
**Import for review** opens the package workspace with a disabled local installation. Preview and activate it after
|
||||
reviewing trust, then add it to a board.
|
||||
|
||||
The **Packages** library shows each installation’s version, author, and **View on Workshop** link. Choose
|
||||
**Check for updates**, then **Review update** to load a release into the editor. If your saved draft has local
|
||||
changes, confirm its replacement first. The active release continues running until you activate the reviewed draft.
|
||||
Use **Duplicate** or export local changes you want to keep before replacing them.
|
||||
|
||||
Choose **Publish to Workshop** from the package’s action menu or its editor. Packages and Custom JSX widgets use the
|
||||
same publishing form: sign in, add a title, description, and optional screenshots, then publish. The signed-in Workshop
|
||||
account receives attribution. Homarr updates your existing listing or creates an attributed copy of another author’s
|
||||
package automatically. **Release details** contains the version and optional release notes; updates suggest the next
|
||||
patch version. Review source, dependencies, and fixtures before publishing. Local bindings and credentials remain
|
||||
on the Homarr installation.
|
||||
|
||||
The management sidebar can collapse to icons using its toggle. It collapses automatically in the package editor to
|
||||
leave more room for code and preview; expand it whenever you need the full labels. On mobile, use the header menu.
|
||||
|
||||
## Reference packages with local services
|
||||
|
||||
**Sonarr operations** supports two native Sonarr connections, upcoming and missing episodes, queue progress, source
|
||||
filtering, and confirmed episode-search actions. Compact view shows four rows; the advanced view shows all loaded
|
||||
rows with text filtering. Each server contributes up to 100 missing or queued episodes. An unavailable second
|
||||
server does not hide the primary server's results. Searching requires native integration interaction access and
|
||||
submits a Sonarr command; a queued command does not prove an eligible release was found or downloaded.
|
||||
|
||||
**Filesystem backup monitor** reads backup metadata and an optional JSON status report mounted into Homarr. Its
|
||||
README contains the success/failure/running report format, timestamps, duration, size and retained run history.
|
||||
The optional **Run backup** action uses a fixed executable and argument array deliberately configured in
|
||||
`command.ts`. It runs through Node `execFile` without a shell, requires full placement permission, and never accepts
|
||||
command text from the browser. Preview simulates it. Mount the executable into Homarr, then review and activate its
|
||||
configuration. Backup activity and status reports do not verify restore integrity.
|
||||
|
||||
**Media and camera wall** combines native media sessions with optional authenticated camera snapshots and an Immich
|
||||
image gallery. Camera paths share a bound HTTP connection; its credentials stay on the server. Immich uses a secret
|
||||
`x-api-key` header with `asset.read` and `asset.view`. The package fetches images server-side and returns bounded
|
||||
image data to the widget. It supports up to eight camera snapshots and twelve Immich thumbnails. Clicking an image
|
||||
opens the advanced view with shared selection. Camera snapshots poll; this reference does not stream RTSP or other
|
||||
continuous video, and individual images must remain under 675 KiB.
|
||||
|
||||
## Optional history collection
|
||||
|
||||
A placement’s **History collection** shows its latest retained sample and time. Expand the sampled-point table for
|
||||
recent values or **Technical details** for the stored record.
|
||||
|
||||
Expand a placement’s **History collection** to select a named query handler, input JSON, value path, unit, sampling
|
||||
interval, retention period, and maximum sample count. Collection is disabled by default. Enabling it runs the query
|
||||
on Homarr’s server even when the dashboard is closed. Pause it without deleting samples, or explicitly discard
|
||||
the collection and its history. The collection panel shows recent samples and request errors; package code can
|
||||
consume retained history through the widget SDK.
|
||||
|
||||
## Guest actions
|
||||
|
||||
A package can declare a named action as available for guest grants. Under a placement's **Guest actions**, the owner
|
||||
enables that specific handler for an explicit JSON array of permitted inputs. Other inputs remain denied. Changing
|
||||
server code, dependencies, or bound connection configuration invalidates the grant and requires owner renewal.
|
||||
Board access and the package's declared permissions still govern normal authenticated usage.
|
||||
|
||||
## Repair a v2 widget
|
||||
|
||||
The existing restricted v2 workbench remains available from the installed widgets tab. Invalid v2 definitions expose
|
||||
**Repair widget**, which shows validation issues and editable source without changing the widget's dashboard ID.
|
||||
Save repaired source or export a repair draft. Credentials are retained only when the original source and
|
||||
authentication binding remain unchanged; configure credentials again when those bindings change.
|
||||
|
||||
## Build cache maintenance
|
||||
|
||||
Administrators can call `customWidget.package.cleanupArtifacts` through the authenticated API. Start with
|
||||
`{ "dryRun": true }` to inspect candidate artifact IDs and local directories, then call it with `dryRun: false` to
|
||||
remove those that are still unused. Cleanup preserves active and previous releases, every valid saved source draft
|
||||
(including disabled offline imports), and artifacts leased by recent imports, builds or previews. Unreferenced data
|
||||
must be at least seven days old. Preview sessions themselves expire after one hour.
|
||||
|
||||
Each call removes at most 100 database artifacts and 20 local directories by default. Adjust `maximumArtifacts`
|
||||
(up to 500) and `maximumDirectories` (up to 100), or repeat when the result reports `limited: true`. Directory cleanup
|
||||
applies to the current Homarr replica's runtime and dependency cache. Other replicas can clean their own local cache
|
||||
through the same procedure. Cleanup coordinates reference changes across replicas and never invokes package code.
|
||||
This is explicit maintenance; no automatic build-cache cleanup job is enabled.
|
||||
|
||||
For an external editor workflow with normal files, local previews and immutable release publishing, see the
|
||||
[widget CLI commands](../../advanced/command-line/index.mdx#build-and-share-custom-widget-packages).
|
||||
|
||||
Each server worker runs at most eight handlers concurrently and queues at most 64 more for up to 30 seconds.
|
||||
Extra requests receive a capacity error; one failing source does not block successful results from other requests.
|
||||
|
||||
## Webhook lifecycle and advanced links
|
||||
|
||||
Each placement can issue a separate token for a named action with an input schema. Homarr displays the POST URL and
|
||||
bearer token once. Tokens stay outside source drafts, recovery and exports. Code or local binding changes invalidate
|
||||
existing tokens; create a new one for the updated placement. Explicitly revoked tokens remain revoked after rollback.
|
||||
The webhook accepts JSON matching the action schema, up to 1 MiB, and runs with its owner's current authorization.
|
||||
|
||||
A board URL can link directly to an installed widget's advanced view using `#widget=<placementId>`. The viewer still
|
||||
needs access to the board and widget. This opens the dashboard widget surface rather than a standalone app route.
|
||||
@@ -8,18 +8,17 @@ tags:
|
||||
|
||||
# Model Context Protocol (MCP)
|
||||
|
||||
Homarr exposes a stateless [Model Context Protocol](https://modelcontextprotocol.io) server at `/api/mcp`. It maps the
|
||||
current Homarr API to tools for compatible AI clients.
|
||||
Connect an AI client to Homarr to manage boards, apps, integrations, and other services through MCP tools.
|
||||
|
||||
Homarr uses MCP v2 (protocol `2026-07-28`) at `/api/mcp`, with compatibility for 2025 Streamable HTTP clients.
|
||||
Requests are stateless: each request authenticates independently, without an MCP session ID.
|
||||
|
||||

|
||||
|
||||
The endpoint implements MCP `2026-07-28` and accepts 2025 Streamable HTTP clients through a compatibility path. The old
|
||||
`/api/mcp/mcp` route remains as an alias; use `/api/mcp` for new clients.
|
||||
## Connect a client
|
||||
|
||||
## Configure a client
|
||||
|
||||
Create an API key under **Management → Tools → API → Authentication**, then add the endpoint and header to the client's
|
||||
MCP configuration:
|
||||
Create an API key under **Management → Tools → API → Authentication**. Add this configuration to your client,
|
||||
replacing the URL and API key:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -34,7 +33,10 @@ MCP configuration:
|
||||
}
|
||||
```
|
||||
|
||||
Clients without Streamable HTTP support can use `mcp-remote` over STDIO:
|
||||
Use the complete key, including the ID, dot, and token. Keep it private and use HTTPS for remote connections.
|
||||
The **AI / MCP** tab on Homarr's API page provides your endpoint, configuration, and available tools.
|
||||
|
||||
For clients that only support STDIO, use `mcp-remote`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -47,21 +49,30 @@ Clients without Streamable HTTP support can use `mcp-remote` over STDIO:
|
||||
}
|
||||
```
|
||||
|
||||
The **AI / MCP** tab on Homarr's API page shows the current endpoint, configuration, and generated tool catalog.
|
||||
### OAuth
|
||||
|
||||
## Authentication and permissions
|
||||
Clients with OAuth support can authorize through OAuth 2.1 with PKCE instead of an API key.
|
||||
Homarr publishes discovery metadata under `/.well-known/`.
|
||||
|
||||
MCP supports API keys and OAuth 2.1 with PKCE. API keys are simpler for direct client configuration; OAuth is available
|
||||
for clients that support dynamic authorization.
|
||||
If your reverse proxy does not preserve the public host and protocol, set `BASE_URL` to your public origin,
|
||||
such as `https://homarr.example.com`, without a path.
|
||||
|
||||
Tools use the permissions of the API key owner or authorized user. Creating a key does not grant additional access.
|
||||
Custom Widget and Workshop authoring tools, prompts, and resources are visible only to administrators.
|
||||
## Permissions and tools
|
||||
|
||||
Use HTTPS when the endpoint crosses an untrusted network.
|
||||
Tools use the permissions of the API key owner or OAuth user. Queries read data; mutations change it.
|
||||
Inspect the **AI / MCP** tab or your client's tool list for available actions.
|
||||
|
||||
## Verify the endpoint
|
||||
Custom Widget and Workshop authoring tools, prompts, and resources require administrator access.
|
||||
For Custom Widgets, install the official skill and follow the preview-and-evidence workflow described in
|
||||
[Connect an agent](./custom-widgets/agent-authoring). Secret values are configured in Homarr and are never returned
|
||||
through MCP.
|
||||
|
||||
The 2025 compatibility path accepts a compact `tools/list` request:
|
||||
The built-in [Assistant](./assistant) uses the same tool catalog with the current Homarr session and asks for approval
|
||||
before mutations by default.
|
||||
|
||||
## Verify the connection
|
||||
|
||||
This request lists tools through the older-client compatibility path. It checks authentication and tool discovery:
|
||||
|
||||
```sh
|
||||
curl -X POST \
|
||||
@@ -72,17 +83,14 @@ curl -X POST \
|
||||
https://homarr.example.com/api/mcp
|
||||
```
|
||||
|
||||
The removed HTTP+SSE `/sse` and `/message` transports return `410 Gone`.
|
||||
A successful discovery or initialization response alone does not verify that tools can be listed.
|
||||
|
||||
## Tools and resources
|
||||
## Troubleshooting
|
||||
|
||||
The generated catalog includes available board, app, integration, Docker, Kubernetes, media, DNS, download, and other
|
||||
Homarr procedures. Queries are read-only; mutations change state. The exact catalog depends on the release and current
|
||||
user permissions, so inspect `tools/list` or the table in Homarr instead of relying on a static list.
|
||||
|
||||
For Custom Widgets, install the official skill and follow the preview-and-evidence workflow described in
|
||||
[Connect an agent](./custom-widgets/agent-authoring). Secret values are configured in Homarr and are never returned
|
||||
through MCP.
|
||||
|
||||
The built-in [Assistant](./assistant) uses the same tool catalog with the current Homarr session and asks for approval
|
||||
before mutations by default.
|
||||
- **`invalid_token`**: copy the complete API key in `<id>.<token>` format.
|
||||
- **Missing tools**: check the user's permissions. Invalid tool schemas and duplicate names are omitted; check server
|
||||
logs for `MCP tool omitted from catalog` and report the diagnostic with your Homarr version. Exclude API keys.
|
||||
- **`Date cannot be represented in JSON Schema`**: update Homarr. Older versions could fail to list all tools because
|
||||
of an unsupported date schema.
|
||||
- **`410 Gone` on `/sse` or `/message`**: use `/api/mcp` with a Streamable HTTP client. The old `/api/mcp/mcp` URL remains
|
||||
an alias.
|
||||
@@ -11,6 +11,6 @@ tags:
|
||||
Using the media feature, you can upload images for custom icons to Homarr.
|
||||
Images will be stored in the database. They can be selected in the [icon picker](/docs/advanced/icons).
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
@@ -22,7 +22,7 @@ You can add any search engine that has a search URL that accepts a search term a
|
||||
|
||||
To create a custom search engine, you can click the button at the top of the page.
|
||||
|
||||

|
||||

|
||||
|
||||
### Name
|
||||
|
||||
|
||||
@@ -20,3 +20,15 @@ Each task exposes:
|
||||

|
||||
|
||||
Disabling a task stops scheduled runs but does not prevent a manual run.
|
||||
|
||||
## Widget history collectors
|
||||
|
||||
Trusted widget installations can opt into background sampling from their **Activation & boards** workspace tab.
|
||||
Each collector names a declared query, input, optional response field, interval, and retention limit. Collection is
|
||||
separate from viewer refresh; Redis admits one sampling run per interval across serving replicas. New collectors are
|
||||
disabled until the owner enables them. The owner must retain administrator access for collection to continue.
|
||||
|
||||
The production application schedules enabled collectors while it is running, including when no dashboard is open.
|
||||
Each series is bounded by its configured retention/sample count and a 1 MiB managed record limit. Pausing a collector
|
||||
stops requests. Removing it explicitly removes its retained series. Application history APIs remain preferable when
|
||||
available because Homarr cannot collect observations while it is offline.
|
||||
@@ -13,8 +13,13 @@ A **user** is a Homarr identity with preferences, sessions, group memberships, a
|
||||
use local credentials, LDAP, or OIDC; see [Single sign-on](/docs/advanced/single-sign-on).
|
||||
|
||||
Manage users under **Management → Users → Manage**. Administrators can change a user's profile, language, home boards,
|
||||
search preferences, week start, accessibility preference, and local password. Deleting a user does not delete boards
|
||||
they created.
|
||||
search preferences, data-size units (binary KiB/MiB or decimal KB/MB), week start, accessibility preference, and local
|
||||
password. Decimal units are the default. Change them from the user form or **Command menu (Ctrl/Cmd + K) →
|
||||
Preferences → Data size units**. Byte rates follow the same preference; bit rates stay decimal and use labels such as
|
||||
Mb/s. Deleting a user does not delete boards they created.
|
||||
|
||||
In demo mode (`DEMO_MODE=true`), user deletion and password changes are disabled for everyone, including administrators,
|
||||
even when `DEMO_READ_ONLY=false` allows other changes. This applies to the UI and API.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"title": "Widgets",
|
||||
"description": "Widgets allow you to show data on your board or control applications. Click on one of the widgets below for further information:",
|
||||
"image": "/img/docusaurus.png"
|
||||
"description": "Widgets display data or provide actions on a board. Choose a widget to see its configuration, supported integrations, and limitations.",
|
||||
"image": "/img/pictures/widgets/widget-dark.jpg"
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,12 @@ import { bookmarksWidget } from ".";
|
||||
|
||||
The Bookmarks widget keeps direct URLs and Homarr Apps in one ordered list. URLs do not require an App record; Homarr
|
||||
uses the site's favicon when available. Choose adaptive, compact-grid, or other layouts from the widget configuration.
|
||||
Vertical uses consistent compact rows and keeps bookmark names visible whenever the widget is wide enough, regardless
|
||||
of how many bookmarks are configured. Adaptive and grid layouts reduce card height and spacing to show as many
|
||||
bookmarks as possible before scrolling. Horizontal uses a compact, centered scrolling row with narrower cards in
|
||||
one-row widgets. Horizontal, vertical, compact-grid, and icon-only layouts keep their gaps tight. Compact grid also
|
||||
preserves its configured columns when the dashboard is scaled. Bookmark cards, icons, text, and gaps keep the same
|
||||
visual density when a low-column dashboard is enlarged to fill the screen.
|
||||
|
||||
### Screenshots
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ export const bookmarksWidget: WidgetDefinition = {
|
||||
},
|
||||
{
|
||||
name: "Layout",
|
||||
description: "How bookmarks use the available widget space.",
|
||||
description:
|
||||
"How bookmarks use the available widget space. Vertical keeps compact rows and stable title visibility.",
|
||||
values: { type: "select", options: ["Adaptive", "Vertical", "Horizontal", "Grid", "Compact grid", "Icons"] },
|
||||
defaultValue: "Adaptive",
|
||||
},
|
||||
@@ -28,7 +29,7 @@ export const bookmarksWidget: WidgetDefinition = {
|
||||
},
|
||||
{
|
||||
name: "Card spacing",
|
||||
description: "Space between bookmark cards.",
|
||||
description: "Space between bookmark cards. Horizontal, compact-grid, and icon-only layouts cap larger gaps.",
|
||||
values: { type: "select", options: ["Extra small", "Small", "Medium", "Large", "Extra large"] },
|
||||
defaultValue: "Extra small",
|
||||
},
|
||||
|
||||
@@ -66,8 +66,9 @@ export const downloadsWidget: WidgetDefinition = {
|
||||
defaultValue: "yes",
|
||||
},
|
||||
{
|
||||
name: "Hide completed torrent under this threshold (in kiB/s)",
|
||||
description: "This will hide completed torrent entries whose upload speed is below the specified threshold.",
|
||||
name: "Hide completed torrent under this threshold",
|
||||
description:
|
||||
"This hides completed torrents whose upload speed is below the threshold. The input uses KiB/s with binary units and KB/s with decimal units.",
|
||||
values: "Any number above 0, 0 to disable",
|
||||
defaultValue: "0",
|
||||
},
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: "llama.cpp"
|
||||
description: "Displays the health, loaded model and generation speed of a local llama.cpp llama-server."
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
import { WidgetHeader } from "@site/src/components/widgets/header";
|
||||
import { AddingWidget } from "@site/src/components/widgets/adding";
|
||||
import { WidgetConfig } from "@site/src/components/widgets/configuration";
|
||||
import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
|
||||
import { llamacppWidget } from ".";
|
||||
import { llamacppIntegration } from "@site/docs/integrations/llama-cpp";
|
||||
|
||||
<WidgetHeader widget={llamacppWidget} categories={["System Monitoring"]} />
|
||||
|
||||
This widget shows the live status of your local [llama.cpp](https://github.com/ggml-org/llama.cpp) `llama-server`: whether it is online, which model is loaded (with quantization, context size, file size and parameter count), the generation speed in tokens per second, and how many requests are being processed. When the server is started with `--metrics`, it also reports total token counts and the prompt cache hit rate.
|
||||
|
||||
### Generation speed
|
||||
|
||||
The widget picks the most meaningful speed for the current state (hover the speed for an explanation of which one is shown):
|
||||
|
||||
- **While a request is being generated** — the average speed of that request since it started, derived from the token count reported by `/slots`.
|
||||
- **While the server is busy but the per-request data is unavailable** — the live speed from the `/metrics` rate gauge.
|
||||
- **While the server is idle** — the average generation speed since the server started (cumulative tokens ÷ seconds), so the value stays meaningful instead of dropping to zero.
|
||||
|
||||
### Supported Integrations
|
||||
|
||||
<WidgetIntegrations
|
||||
items={[
|
||||
{
|
||||
integration: llamacppIntegration,
|
||||
note: "Displays health, model details, generation speed and request status",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
### Adding the widget
|
||||
|
||||
<AddingWidget />
|
||||
|
||||
### Configuration
|
||||
|
||||
<WidgetConfig configuration={llamacppWidget.configuration} />
|
||||