157 lines
12 KiB
Markdown
157 lines
12 KiB
Markdown
<!-- GSD:project-start source:PROJECT.md -->
|
|
## Project
|
|
|
|
**HWLab**
|
|
|
|
HWLab is a self-hosted, AI-powered hardware inventory management system for homelab environments. It combines local multimodal AI (Gemma 4 via oMLX) for photo-based intake and categorization, NetBox as the authoritative inventory database, and automated label printing with integrated cable testing workflows. Built with Go + React, running entirely on a Mac Mini M4.
|
|
|
|
**Core Value:** Any physical item can be cataloged by uploading a photo — AI extracts data, creates a NetBox record, and prints a QR-coded label — with zero manual data entry for 80-90% of items.
|
|
|
|
### Constraints
|
|
|
|
- **Hardware**: Mac Mini M4 with 16GB — Gemma 4 model must fit in memory (E4B confirmed, 26B A4B needs testing with TurboQuant)
|
|
- **No cloud dependency**: Standard operations must work fully local — OpenRouter only for Tier 2/3 AI
|
|
- **NetBox is source of truth**: HWLab stores no inventory data locally (only advisor chat history + config in SQLite)
|
|
- **USB protocols**: Printer and tester protocols need reverse-engineering once hardware arrives
|
|
- **Tech stack**: Go backend, React TypeScript frontend, Tailwind CSS
|
|
<!-- GSD:project-end -->
|
|
|
|
<!-- GSD:stack-start source:research/STACK.md -->
|
|
## Technology Stack
|
|
|
|
## Recommended Stack
|
|
### Core Technologies
|
|
| Technology | Version | Purpose | Why Recommended |
|
|
|------------|---------|---------|-----------------|
|
|
| Go | 1.22+ | Backend API, USB/serial control, AI orchestration | Native concurrency for USB polling + HTTP serving; single binary deployment; no runtime dependency; best-in-class serial I/O on macOS |
|
|
| React | 18.x | SPA frontend | Mature ecosystem, hooks-based state management, excellent TypeScript support; right level of complexity for a single-operator tool |
|
|
| TypeScript | 5.x | Frontend type safety | Catches API contract drift early; NetBox has complex nested types that benefit from strict typing |
|
|
| Tailwind CSS | 3.x | Utility-first styling | ClickHouse-inspired design system maps well to utility classes; no CSS file sprawl; dark theme with arbitrary values is trivial |
|
|
| SQLite (via mattn/go-sqlite3) | 3.x / v1.14+ | Local-only storage (chat history, config, session state) | Zero-dependency embedded DB; perfectly suited for single-operator append-heavy chat logs; no separate process |
|
|
| oMLX | latest | Local LLM inference server on Mac Mini M4 | Native Apple Silicon MLX backend, OpenAI-compatible API endpoint, paged SSD KV caching, continuous batching; supports Gemma 4 VLM natively; drop-in for any OpenAI-SDK client |
|
|
### Go Backend Libraries
|
|
| Library | Version | Purpose | When to Use |
|
|
|---------|---------|---------|-------------|
|
|
| github.com/netbox-community/go-netbox/v4 | v4.x | NetBox REST API client | All CRUD against NetBox — officially maintained, generated from NetBox OpenAPI spec |
|
|
| go.bug.st/serial | v1.x | USB serial communication | Printer + cable tester I/O; actively maintained, cross-platform, better API than tarm/serial; supports port enumeration |
|
|
| github.com/gin-gonic/gin | v1.9+ | HTTP router and middleware | Fast, well-documented, large middleware ecosystem; handles file uploads for photo intake cleanly |
|
|
| github.com/mattn/go-sqlite3 | v1.14+ | SQLite driver (cgo) | Standard Go SQLite driver; use with database/sql for portability |
|
|
| github.com/jmoiern/sqlx | v1.3+ | database/sql extension | Named params and struct scanning reduce boilerplate for chat history queries |
|
|
| github.com/sashabaranov/go-openai | v1.x | OpenAI-compatible client | Handles both oMLX local endpoint and OpenRouter remote; single client, swap base URL per tier |
|
|
| github.com/skip2/go-qrcode | v0.0.0 | QR code generation | Generate HW-XXXXX QR codes server-side before sending to printer |
|
|
| github.com/google/uuid | v1.x | UUID generation | HW-XXXXX ID assignment; deterministic or random depending on design choice |
|
|
| github.com/spf13/viper | v1.x | Config management | YAML/env config for AI endpoints, NetBox URL/token, USB port paths |
|
|
### Frontend Libraries
|
|
| Library | Version | Purpose | When to Use |
|
|
|---------|---------|---------|-------------|
|
|
| Vite | 5.x | Build tool and dev server | Fastest HMR in class; Go backend runs separately, Vite proxies API calls in dev |
|
|
| TanStack Query (React Query) | v5 | Server state management | NetBox data is server state not client state; handles caching, refetch, stale-while-revalidate |
|
|
| TanStack Router | v1 | Type-safe routing | Better TypeScript integration than React Router v6 for complex nested routes |
|
|
| Zustand | v4 | Client state (UI state, chat) | Lightweight, no boilerplate, perfect for chat message buffer and USB device status |
|
|
| shadcn/ui | latest | Component primitives | Radix UI + Tailwind, fully owned code (not a dependency), dark mode first, ClickHouse aesthetic compatible |
|
|
| react-dropzone | v14 | Photo drag-and-drop | Standard library for file upload UX; works with multipart form to Go backend |
|
|
| react-hot-toast | v2 | Toast notifications | Inventory actions (label printed, test passed) need lightweight non-blocking feedback |
|
|
| lucide-react | latest | Icon set | Consistent, tree-shakeable, TypeScript-native, pairs well with shadcn/ui |
|
|
### Development Tools
|
|
| Tool | Purpose | Notes |
|
|
|------|---------|-------|
|
|
| Air (cosmtrek/air) | Go hot reload in development | Watch + rebuild backend on save; configure alongside Vite proxy |
|
|
| golangci-lint | Go linting | Run in CI; catches serial/concurrent code issues early |
|
|
| ESLint + typescript-eslint | Frontend linting | Strict TypeScript rules; catch NetBox API type drift |
|
|
| Prettier | Frontend formatting | Single config, no debates |
|
|
| Bruno (or httpie) | API testing | Test NetBox integration and Go endpoints locally without Postman account |
|
|
## Installation
|
|
# Go backend — initialize module
|
|
# Core Go dependencies
|
|
# Frontend — scaffold
|
|
# Frontend core dependencies
|
|
# shadcn/ui init (run in frontend dir)
|
|
# Dev dependencies
|
|
## Alternatives Considered
|
|
| Recommended | Alternative | When to Use Alternative |
|
|
|-------------|-------------|-------------------------|
|
|
| go.bug.st/serial | tarm/serial | tarm/serial is adequate if you only need basic read/write and already have it; go.bug.st/serial is preferred for new projects due to active maintenance and port listing |
|
|
| gin-gonic/gin | chi, echo, fiber | chi if you want stdlib-compatible handlers; echo for similar feature set; fiber if you need extreme HTTP throughput (not relevant here) |
|
|
| go-openai (sashabaranov) | Custom HTTP client | Custom client only if you need streaming SSE to browser — go-openai supports streaming too, prefer it |
|
|
| TanStack Router | React Router v6 | React Router v6 is fine if team is already familiar; TanStack Router has stronger TypeScript guarantees for route params |
|
|
| TanStack Query | SWR | SWR is lighter but TanStack Query has better devtools and mutation handling for NetBox writes |
|
|
| mattn/go-sqlite3 (cgo) | modernc.org/sqlite (pure Go) | Use modernc.org/sqlite if you want zero cgo dependency (simpler cross-compilation); slightly slower but avoids gcc requirement |
|
|
| oMLX | Ollama | Ollama works but oMLX has better Apple Silicon performance via MLX backend; Gemma 4 VLM continuous batching support is oMLX-specific |
|
|
## What NOT to Use
|
|
| Avoid | Why | Use Instead |
|
|
|-------|-----|-------------|
|
|
| GORM | Heavy ORM abstraction for what is essentially two tables (chat logs, config); migrations and reflection overhead not worth it for SQLite local-only store | database/sql + sqlx directly |
|
|
| tarm/serial | Last commit 2018, unmaintained, no port enumeration, open issues unaddressed | go.bug.st/serial |
|
|
| Redux (React) | Massive boilerplate for a solo-operator SPA; overkill when TanStack Query handles server state | Zustand + TanStack Query |
|
|
| axios | Unnecessary HTTP client abstraction; TanStack Query works with native fetch; one fewer dependency | fetch API directly inside TanStack Query |
|
|
| Next.js | SSR/SSG overhead not needed — Go handles the API, React is a pure SPA served as static files | Vite + React SPA |
|
|
| llama.cpp Go bindings | Not optimized for Apple Silicon; MLX backend (oMLX) is 2-4x faster on M-series; cgo binding complexity | oMLX via OpenAI-compatible HTTP |
|
|
| Direct NetBox HTTP calls (no client) | NetBox v4 API has 200+ endpoints; go-netbox generates typed structs from OpenAPI spec — manual HTTP is brittle | go-netbox/v4 |
|
|
## Stack Patterns by Variant
|
|
- Use a single `go-openai` client instance per tier, configured with `BaseURL` pointing to oMLX (`http://localhost:PORT/v1`) for Tier 1, and `https://openrouter.ai/api/v1` for Tier 2/3
|
|
- Switch by returning the appropriate client from a factory based on model config
|
|
- Do NOT hardcode tier selection in business logic — make it config-driven from the start
|
|
- Use `go.bug.st/serial` with a dedicated goroutine per device, communicating via channels
|
|
- Do not share a serial.Port across goroutines — open one port per device, serialize writes in the owning goroutine
|
|
- USB device paths on macOS follow `/dev/cu.usbserial-*` or `/dev/cu.usbmodem*`; use `serial.GetPortsList()` to enumerate at startup
|
|
- Accept multipart/form-data in Gin; decode image in Go; send as base64 in the OpenAI vision API call to oMLX
|
|
- Store the original photo in a local temp directory only until the NetBox record is created; do not persist photos in HWLab itself
|
|
- Gemma 4 E4B fits comfortably in 16GB; Gemma 4 26B A4B needs TurboQuant KV offload — benchmark before committing to 26B
|
|
- go-netbox v4 represents custom fields as `map[string]interface{}`; define typed wrapper structs in your own code to handle `hw_id`, `condition`, `quality_gate` etc.
|
|
- Custom fields must be created in NetBox admin before the Go code can write them — treat this as infrastructure provisioning (Phase 1 task)
|
|
## Version Compatibility
|
|
| Package | Compatible With | Notes |
|
|
|---------|-----------------|-------|
|
|
| go-netbox/v4 | NetBox 4.x | Generated from NetBox 4.x OpenAPI; not compatible with NetBox 3.x API responses |
|
|
| mattn/go-sqlite3 v1.14+ | Go 1.21+ | Requires cgo; needs gcc or Xcode CLT on macOS |
|
|
| shadcn/ui | React 18, Tailwind 3.x | shadcn v2+ requires Tailwind 3.x; not yet tested with Tailwind 4 alpha |
|
|
| oMLX | macOS 15+, Apple Silicon M1+ | Won't run on Intel Mac or Linux |
|
|
| go-openai v1.x | OpenAI API v1, OpenRouter, oMLX | Any OpenAI-compatible endpoint works; set `BaseURL` in `ClientConfig` |
|
|
## Sources
|
|
- [github.com/jundot/omlx](https://github.com/jundot/omlx) — oMLX feature set, Gemma 4 VLM support confirmed (HIGH confidence)
|
|
- [github.com/netbox-community/go-netbox](https://github.com/netbox-community/go-netbox) — Official Go client, v4 confirmed (HIGH confidence)
|
|
- [go.bug.st/serial pkg.go.dev](https://pkg.go.dev/go.bug.st/serial) — Active maintenance confirmed, preferred over tarm/serial (HIGH confidence)
|
|
- [omlx.ai](https://omlx.ai/) — macOS 15+ requirement, 16GB minimum RAM, MLX backend (HIGH confidence)
|
|
- WebSearch: tarm/serial maintenance status — last substantive commit 2018, multiple forks active (MEDIUM confidence)
|
|
- Training data: gin, TanStack Query, Zustand, Vite, shadcn/ui versions — standard 2025 React/Go stack (MEDIUM confidence, verify pinned versions before first install)
|
|
<!-- GSD:stack-end -->
|
|
|
|
<!-- GSD:conventions-start source:CONVENTIONS.md -->
|
|
## Conventions
|
|
|
|
Conventions not yet established. Will populate as patterns emerge during development.
|
|
<!-- GSD:conventions-end -->
|
|
|
|
<!-- GSD:architecture-start source:ARCHITECTURE.md -->
|
|
## Architecture
|
|
|
|
Architecture not yet mapped. Follow existing patterns found in the codebase.
|
|
<!-- GSD:architecture-end -->
|
|
|
|
<!-- GSD:skills-start source:skills/ -->
|
|
## Project Skills
|
|
|
|
No project skills found. Add skills to any of: `.claude/skills/`, `.agents/skills/`, `.cursor/skills/`, or `.github/skills/` with a `SKILL.md` index file.
|
|
<!-- GSD:skills-end -->
|
|
|
|
<!-- GSD:workflow-start source:GSD defaults -->
|
|
## GSD Workflow Enforcement
|
|
|
|
Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.
|
|
|
|
Use these entry points:
|
|
- `/gsd-quick` for small fixes, doc updates, and ad-hoc tasks
|
|
- `/gsd-debug` for investigation and bug fixing
|
|
- `/gsd-execute-phase` for planned phase work
|
|
|
|
Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.
|
|
<!-- GSD:workflow-end -->
|
|
|
|
|
|
|
|
<!-- GSD:profile-start -->
|
|
## Developer Profile
|
|
|
|
> Profile not yet configured. Run `/gsd-profile-user` to generate your developer profile.
|
|
> This section is managed by `generate-claude-profile` -- do not edit manually.
|
|
<!-- GSD:profile-end -->
|