feat(hub): services-index landing page at /#42
Merged
Conversation
Closes the operator-friction Garth flagged: "a single entry point that
links all of our resource pages on these deployments into a single web
UI for easier access by humans. Memorizing all of the ports and
manually typing them all in is tedious."
# What changed
## New: shepherd/ui/hub.html
Landing page rendered at GET / on shepherd-control (port 40117). Three
sections:
1. Quick-link cards for every service on this host:
- Shepherd herd (this app's /herd dashboard) → :40117/herd
- Olla federation + load-balancer → :40114/
- Smart Router gestalt UI → :40115/gestalt/ui
- LiteLLM admin UI → :4000/ui
- Retriever health → :42000/health
- Ollama API → :11434/api/tags
Each card shows: emoji + name + short description, live up/down
status badge (refreshed every 15s), port number, and an Open ↗ link.
Auto-discovers the host hostname from window.location, so links
work from localhost, LAN IP, mesh hostname, or VPN-routed access —
no template substitution required.
2. Live status banner: 'N / M services up' (refreshes with the cards).
3. Documentation + hardware guides section linking to:
- README, Troubleshooting, Retriever guide
- Intel iGPU Vulkan guide + per-hardware docs index
- growlf/intel_nuc_skullcanyon_ollama_with_gpu standalone repo
(per Garth's per-hardware-target OSS-repo doctrine)
- ai-stack GitHub repo + open issues + CONTRIBUTING
## New: shepherd/ui/hub.js
- Renders the service cards from a small SERVICES catalog
- Polls each service's lightweight health endpoint every 15s using
fetch(... mode: 'no-cors'); opaque-response = up, throw/timeout = down
- 3 sec per-probe timeout, runs in parallel via Promise.all
- Updates the overall-status banner with up/total count
## Modified: shepherd/ui/shepherd.css
Appended hub-page styles (~165 lines) under a 'body.hub' selector,
leaving the existing kiosk (body.kiosk) styling untouched:
- .hub-grid: auto-fill card-grid with min 280px columns
- .service-card: dark theme matching kiosk; colored left-border by status
- .service-badge: pill-shaped up/down/checking indicator
- .doc-card: documentation section card grid
## Modified: shepherd/shepherd_control/app.py
Two route changes (no other logic touched):
- GET / → now serves hub.html (was: index.html)
- GET /herd → new; serves index.html (the existing kiosk herd view)
Static-file mount at /ui/* is unchanged. /herd/aggregate JSON data
endpoint is unchanged. The kiosk view JS still works at /herd —
it fetches /herd/aggregate as before.
# Test plan
- [x] Python syntax: python3 -m py_compile shepherd/shepherd_control/app.py
- [x] JS parse: node --check shepherd/ui/hub.js
- [x] HTML tag balance verified
- [ ] Live: GET / returns hub.html with service cards
- [ ] Live: GET /herd returns existing kiosk view, no regression
- [ ] Live: status badges update correctly for running vs stopped services
- [ ] Live: 'Open ↗' links use the same hostname the user accessed by
# Scope notes
Per scope-decision (Medium) on PR #41 / d758e57d:
- IN scope: hub-page + status badges + docs section
- OUT of scope (separate PRs): LiteLLM admin login fix; on-cluster
substrate-verify Shepherd shows all peers / dashboard-redraw issue.
Those are observability-backlog items that fold into followup work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the operator-friction Garth flagged: "a single entry point that links all of our resource pages on these deployments into a single web UI for easier access by humans. Memorizing all of the ports and manually typing them all in is tedious."
What changed
New: shepherd/ui/hub.html
Landing page rendered at GET / on shepherd-control (port 40117). Three sections:
Quick-link cards for every service on this host:
Each card shows: emoji + name + short description, live up/down
status badge (refreshed every 15s), port number, and an Open ↗ link.
Auto-discovers the host hostname from window.location, so links
work from localhost, LAN IP, mesh hostname, or VPN-routed access —
no template substitution required.
Live status banner: 'N / M services up' (refreshes with the cards).
Documentation + hardware guides section linking to:
New: shepherd/ui/hub.js
Modified: shepherd/ui/shepherd.css
Appended hub-page styles (~165 lines) under a 'body.hub' selector, leaving the existing kiosk (body.kiosk) styling untouched:
Modified: shepherd/shepherd_control/app.py
Two route changes (no other logic touched):
Static-file mount at /ui/* is unchanged. /herd/aggregate JSON data endpoint is unchanged. The kiosk view JS still works at /herd — it fetches /herd/aggregate as before.
Test plan
Scope notes
Per scope-decision (Medium) on PR #41 / d758e57d:
Summary
Type of change
Related issues
Testing
docker compose configpasses with no errorsshellcheckChecklist
.env.exampleis up to date (if env vars were added/removed)