- Fix DockerNode default icon: use Anchor instead of Package
- Add border_style option to Group Rectangle (solid/dashed/dotted/double/none)
- Group node type selector by category (Hardware, Virtualization, IoT, Generic)
- Add tests for border style feature
Token was visible in server logs, browser history, and proxy access logs.
Backend now accepts the connection first, then validates a JSON auth
message {"token": "<jwt>"} sent by the client on open before adding
the socket to the active connections pool.
- Replace crypto.randomUUID() with a polyfill (generateUUID) that falls
back to crypto.getRandomValues or Math.random — fixes crash on HTTP
non-secure contexts where randomUUID is unavailable
- Fix WebSocket URL hardcoding port 8000 — use window.location.host so
connections go through Nginx proxy in Docker/LXC instead of bypassing it
- Add /api/v1/status/ws/ location block in nginx.conf with WebSocket
upgrade headers (must precede /api/ to avoid missing Upgrade header)
Silent return when label was empty made the Add button appear broken.
Native browser required tooltip doesn't render in Radix Dialog portals.
Now shows inline red error message and highlights the field.
- Add parent_id to NodeUpdate schema in backend so PATCH /nodes/{id}
accepts it (was silently ignored before)
- Expose parent_id in update_node MCP tool schema so the MCP SDK
forwards it to the backend instead of stripping it
- Add regression tests in both backend and MCP layers
- Replace BaseHTTPMiddleware with pure ASGI middleware in auth.py to fix
the "Unexpected message: http.response.start" crash on SSE streams
- Migrate from SseServerTransport to StreamableHTTPSessionManager in main.py
- Add _slim_canvas() in tools.py to strip React Flow layout fields from
get_canvas responses (60-80% payload reduction)
- Update tests: assert canvas slimming, mock session_manager.handle_request
in auth tests to avoid uninitialized task group errors
Exposes homelab topology to MCP-compatible AI clients (Claude Code, etc.)
over LAN via HTTP/SSE on port 8001.
- New mcp/ service: FastAPI + mcp SDK, SSE transport
- Auth: X-API-Key for AI clients, X-MCP-Service-Key for backend (Docker-internal)
- Resources: canvas, nodes, edges, scan/pending, scan/runs
- Tools: create/update/delete nodes+edges, trigger scan, approve/hide devices
- Backend deps.py: accepts JWT or MCP service key (no plain-text password)
- 40 tests (auth, resources, tools) across asyncio + trio
- docker-compose.yml: mcp service on port 8001
- README: MCP setup section with Claude Code/Desktop config examples
- Add animated toggle per edge in EdgeModal (cyan switch, "Flow Animation")
- SVG-native <animate> element for reliable cross-browser dot animation
- Persist animated field: backend model, schemas (EdgeBase/EdgeUpdate/EdgeSave), DB migration
- Include animated in App.tsx edgesToSave serialization so it survives save/reload
- Add animated: bool to EdgeData TypeScript type
- Add hideIp / toggleHideIp to canvasStore (UI-only, not persisted)
- Add maskIp util: 192.168.1.115 → 192.168.XX.XX (non-IPv4 passthrough)
- BaseNode reads hideIp from store and masks last two octets when active
- Sidebar shows Eye/EyeOff toggle below Scan Network, highlights when active
- Exclude peer-to-peer edges from Dagre to keep same-type nodes at same rank
- Snap peer group Y to average, re-space X preserving user's original order
- Reverse upward edges (sourceHandle=top) in Dagre so child nodes always render below parent
- Add layout.test.ts covering peer alignment, X ordering, and upward edge cases
- Add custom SVG favicon and Logo component (house + network nodes motif)
- Update page title to Homelable with meta description
- Show Logo in sidebar header and toolbar
- Add theme store and ThemeModal for canvas style switching
- Refactor node colors and edge styles for theme support
- Add groupRect entry to NODE_DEFAULT_COLORS (required by Record<NodeType, NodeColors>)
- Explicitly type Select onValueChange callbacks as (v: string) to satisfy
stricter type inference in tsc -b build mode
- Any TCP service (except port 22 and known non-HTTP protocols) is now
clickable — fixes Sonarr, Radarr, Jellyfin, Proxmox web UI, etc.
- HTTPS auto-detected for port 443/8443 or names containing ssl/tls/https
- Non-HTTP blocklist: FTP, SMTP, DNS, LDAP, SMB, MySQL, Postgres,
Redis, MongoDB, Kafka, Memcached, etc.
- Inline "Add service" form in detail panel (name, port, tcp/udp)
- Remove button (×) on hover for each service row
- Group rect nodes excluded from detail panel
- getServiceUrl extracted to utils/serviceUrl.ts
- 13 new tests (79 total, all pass)
- New GroupRectNode: rounded rect with NodeResizer (8 handles), no
connection handles, always behind network nodes via negative zIndex
- GroupRectModal: label, font preset (Inter/Mono/Serif), 3×3 text
position grid, text/border/background color pickers, z-order 1–9
- Sidebar: "Add Rectangle" button (below Add Node), group rects
excluded from node count stats
- Save/load: size persisted in custom_colors.width/height, no backend
schema changes required
- elevateNodesOnSelect=false on ReactFlow so selected rects never
pop above network nodes
- Tests: 3 new store tests + 9 GroupRectModal tests (66 total, all pass)
- Prompt for container ID with auto-detected default (pvesh nextid)
- Prompt for root password with confirmation loop instead of auto-generating
- Both still overridable via CTID/ROOT_PASSWORD env vars for automation
- ping not available in base Debian 12 LXC — was silently hitting 20s timeout
- curl may also be missing — install it explicitly before running lxc-install.sh
- use ip route check for network readiness instead of ping
- install-proxmox.sh: runs on PVE host, creates Debian 12 LXC via pct,
then calls lxc-install.sh inside the container (tteck community-scripts pattern)
- lxc-install.sh: runs inside the container, fixed to use .env instead of
config.yml, correct bcrypt hash, proper CORS_ORIGINS with container IP
- README: clarify that install-proxmox.sh runs on the PVE host
On fresh installs the data/ dir may not exist, causing SQLite to create a
stub file without write permissions. mkdir(parents=True, exist_ok=True) runs
at import time before any DB operation.
Also add data/.gitignore to prevent homelab.db from ever being committed.