|
| 1 | +# Home Gear (Dashboard) |
| 2 | + |
| 3 | +The **Home** gear is Gearbox's box-agnostic app dashboard — a self-hosted |
| 4 | +start page with launcher tiles, service widgets, sparkline graphs, |
| 5 | +bookmarks, and a search bar. It's the first gear with |
| 6 | +`Info.Scope == ScopeSystem`: there is one row per install, not one per |
| 7 | +monitored box, since a dashboard isn't tied to a particular server. |
| 8 | + |
| 9 | +> **Implementation reference:** the canonical, code-adjacent docs live |
| 10 | +> alongside the source at |
| 11 | +> [`internal/gears/home/README.md`](../internal/gears/home/README.md) — |
| 12 | +> capabilities, routes, permissions, schema, and architecture notes. |
| 13 | +> This page is the **operator-facing overview** with quickstart and |
| 14 | +> common questions. |
| 15 | +
|
| 16 | +## Quick start |
| 17 | + |
| 18 | +1. Sign in to Gearbox as an admin and enable the **Home** gear from |
| 19 | + `Settings → Gears`. (Home is system-scoped, so it's listed in the |
| 20 | + global gears section, not under any individual box.) |
| 21 | +2. Navigate to `/home/`. You'll see an empty board with an **+ Add tile** |
| 22 | + button in the top-right. |
| 23 | +3. Click **+ Add tile**, paste an app's URL (e.g. `https://sonarr.example.com`), |
| 24 | + tab out of the field. The backend probes well-known endpoints in |
| 25 | + parallel; on a fingerprint hit, a green "Detected: <App>" banner |
| 26 | + pre-fills name + icon + slug. |
| 27 | +4. For widget data, paste the upstream's API key into the **API Key** |
| 28 | + field. Click **API Instructions** in the detection banner if you |
| 29 | + need step-by-step guidance — works for Sonarr/Radarr/Prowlarr/ |
| 30 | + Lidarr/Readarr/Bazarr, qBittorrent, Plex, Jellyfin, Tautulli, |
| 31 | + Pi-hole, AdGuard, Portainer, Immich, and UniFi. |
| 32 | +5. **Save**. The tile lands on the board and starts its 30-second |
| 33 | + refresh loop. |
| 34 | + |
| 35 | +## Built-in widget providers |
| 36 | + |
| 37 | +Apps with first-class widgets (live data pills, server-rendered field |
| 38 | +maps over SSE): |
| 39 | + |
| 40 | +| App | Auth | Sample fields | |
| 41 | +|----------------------|-------------|--------------------------------------------------------| |
| 42 | +| Sonarr / Radarr | API key | `wanted`, `missing`, `queued`, `series` / `movies` | |
| 43 | +| Lidarr / Readarr | API key | `wanted`, `queued`, `artists` / `books` | |
| 44 | +| Prowlarr | API key | `numIndexers`, `numGrabs`, `numQueries`, `numFailQueries` | |
| 45 | +| qBittorrent | basic auth | `download`, `upload`, `leech`, `seed` | |
| 46 | +| Pi-hole | API key | `queries`, `blocked`, `blocked_percent`, `gravity` | |
| 47 | +| Plex | `X-Plex-Token` | `streams`, `transcodes`, `bandwidth`, `movies`, `tv`, `episodes`, `libraries` | |
| 48 | +| UniFi Network | `X-API-KEY` (Integration API) | `clients`, `wifi`, `wired`, `vpn`, `devices_online`, `devices_offline`, `wan_status`, `wan_down` *(graph)*, `wan_up` *(graph)*, `gateway_cpu`, `gateway_mem`, `uptime` | |
| 49 | + |
| 50 | +Apps in the catalog without a tier-1 provider still get launcher tiles |
| 51 | +(icon + name + reachability status). Adding a new provider is a |
| 52 | +small Go file; see the **Adding a Tier-1 widget provider** section in |
| 53 | +the gear's [internal README](../internal/gears/home/README.md). |
| 54 | + |
| 55 | +## Sparkline graphs |
| 56 | + |
| 57 | +Fields tagged `graphable: true` in the catalog (currently UniFi |
| 58 | +`wan_down` and `wan_up`) render an inline ~38×12 SVG trend line next |
| 59 | +to the value. The browser buffers the last 60 samples per |
| 60 | +tile×field in memory and scales each line to its buffer's min/max. |
| 61 | +Bandwidth values are normalized to bits/s before graphing so the |
| 62 | +trend stays smooth across Kbps↔Mbps unit boundaries. |
| 63 | + |
| 64 | +The buffer is **per session** — sparklines start from when you opened |
| 65 | +the dashboard and grow as updates arrive. UniFi's Integration API |
| 66 | +doesn't ship historical time-series, so anything older than the |
| 67 | +current session isn't available. After ~2 minutes of dashboard |
| 68 | +uptime you have 4 samples (1 every 30 s); after 30 minutes the buffer |
| 69 | +is at its 60-sample cap and starts rolling. |
| 70 | + |
| 71 | +## Security model (where do my API keys live?) |
| 72 | + |
| 73 | +API keys, basic-auth passwords, and bearer tokens are: |
| 74 | + |
| 75 | +- **Encrypted at rest** with the install's master key via the existing |
| 76 | + AES-256-GCM `crypto.Encryptor` (the same primitive used for agent |
| 77 | + API keys). Keys are stored in the `home_tile_secrets` table, |
| 78 | + separate from `home_tiles` so the secret can be `JOIN`ed only by |
| 79 | + code paths that need it. |
| 80 | +- **Used only by backend handlers**. All third-party API calls |
| 81 | + (Sonarr, Plex, UniFi, etc.) are made by the Gearbox Go process |
| 82 | + with the decrypted key in headers. The browser never has the key. |
| 83 | +- **Never serialized into responses or templ renders**. The widget |
| 84 | + refresh sends the *rendered field map* (`{"streams": "2", |
| 85 | + "bandwidth": "12.4 Mbps"}`) over SSE — the upstream's raw response |
| 86 | + body never reaches the browser. |
| 87 | +- **Surfaced to the UI as `has_secret: true` only**. To see a stored |
| 88 | + key after creation, you re-enter it (Stripe-style); there is no |
| 89 | + "show key" button. |
| 90 | + |
| 91 | +If you compromise the Gearbox host, you can read the in-memory cache |
| 92 | +and decrypt at-rest secrets with the master key — that's the same |
| 93 | +threat model as every other secret in the app. |
| 94 | + |
| 95 | +## Configuration |
| 96 | + |
| 97 | +System-wide settings live on the gear's own `gears.config` JSON row |
| 98 | +(`server_id = '__system__'`, `name = 'home'`): |
| 99 | + |
| 100 | +```json |
| 101 | +{ |
| 102 | + "system_default_landing_path": "/home", |
| 103 | + "health_checks_enabled": true, |
| 104 | + "default_status_interval_seconds": 30, |
| 105 | + "attribution_shown": false |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +Per-tile cadence overrides live on the tile's own config JSON |
| 110 | +(`AppConfig.status_interval_seconds`, `AppConfig.status_checks_disabled`). |
| 111 | + |
| 112 | +## Backup / restore |
| 113 | + |
| 114 | +The gear ships with schema-versioned import/export at |
| 115 | +`/home/api/export` and `/home/api/import`. The JSON file is portable |
| 116 | +across installs, but **encrypted secrets are deliberately excluded** |
| 117 | +— carrying them across hosts would require sharing the master key, |
| 118 | +and we'd rather you re-enter keys on the destination than punch a |
| 119 | +hole in the threat model. |
0 commit comments