{"id":47727406,"url":"https://github.com/mzac/uhld","last_synced_at":"2026-04-06T18:02:26.017Z","repository":{"id":347637762,"uuid":"1194721714","full_name":"mzac/uhld","owner":"mzac","description":"Ultimate Homelab Dashboard","archived":false,"fork":false,"pushed_at":"2026-04-02T03:39:04.000Z","size":980,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-03T06:30:06.684Z","etag":null,"topics":["adguard","cloudflare","dashboard","docker","fastapi","homelab","kubernetes","pihole","plex","plugin-system","proxmox","python","react","self-hosted","sqlite","tailscale","tailwindcss","typescript","unifi"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mzac.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-28T18:17:23.000Z","updated_at":"2026-04-02T22:02:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mzac/uhld","commit_stats":null,"previous_names":["mzac/uhld"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/mzac/uhld","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mzac%2Fuhld","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mzac%2Fuhld/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mzac%2Fuhld/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mzac%2Fuhld/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mzac","download_url":"https://codeload.github.com/mzac/uhld/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mzac%2Fuhld/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31483380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T17:22:55.647Z","status":"ssl_error","status_checked_at":"2026-04-06T17:22:54.741Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adguard","cloudflare","dashboard","docker","fastapi","homelab","kubernetes","pihole","plex","plugin-system","proxmox","python","react","self-hosted","sqlite","tailscale","tailwindcss","typescript","unifi"],"created_at":"2026-04-02T20:52:37.147Z","updated_at":"2026-04-06T18:02:26.008Z","avatar_url":"https://github.com/mzac.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# UHLD — Ultimate Homelab Dashboard\n\n\u003e **Work in progress — under heavy development. Expect breaking changes.**\n\nA self-hosted, plugin-driven dashboard for your homelab. Monitor and manage Proxmox, Docker, Kubernetes, AdGuard, Pi-hole, Tailscale, UniFi, Plex, Cloudflare, and more from a single unified interface.\n\n\u003e This project is built entirely using [Claude Code](https://claude.ai/code), Anthropic's agentic coding tool.\n\n![UHLD Dashboard](images/dashboard.png)\n\n---\n\n## What it is\n\nUHLD is the homelab equivalent of Home Assistant — but for infrastructure instead of home automation. Deploy it as a single Docker container, enable plugins for the services you run, and get a unified dashboard to monitor and interact with your entire homelab from one place.\n\n- **Plugin-first** — every integration is a plugin, nothing is hardcoded\n- **Multi-instance** — run multiple instances of any plugin (two Proxmox clusters, two UniFi controllers, etc.)\n- **Read/monitor by default** — write and action operations always require explicit intent\n- **Single container** — FastAPI backend + React frontend, one Docker image\n- **Credential encryption** — all plugin secrets are encrypted at rest\n- **Light/dark theme** — CSS-variable-backed adaptive theme with toggle\n- **Drag-to-reorder dashboard** — customizable widget layout persisted per-browser\n\n---\n\n## Current Status\n\n| Feature | Status |\n|---------|--------|\n| Core framework (auth, plugin registry, settings) | ✅ Complete |\n| First-launch setup (auto admin/admin, forced password change) | ✅ Complete |\n| Light / dark mode toggle | ✅ Complete |\n| Drag-to-reorder dashboard tiles | ✅ Complete |\n| Multi-instance plugin support | ✅ Complete |\n| Multi-user with admin / viewer roles | ✅ Complete |\n| TOTP 2FA (Google Authenticator, Authy) | ✅ Complete |\n| Passkeys / WebAuthn (YubiKey, Touch ID, Face ID, Windows Hello) | ✅ Complete |\n| OAuth / OIDC (Entra ID, Google, GitHub) | ✅ Complete |\n| Proxmox VE plugin (nodes, host drill-down, VM/CT topology tree, performance graphs, start/stop/reboot) | ✅ Complete |\n| AdGuard Home plugin (stats, query log, protection toggle) | ✅ Complete |\n| Pi-hole plugin (stats, query log, blocking toggle) | ✅ Complete |\n| Tailscale plugin (devices, users, DNS, ACL editor, sidecar status) | ✅ Complete |\n| UniFi plugin (clients, devices, ports, networks, WiFi, firewall) | ✅ Complete |\n| Docker plugin (containers, images, logs, start/stop/restart) | ✅ Complete |\n| Kubernetes plugin (nodes, workloads, networking, storage, logs, shell, YAML editor) | ✅ Complete |\n| Nginx Proxy Manager plugin (proxy host + certificate CRUD) | ✅ Complete |\n| Network Tools plugin (ping/traceroute live stream, speedtest history) | ✅ Complete |\n| LLM Assistant plugin (OpenAI/Ollama/Anthropic/OpenWebUI) | ✅ Complete |\n| Cloudflare plugin (zones, DNS records, analytics, zone settings) | ✅ Complete |\n| Plex plugin (active sessions, libraries, media actions) | ✅ Complete |\n| Notifications plugin (email, Telegram, webhook) | ✅ Complete |\n| Configuration backup \u0026 restore | ✅ Complete |\n| Asset Inventory plugin (CMDB for homelab hardware) | ✅ Complete |\n| Patch Panel plugin (port/device/switch mapping) | ✅ Complete |\n| Remote Packet Capture (tcpdump local or SSH, live stream, PCAP download) | ✅ Complete |\n| Tasks \u0026 Incidents plugin (infrastructure task queue) | ✅ Complete |\n| HDHomeRun plugin (live TV, multi-stream grid, PiP, signal monitoring) | ✅ Complete |\n| UPS / NUT plugin (battery %, load, runtime, power events, notifications) | ✅ Complete |\n| Jellyfin / TrueNAS / Synology | Planned |\n\n---\n\n## Tech Stack\n\n- **Backend:** Python 3.12, FastAPI, SQLAlchemy async + aiosqlite, APScheduler\n- **Frontend:** React 18, TypeScript, Vite, Tailwind CSS, Zustand, dnd-kit\n- **Auth:** JWT (httpOnly cookie), bcrypt, TOTP (`pyotp`), WebAuthn (`py-webauthn`), OAuth 2.0 / OIDC\n- **Storage:** SQLite — zero external dependencies\n- **Deployment:** Multi-stage Docker (node:20-alpine → python:3.12-slim)\n\n---\n\n## Quick Start\n\n### Requirements\n\n- Docker and Docker Compose\n\n### Generate secrets\n\n```bash\npython -c \"import secrets; print(secrets.token_hex(32))\"                               # JWT_SECRET\npython -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\"  # ENCRYPTION_KEY\n```\n\n### docker-compose.yml\n\n```yaml\nservices:\n  uhld:\n    image: ghcr.io/mzac/uhld:latest\n    ports:\n      - \"8222:8000\"\n    volumes:\n      - ./data:/data\n    environment:\n      - JWT_SECRET=your_jwt_secret_here\n      - ENCRYPTION_KEY=your_fernet_key_here\n      - TZ=America/New_York\n    restart: unless-stopped\n```\n\n```bash\ndocker compose up -d\n```\n\nThen open `http://localhost:8222`.\n\n### First login\n\nOn first launch UHLD auto-creates an **`admin` / `admin`** account and immediately prompts you to set a new password before continuing.\n\n---\n\n## ⚠️ Security Considerations\n\n**USE AT YOUR OWN RISK.** UHLD has access to sensitive infrastructure data and control operations across your entire homelab. Treat it with the same security care you would any administrative dashboard.\n\n### What UHLD Can Access\n\n- **Proxmox:** VM/LXC lifecycle, host information, storage, resource usage\n- **Docker:** Container lifecycle (start/stop/restart), log access, image inventory\n- **Kubernetes:** Pod/deployment management, container logs, **interactive shell exec into any running pod**, direct YAML patch/apply to cluster resources — treat this as equivalent to `kubectl` access\n- **UniFi:** Network devices, clients, WiFi settings, firewall rules\n- **Tailscale:** Node management, user access, DNS, ACLs\n- **AdGuard/Pi-hole:** DNS blocking rules, query logs\n- **All plugins:** Any credentials you provide are stored and used to access those services\n\nAn attacker who gains access to UHLD can:\n- View sensitive data from all connected services\n- Start/stop/reboot VMs and containers\n- Modify network configurations\n- Access logs and diagnostics containing PII or secrets\n- **Execute an interactive shell inside any running Kubernetes pod** — including pods with access to databases, secrets, internal APIs, or service account tokens\n- **Apply arbitrary YAML to your Kubernetes cluster** — equivalent to having `kubectl apply` access\n- Potentially pivot to other infrastructure components via pod environments, mounted secrets, or service accounts\n\n### Security Best Practices\n\n**Always:**\n- ✅ Use **strong, unique passwords** for the UHLD admin account — this is your primary defense\n- ✅ Enable **TOTP 2FA** or register a **passkey** — both are available in Settings → Account\n- ✅ Keep UHLD on a **private network or VPN** — never expose the web interface directly to the public internet\n- ✅ Access only via **HTTPS with valid certificates** in production\n- ✅ Use **separate credentials** for UHLD that differ from your personal/primary passwords\n- ✅ Limit access to **trusted users only** — use role-based access control (Settings → Users)\n- ✅ Keep UHLD and all connected services **patched and up-to-date**\n- ✅ Regularly **rotate credentials** for service accounts (API keys, tokens, passwords)\n- ✅ Monitor **logs and audit trails** for suspicious activity\n- ✅ Back up and **encrypt your database** (`/data/uhld.db`) — it contains encrypted credentials and configuration\n\n**Never:**\n- ❌ Use default credentials (`admin/admin`) in production — change immediately\n- ❌ Expose UHLD to the public internet without proper authentication and TLS\n- ❌ Share the `JWT_SECRET` or `ENCRYPTION_KEY` — regenerate if compromised\n- ❌ Store credentials in plain text or commit them to version control\n- ❌ Ignore security updates or plugin vulnerabilities\n\n### Deployment Recommendations\n\n**For homelab/home use:**\n- Deploy behind a **firewall or NAT** (not port-forwarded to the internet)\n- Access via **Tailscale VPN** for remote access (use the Tailscale plugin for easy setup)\n- Disable plugins you don't use to reduce attack surface\n\n**For production/shared environments:**\n- Deploy in a **private network** with network segmentation\n- Use a **reverse proxy** (nginx, Traefik) with authentication (OAuth, OIDC)\n- Enable **HTTPS with valid certificates**\n- Implement **rate limiting** and **WAF rules**\n- Use **secrets management** for all credentials (HashiCorp Vault, K8s Secrets)\n- Enable **audit logging** and **monitoring**\n- Implement **least-privilege access** — separate admin, operator, and viewer roles\n\n### Reporting Security Issues\n\nIf you discover a security vulnerability, **do not open a public issue.** Please contact the maintainers privately so the issue can be patched before disclosure.\n\n---\n\n## Environment Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `JWT_SECRET` | Yes | JWT signing secret |\n| `ENCRYPTION_KEY` | Yes | Fernet key for encrypting plugin credentials |\n| `DATABASE_PATH` | No | SQLite path (default: `/data/uhld.db`) |\n| `TZ` | No | Timezone (default: `America/Montreal`) |\n| `LOG_LEVEL` | No | Python log level (default: `INFO`) |\n| `WEBAUTHN_RP_ID` | No | Passkey relying-party ID — hostname only (auto-derived from request if unset) |\n| `WEBAUTHN_RP_NAME` | No | Display name shown in passkey prompts (default: `UHLD`) |\n| `WEBAUTHN_ORIGIN` | No | Full origin URL for WebAuthn (auto-derived from request if unset) |\n| `OAUTH_BASE_URL` | No | Base URL of this UHLD instance — used for OAuth redirect URIs |\n| `OAUTH_AUTO_PROVISION` | No | `true` to auto-create local accounts on first OAuth login (default: `false`) |\n| `OAUTH_ENTRA_CLIENT_ID` | No | Microsoft Entra ID (Azure AD) app client ID |\n| `OAUTH_ENTRA_CLIENT_SECRET` | No | Microsoft Entra ID app client secret |\n| `OAUTH_ENTRA_TENANT_ID` | No | Entra tenant ID or `common` for multi-tenant |\n| `OAUTH_GOOGLE_CLIENT_ID` | No | Google OAuth 2.0 client ID |\n| `OAUTH_GOOGLE_CLIENT_SECRET` | No | Google OAuth 2.0 client secret |\n| `OAUTH_GITHUB_CLIENT_ID` | No | GitHub OAuth app client ID |\n| `OAUTH_GITHUB_CLIENT_SECRET` | No | GitHub OAuth app client secret |\n\n---\n\n## Plugin Configuration\n\nPlugins are enabled and configured through **Settings → Plugins**. Each plugin's form is rendered dynamically from its JSON Schema — no manual config files needed. Sensitive fields (API keys, passwords, tokens) are encrypted before being stored.\n\n### Multi-instance plugins\n\nAny plugin can be enabled more than once with different connection settings. Use the **Add instance** button in Settings → Plugins to add a second instance (e.g., a second Proxmox cluster or UniFi controller). Each instance has its own sidebar entry and dashboard tile.\n\n---\n\n## Tailscale Sidecar (optional)\n\nUHLD can run as a Tailscale node to expose itself over your tailnet with HTTPS via MagicDNS. Use the provided `docker-compose.local.yml` and `build-run-local.sh` as a reference. Requires `TS_AUTHKEY` in `.env.local`.\n\nWhen a Tailscale sidecar is detected (Unix socket at `/var/run/tailscale/tailscaled.sock`), the Tailscale plugin view shows a live local status bar with node name, IP, and connection state.\n\n---\n\n## Development\n\n```bash\n# Backend\npip install -r requirements.txt\nuvicorn backend.main:app --reload --host 0.0.0.0 --port 8000\n\n# Frontend\ncd frontend \u0026\u0026 npm install \u0026\u0026 npm run dev\n# Vite proxies /api/* to localhost:8000\n\n# Local Docker build (with Tailscale sidecar)\n./build-run-local.sh   # requires .env.local with TS_AUTHKEY\n\n# Standard Docker build\n./build-run.sh\n```\n\n---\n\n## Related Projects\n\nThis project shares its architecture and code style with [apt-ui](https://github.com/mzac/apt-ui), a self-hosted apt package management dashboard — also built entirely with Claude Code.\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmzac%2Fuhld","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmzac%2Fuhld","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmzac%2Fuhld/lists"}