"""Tool catalogue — the Family Home Lab "home dash". Each tool maps to a category (colour sticker), a friendly name/description and a launch mode: "embed" (inline iframe) or "window" (new tab / external link). This is a curated home-dash across .13 and .35 plus Proxmox / Home Assistant. Most of these are external apps that keep their OWN secondary login — we link out to them and never try to pass credentials (no SSO passthrough). Flags: external : link out in a new tab (opens URL directly, no /tool/ wrapper) login : the target has its own login the user must sign into lan : only resolves on the LAN (*.home.lab), not from the internet admin : admin-only (infra / backups / monitoring) """ from __future__ import annotations from dataclasses import dataclass from .config import settings @dataclass(frozen=True) class Tool: id: str name: str category: str description: str url: str mode: str = "window" # "embed" | "window" status: str = "online" external: bool = True login: bool = False lan: bool = False admin: bool = False private: bool = False # per-user private app (e.g. dsh instance) def section_label(category: str) -> str: return { "chat": "Chat & AI", "image": "Image Lab", "video": "Video Lab", "audio": "Audio Lab", "docs": "Notes & Docs", "ai": "AI Tools", "media": "Media & Fun", "data": "Data & Links", "storage": "Storage & Backups", "home": "Home & Lab", "infra": "Infrastructure", "network": "Home Network", "home-mgmt": "Home Management", "dev": "Development", "portfolio": "Portfolio", }.get(category, category.title()) def section_color(category: str) -> str: return settings.SECTION_COLORS.get(category, "#a39e98") # Every category a user may have tools in; simple tooling produces all of these. ALL_CATEGORIES = [ "chat", "ai", "image", "video", "audio", "media", "docs", "data", "home-mgmt", "storage", "dev", "portfolio", "home", "infra", "network", ] def _cat() -> list[Tool]: """Curated home-dash catalogue across .13 / .35 / Proxmox / Home Assistant.""" return [ # ---- Chat & AI ----------------------------------------------------- Tool("dsh", "DeepSeek Harness", "chat", "Your personal assistant instance", "https://dsh-sam.lab.audasmedia.com.au", private=True), Tool("chat-lab", "Family Chat", "chat", "Group chat — available to everyone", "https://chat.lab.audasmedia.com.au", login=True), Tool("pb-chat", "PB Chat (Admin)", "chat", "Chat admin console (admins only)", "https://pb.chat.lab.audasmedia.com.au", login=True, admin=True), Tool("ai-router", "AI Router", "ai", "OmniRoute LLM gateway dashboard", "https://omniroute.lab.audasmedia.com.au"), Tool("n8n", "n8n", "ai", "Workflow automation", "https://n8n.lab.audasmedia.com.au", login=True), Tool("langfuse", "Langfuse", "ai", "LLM tracing & observability", "https://langfuse.home.lab", login=True, lan=True, admin=True), Tool("prefect", "Prefect", "ai", "Workflow orchestration", "https://prefect.lab.audasmedia.com.au", login=True), Tool("archon", "Archon", "ai", "Autonomous coding agent", "https://archon.home.lab", login=True, lan=True), Tool("supabase", "LangChain (Supabase)", "ai", "LangChain dev service", "https://supabase.home.lab", login=True, lan=True, admin=True), # ---- Image --------------------------------------------------------- Tool("gimp", "GIMP", "image", "Photo editing & retouching", "https://gimp.lab.audasmedia.com.au"), Tool("penpot", "Penpot", "image", "Open-source design & prototyping", "https://penpot.lab.audasmedia.com.au", login=True), Tool("photo-filter", "Photo Filter", "image", "Photo utility / filtering", "https://photo-filter.lab.audasmedia.com.au"), # ---- Video --------------------------------------------------------- Tool("video-editor", "Video Editor", "video", "Desktop video editing in your browser", "https://video.lab.audasmedia.com.au"), Tool("jellyfin", "Jellyfin", "media", "Movies, TV & music server", "https://jellyfin.lab.audasmedia.com.au", login=True), Tool("jellyseerr", "Jellyseerr", "media", "Request movies & shows", "https://jellyseerr.lab.audasmedia.com.au", login=True), Tool("games", "Games", "media", "Retro games (emulatorjs, LAN)", "https://games.home.lab", lan=True), Tool("sonarr", "Sonarr", "media", "TV series manager", "https://sonarr.lab.audasmedia.com.au", login=True, admin=True), Tool("radarr", "Radarr", "media", "Movie manager", "https://radarr.lab.audasmedia.com.au", login=True, admin=True), Tool("readarr", "Readarr", "media", "Ebook manager", "https://readarr.lab.audasmedia.com.au", login=True, admin=True), Tool("lidarr", "Lidarr", "media", "Music manager", "https://lidarr.lab.audasmedia.com.au", login=True, admin=True), Tool("sabnzbd", "SABnzbd", "media", "Usenet downloader", "https://sabnzbd.lab.audasmedia.com.au", login=True, admin=True), Tool("qbittorrent", "qBittorrent", "media", "Torrent client", "https://qbittorrent.lab.audasmedia.com.au", login=True, admin=True), Tool("spotweb", "Spotweb", "media", "Spotnet indexer", "https://spotweb.lab.audasmedia.com.au", login=True, admin=True), Tool("headphones", "Headphones", "media", "Music manager", "https://headphones.lab.audasmedia.com.au", login=True, admin=True), # ---- Audio --------------------------------------------------------- Tool("audio-editor", "Audio Editor", "audio", "Multi-track audio editing", "https://audio.lab.audasmedia.com.au"), Tool("lmms", "LMMS Music Studio", "audio", "Beats, melodies & full arrangements", "https://lmms.lab.audasmedia.com.au"), Tool("snapcast", "Snapcast", "audio", "Sync audio to speakers around the house", "http://192.168.20.13:1780", lan=True), Tool("mopidy", "Mopidy", "audio", "Music player (LAN only)", "http://mopidy.home.lab", lan=True), # ---- Notes & Docs -------------------------------------------------- Tool("silverbullet", "SilverBullet", "docs", "Markdown notes & knowledge", "https://silverbullet.lab.audasmedia.com.au", login=True), Tool("affine", "AFFiNE", "docs", "Docs + whiteboard + database", "https://affine.lab.audasmedia.com.au", login=True), Tool("hedgedoc", "HedgeDoc", "docs", "Real-time collaborative markdown", "https://hedgedoc.lab.audasmedia.com.au", login=True), Tool("trilium", "Trilium", "docs", "Privacy-focused notes", "https://trillium.lab.audasmedia.com.au", login=True), Tool("flatnotes", "Flatnotes", "docs", "Minimal markdown notes", "https://flatnotes.lab.audasmedia.com.au", login=True), Tool("vikunja", "Vikunja", "docs", "Tasks & to-dos", "https://vikunja.lab.audasmedia.com.au", login=True), Tool("bookstack", "BookStack", "docs", "Documentation chains", "https://bookstack.home.lab", login=True, lan=True), Tool("dokuwiki", "DokuWiki", "docs", "Lightweight wiki", "https://dokuwiki.lab.audasmedia.com.au", login=True), Tool("kanboard", "Kanboard", "docs", "Kanban boards", "https://kanboard.lab.audasmedia.com.au", login=True), # ---- Data & Links -------------------------------------------------- Tool("nocodb", "NocoDB", "data", "Turn spreadsheets into apps", "https://nocodb.lab.audasmedia.com.au", login=True), Tool("linkace", "LinkAce", "data", "Bookmark manager", "https://linkace.lab.audasmedia.com.au", login=True), Tool("wherewoof", "Where-Woof", "data", "Family location tracking", "https://where-woof.com", login=True), Tool("wherewoof-admin", "Where-Woof Admin", "data", "Manage family location tracking", "https://admin.where-woof.com", login=True), Tool("freshrss", "FreshRSS", "data", "RSS reader", "https://freshrss.lab.audasmedia.com.au", login=True), Tool("firefly", "Firefly III", "data", "Personal finance", "https://firefly.lab.audasmedia.com.au", login=True, admin=True), Tool("vaultwarden", "Vaultwarden", "data", "Password manager", "https://vaultwarden.lab.audasmedia.com.au", login=True, admin=True), # ---- Home Management ---------------------------------------------- Tool("homebox", "Homebox", "home-mgmt", "Home inventory & items", "https://homebox.lab.audasmedia.com.au", login=True), Tool("toora", "Toora Wiki", "home-mgmt", "Home wiki", "https://toora.lab.audasmedia.com.au", login=True), Tool("sams", "Sams Files", "home-mgmt", "Home file server (LAN)", "http://sams.home.lab", lan=True), Tool("owl", "Owl Recipes", "home-mgmt", "Kitchen recipes & household", "https://owl.lab.audasmedia.com.au", login=True), # ---- Storage & Files ---------------------------------------------- Tool("nextcloud", "Nextcloud", "storage", "Files, calendar, contacts", "https://nextcloud.lab.audasmedia.com.au", login=True), Tool("immich", "Immich", "storage", "Self-hosted photo gallery", "https://immich.lab.audasmedia.com.au", login=True), Tool("paperless", "Paperless-ngx", "storage", "Scan, OCR & archive documents", "https://paperless.lab.audasmedia.com.au", login=True), Tool("filebrowser", "File Browser", "storage", "File management web UI", "https://filebrowser.lab.audasmedia.com.au", login=True), Tool("garage", "Garage S3", "storage", "Object storage (buckets)", "http://192.168.20.13:3902", login=True, lan=True, admin=True), Tool("pingvin", "Pingvin Share", "storage", "Share files & notes", "https://pingvin.lab.audasmedia.com.au", login=True), Tool("kopia", "Kopia", "storage", "Backups & restore", "https://kopia.home.lab", login=True, lan=True, admin=True), Tool("backrest", "Backrest", "storage", "Restic backup UI", "https://restic.home.lab", login=True, lan=True, admin=True), Tool("offsite", "Offsite Backup", "storage", "Amazon S3 offsite backup management", "https://offsite.lab.audasmedia.com.au", login=True, admin=True), # ---- Development --------------------------------------------------- Tool("gitea", "Gitea", "dev", "Git repositories", "https://gitea.lab.audasmedia.com.au", login=True), # ---- Portfolio (Sam's resume sites) -------------------------------- Tool("sam-developer", "Developer", "portfolio", "Resume — software development", "https://sam-developer.lab.audasmedia.com.au"), Tool("sam-devops", "DevOps", "portfolio", "Resume — DevOps & infrastructure", "https://sam-devops.lab.audasmedia.com.au"), Tool("sam-iot-electronics", "IoT & Electronics", "portfolio", "Resume — IoT & electronics", "https://sam-iot-electronics.lab.audasmedia.com.au"), Tool("sam-pursuits", "Pursuits", "portfolio", "Resume — projects & pursuits", "https://sam-pursuits.lab.audasmedia.com.au"), Tool("wikijs", "Wiki.js", "portfolio", "Resume wiki — machines & projects", "https://wikijs.lab.audasmedia.com.au", login=True), Tool("lynx", "Lynx", "portfolio", "Resume — projects gallery", "https://lynx.lab.audasmedia.com.au", login=True), Tool("sequence", "Sequence", "portfolio", "Resume — sequence", "https://sequence.lab.audasmedia.com.au"), # ---- Home & Lab ---------------------------------------------------- Tool("home-assistant", "Home Assistant", "home", "Home automation", "https://homeassistant.home.lab", login=True, lan=True), Tool("proxmox", "Proxmox VE", "home", "Virtualisation & containers", "https://proxmox.home.lab", login=True, lan=True, admin=True), Tool("proxmox-backup", "Proxmox Backup", "home", "VM/CT backup server", "https://proxmox_backup.home.lab", login=True, lan=True, admin=True), # ---- Infrastructure & Network ------------------------------------- Tool("grafana", "Grafana", "infra", "Metrics dashboards", "https://grafana.home.lab", login=True, lan=True, admin=True), Tool("uptimekuma", "Uptime Kuma", "infra", "Uptime monitoring & alerts", "https://uptimekuma.lab.audasmedia.com.au", login=True), Tool("apprise", "Apprise", "infra", "Notification & messaging API", "https://apprise.lab.audasmedia.com.au"), Tool("portainer", "Portainer", "infra", "Docker management", "https://portainer.home.lab", login=True, lan=True, admin=True), Tool("dozzle", "Dozzle", "infra", "Container logs (LAN)", "https://dozzle.home.lab", login=True, lan=True, admin=True), Tool("watchyourlan", "WatchYourLAN", "infra", "LAN device scanning (LAN)", "https://watchyourlan.home.lab", login=True, lan=True, admin=True), Tool("phpipam", "phpIPAM", "infra", "IP address manager (LAN)", "https://phpipam.home.lab", login=True, lan=True, admin=True), Tool("flare", "Flare", "infra", "Start page (LAN)", "https://flare.home.lab", login=True, lan=True), Tool("ntfy", "Ntfy", "infra", "Push notifications (LAN)", "https://ntfy.home.lab", login=True, lan=True), Tool("worldmonitor", "World Monitor", "infra", "Global monitoring", "https://worldmonitor.lab.audasmedia.com.au", login=True, admin=True), Tool("homepage", "Homepage", "infra", "Start page", "https://homepage.lab.audasmedia.com.au", login=True), Tool("librenms", "LibreNMS", "network", "Network monitoring", "https://librenms.home.lab", login=True, lan=True, admin=True), Tool("pihole", "Pi-hole", "network", "Network-wide ad blocking", "https://pihole.home.lab", login=True, lan=True), ] TOOLS = _cat() def tools_for_user(user) -> list[tuple[str, list[Tool]]]: """Return tools the user is permitted to see, grouped into category sections. Family categories (media/fun, data, storage, home) are open to everyone. Infra/network are gated by the admin flag on each tool. Existing per-user capabilities (chat/image/video/audio/docs) still apply to those categories. """ # Admins see every category regardless of per-user capability flags. if user.is_admin: allowed = {cat: True for cat in ALL_CATEGORIES} else: allowed = { "chat": user.can_chat, "ai": user.can_chat, # ai stack broadly tied to chat entitlement "image": user.can_image, "video": user.can_video, "audio": user.can_audio, "docs": user.can_docs, # open family-facing categories "media": True, "data": True, "storage": True, "home": True, "home-mgmt": True, "dev": True, "portfolio": True, "infra": True, # filtered by per-tool admin flag below "network": True, } sections: dict[str, list[Tool]] = {} for tool in TOOLS: if tool.admin and not user.is_admin: continue if not allowed.get(tool.category, True): continue # Specialise the dsh chat link to the logged-in user's own instance. if tool.id == "dsh": tool = _dsh_for_user(user) sections.setdefault(tool.category, []).append(tool) ordered = [] for cat in ALL_CATEGORIES: if cat in sections: ordered.append((cat, sections[cat])) return ordered def _dsh_for_user(user) -> Tool: base = next(t for t in TOOLS if t.id == "dsh") host = user.username.strip().lower() return Tool( id=base.id, name=base.name, category=base.category, description=base.description, url=f"https://dsh-{host}.lab.audasmedia.com.au", mode=base.mode, status=base.status, external=True, private=True, )