claude-resources/skills/model-catalog/SKILL.md
Use when working with 3D model files (STL), print tracking, slicer profiles, or managing the model-catalog homelab service at model-catalog.internal.lakehouse.wtf
npx skillsauth add festion/homelab-gitops model-catalogInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A homelab FastAPI service for cataloging 3D models, tracking print history, managing slicer profiles, and storing print photos. Deployed as LXC 210 with Vue.js frontend and REST API.
Use this skill when:
| Component | Details |
|-----------|---------|
| URL | https://model-catalog.internal.lakehouse.wtf |
| Container | LXC 210 on proxmox3 (192.168.1.211:8000) |
| Frontend | Vue 3 + TypeScript (served at /) |
| API Docs | /docs (Swagger UI) |
| Database | SQLite at /var/lib/model-catalog/catalog.db |
| Storage | NFS /mnt/models from TrueNAS |
| Endpoint | Method | Purpose |
|----------|--------|---------|
| /api/health | GET | Health check |
| /api/models | GET, POST | List/upload models |
| /api/models/{id} | GET, DELETE | Get/delete model |
| /api/models/{id}/file | GET | Download STL file |
| /api/prints | GET, POST | List/create print jobs |
| /api/prints/{id} | GET, PATCH | Get/update print |
| /api/prints/{id}/complete | POST | Mark print successful |
| /api/prints/{id}/cancel | POST | Cancel print |
| /api/profiles | GET, POST | List/create slicer profiles |
| /api/profiles/{id}/ini | GET | Download profile INI |
| /api/tags | GET, POST | List/create tags |
| /api/stats | GET | Overall statistics |
| /api/printer/status | GET | Printer status (via PrusaLink) |
The mc CLI is available in the container:
# List models
mc list
mc list --tag "functional-prints"
# Search models
mc search "benchy"
# Add model
mc add /path/to/model.stl --name "Test Print" --tags "test,calibration"
# Show details
mc show <model-id>
# Export model
mc export <model-id> /path/to/output.stl
# Delete model
mc delete <model-id>
# Tag operations
mc tag add <model-id> "new-tag"
mc tag remove <model-id> "old-tag"
Via API:
curl -X POST https://model-catalog.internal.lakehouse.wtf/api/models \
-F "[email protected]" \
-F "name=My Model" \
-F "description=Test print" \
-F "tags=functional,test"
Via CLI (on container):
ssh [email protected]
mc add /mnt/models/myfile.stl --name "My Model" --tags "functional,test"
# Start print
curl -X POST https://model-catalog.internal.lakehouse.wtf/api/prints \
-H "Content-Type: application/json" \
-d '{
"model_id": "uuid-here",
"profile_id": "profile-uuid",
"notes": "First test print"
}'
# Complete print (success)
curl -X POST https://model-catalog.internal.lakehouse.wtf/api/prints/{print-id}/complete \
-H "Content-Type: application/json" \
-d '{"result": "success", "notes": "Perfect print"}'
# Mark failed
curl -X POST https://model-catalog.internal.lakehouse.wtf/api/prints/{print-id}/complete \
-H "Content-Type: application/json" \
-d '{"result": "failure", "notes": "Layer shifting at 50%"}'
# Import PrusaSlicer profile
curl -X POST https://model-catalog.internal.lakehouse.wtf/api/profiles/import \
-F "[email protected]" \
-F "name=PLA Standard"
# List profiles
curl https://model-catalog.internal.lakehouse.wtf/api/profiles
# Download profile INI
curl https://model-catalog.internal.lakehouse.wtf/api/profiles/{id}/ini -o profile.ini
LXC 210 (192.168.1.211):
/var/lib/model-catalog/catalog.db # SQLite database
/opt/model-catalog/ # Application code
/etc/model-catalog/env # Configuration
/mnt/models/ # NFS mount (TrueNAS)
├── models/{id}/original.stl.zst # Compressed STL files
├── gcode/{id}.gcode.zst # Generated G-code (temp)
├── photos/{id}/finished.jpg # Print photos
└── profiles/{id}.ini # Slicer profiles
TrueNAS:
/mnt/truenas_nvme/active_projects/model-catalog/ # NFS backing store
Check service status:
ssh [email protected] "pct exec 210 -- systemctl status model-catalog"
Restart service:
ssh [email protected] "pct exec 210 -- systemctl restart model-catalog"
View logs:
ssh [email protected] "pct exec 210 -- journalctl -u model-catalog -f"
Update deployment:
# Package updated code
cd /home/dev/workspace/model-catalog
tar czf /tmp/update.tar.gz --exclude='.git' app/ frontend/dist/
# Deploy to container
scp /tmp/update.tar.gz [email protected]:/tmp/
ssh [email protected] "
pct push 210 /tmp/update.tar.gz /tmp/update.tar.gz
pct exec 210 -- bash -c 'cd /opt/model-catalog && tar xzf /tmp/update.tar.gz'
pct exec 210 -- chown -R model-catalog:model-catalog /opt/model-catalog
pct exec 210 -- systemctl restart model-catalog
"
┌─────────────────────────────────────────────────────────────┐
│ Browser │
│ https://model-catalog.internal.lakehouse.wtf │
└──────────────────┬──────────────────────────────────────────┘
│ DNS → 192.168.1.101 (Traefik VIP)
▼
┌─────────────────────────────────────────────────────────────┐
│ Traefik HA (VIP) │
│ Primary: 192.168.1.110 | Secondary: 192.168.1.103 │
└──────────────────┬──────────────────────────────────────────┘
│ Proxy → 192.168.1.211:8000
▼
┌─────────────────────────────────────────────────────────────┐
│ LXC 210 (model-catalog) - 192.168.1.211 │
│ │
│ ┌────────────────┐ ┌──────────────────┐ │
│ │ FastAPI (8000) │ │ Vue.js Frontend │ │
│ │ - API │ │ - / │ │
│ │ - /api/* │ │ - /assets/* │ │
│ └────────┬───────┘ └──────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ SQLite (/var/lib/model-catalog/) │ │
│ └────────────────────────────────────┘ │
└─────────────────┬───────────────────────────────────────────┘
│ NFS mount → /mnt/models
▼
┌─────────────────────────────────────────────────────────────┐
│ TrueNAS │
│ /mnt/truenas_nvme/active_projects/model-catalog/ │
│ - STL files (zstd compressed) │
│ - G-code (temporary, deleted after success) │
│ - Print photos (JPG from PrusaConnect) │
│ - Slicer profiles (INI) │
└─────────────────────────────────────────────────────────────┘
Set in /etc/model-catalog/env:
| Variable | Default | Description |
|----------|---------|-------------|
| MC_DATABASE_PATH | /var/lib/model-catalog/catalog.db | SQLite database path |
| MC_STORAGE_PATH | /mnt/models | NFS mount point for files |
| MC_COMPRESSION_LEVEL | 3 | Zstd compression (1-22) |
| MC_DEBUG | false | Enable debug logging |
| MC_PRUSASLICER_PATH | /usr/bin/prusa-slicer | PrusaSlicer CLI path |
Check if model exists:
curl -s https://model-catalog.internal.lakehouse.wtf/api/models?search=benchy | jq '.items[0]'
Upload from local file:
curl -X POST https://model-catalog.internal.lakehouse.wtf/api/models \
-F "file=@/path/to/model.stl" \
-F "name=$(basename model.stl .stl)" \
-F "tags=claude-generated"
Get print statistics:
curl -s https://model-catalog.internal.lakehouse.wtf/api/stats | jq
Track automated print:
import httpx
async with httpx.AsyncClient() as client:
# Create print job
response = await client.post(
"https://model-catalog.internal.lakehouse.wtf/api/prints",
json={
"model_id": model_uuid,
"profile_id": profile_uuid,
"notes": "Automated nightly print"
}
)
print_id = response.json()["id"]
# Mark complete when done
await client.post(
f"https://model-catalog.internal.lakehouse.wtf/api/prints/{print_id}/complete",
json={"result": "success"}
)
Problem: Root path returns {"detail":"Not Found"}
Solution: Frontend not configured to serve from /. Check:
# Verify frontend files deployed
ssh [email protected] "pct exec 210 -- ls -la /opt/model-catalog/frontend/dist/"
# Check if main.py has static file mounting
ssh [email protected] "pct exec 210 -- grep -A 5 'StaticFiles' /opt/model-catalog/app/main.py"
Problem: systemctl status model-catalog shows failed
Solution: Check logs:
ssh [email protected] "pct exec 210 -- journalctl -u model-catalog -n 50"
Common causes:
/mnt/models missing)Problem: Traefik route configured but backend unreachable
Solution:
# Check backend is responding
curl http://192.168.1.211:8000/api/health
# Verify Traefik route points to correct IP
ssh [email protected] "grep -A 5 'model-catalog:' /etc/traefik/dynamic/model-catalog.yml"
Local development (in workspace):
cd /home/dev/workspace/model-catalog
# Run backend
uvicorn app.main:app --reload --port 8000
# Run frontend dev server (separate terminal)
cd frontend
npm run dev
# Run tests
pytest
pytest --cov=app
Testing API locally:
# Health check
curl http://localhost:8000/api/health
# Interactive docs
open http://localhost:8000/docs
/home/dev/workspace/model-catalog//home/dev/workspace/model-catalog/docs/DESIGN.md/home/dev/workspace/model-catalog/docs/DEPLOYMENT.md/home/dev/workspace/model-catalog/docs/ACCEPTANCE.mdbd list (beads in /home/dev/workspace/model-catalog/.beads/)tools
TP-Link Omada SDN network management for the Lakehouse homelab. Use when managing switches, access points, VLANs, spanning tree, or WiFi infrastructure. Triggers on phrases like "adopt device", "configure VLAN", "switch CLI", "WiFi troubleshooting", "Omada API", "STP blocking", "PoE budget", or any network hardware management.
devops
Standardized deployment procedure for services in LXC containers on the Proxmox HA cluster. Use when deploying a new service, creating LXC containers, configuring DHCP reservations (Kea), DNS rewrites (AdGuard), reverse proxy routes (Traefik), or integrating with monitoring (Uptime Kuma) and dashboard (Homepage). Triggers on phrases like "deploy a new service", "create container for", "set up DHCP", "add Traefik route", "configure DNS rewrite", or any homelab infrastructure deployment.
testing
Print settings and optimization for Prusa Mini+ with E3D Revo Micro hotend, bowden direct drive extruder, and ObXidian nozzles (0.25mm, 0.4mm, 0.6mm, 0.8mm). Use when the user asks about print settings, slicer profiles, troubleshooting prints, material temperatures, flow rates, nozzle selection, or optimizing prints for this specific printer configuration. Triggers on phrases like "print settings", "slicer profile", "layer height", "print speed", "temperature", "first layer", "stringing", "calibration", "which nozzle", or any 3D printing task mentioning Prusa Mini, Revo, or ObXidian.
tools
Create and edit 3D printable mesh files (STL, 3MF, OBJ) programmatically using Python trimesh + manifold3d. Use when the user asks to create 3D objects, modify existing STL/3MF files, add holes/features to meshes, combine or split meshes, repair meshes, convert between formats, analyze mesh geometry, or generate parametric parts for 3D printing. Triggers on "create STL", "make a 3D model", "edit mesh", "add hole", "modify STL", "3MF", "printable", "mesh repair", "boolean", "combine meshes", or any 3D modeling task that doesn't require a GUI CAD tool.