HearthNet / docs /reports /IMPROVEMENTS.md
GitHub Actions
feat: P0/P1 β€” Image, OCR, Translation tabs + styled headers on all tabs
b132c6e
|
Raw
History Blame Contribute Delete
11.5 kB
# HearthNet β€” Master Task & Improvement List
*Updated June 15, 2026 Β· Priority order: highest impact first*
---
## βœ… DONE β€” Hackathon Critical
| # | Task | Commit |
|---|------|--------|
| C1 | Demo video recorded and linked in README | ee40c33 |
| C2 | Social post on X @zX14_7 | ee40c33 |
| C3 | NVIDIA_API_KEY set in HF Space secrets | β€” |
| C4 | Deploy `app_nemotron.py` as second HF Space (`HearthNet-Nemotron`) | feat/nemotron-space |
| C5 | MiniCPM3-4B as default model in main Space (OpenBMB + Tiny Titan) | ee40c33 |
| C6 | Modal deploy fix β€” `scaledown_window` replacing deprecated param | ee40c33 |
| C7 | GitHub Codex commits pushed | ee40c33 |
| C8 | NVIDIA API key removed from Gradio frontend (SEC-1 critical fix) | 1c211eb |
| C9 | `/data` permission graceful fallback β€” Space no longer crashes | c9bf597 |
---
## βœ… DONE β€” Services Wired Up
Previously implemented in code but never connected or visible:
| # | Task | What was broken | Commit |
|---|------|----------------|--------|
| W1 | Nemotron tab added to main Gradio UI | `nemotron.py` existed, never imported | c021486 |
| W2 | Voice tab (STT + TTS) β€” new `voice.py` | STT/TTS services registered but no UI | c021486 |
| W3 | FederationService (M14) registered in `install_services()` | Never registered anywhere | c021486 |
| W4 | ImageGenerateService gets Florence2Backend at init | Was instantiated with empty `backends=[]` | c021486 |
| W5 | `edge-tts`, `faster-whisper`, `pytesseract` added to requirements | Not in requirements, so backends silently failed | c021486 |
| W6 | HearthNet theme applied to main app (purple/dark) | `hearthnet_theme` defined but never passed to `gr.Blocks` | latest |
| W7 | Custom CSS header, badges, animated status dot, button hover | Main app had zero custom styling | latest |
| W8 | Voice tab asyncio pattern fixed for Gradio async context | `get_event_loop()` fails inside Gradio's running loop | latest |
---
## πŸ”΄ P0 β€” Must fix before next demo
### P0-1 β€” Image tab: upload β†’ Florence2 describe
**Impact:** Shows off Florence2 + vision capability β€” judge can upload a photo and see AI describe it
**Effort:** 1 hour
**File to create:** `hearthnet/ui/tabs/image.py`
**Capability:** `img.describe@1.0`
**Add to `ui/app.py`:** `from hearthnet.ui.tabs.image import build_image_tab` + `with gr.Tab("πŸ–Ό Image"): build_image_tab(bus)`
```python
# Minimal implementation:
def build_image_tab(bus):
img_input = gr.Image(type="filepath", label="Upload image")
describe_btn = gr.Button("πŸ” Describe with Florence2", variant="primary")
description_out = gr.Textbox(label="Description", lines=4)
def _describe(path):
import base64
with open(path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
result = _run(bus.call("img.describe", (1,0), {"input": {"image_b64": b64}}))
return result.get("output", {}).get("caption", result.get("caption", str(result)))
describe_btn.click(_describe, inputs=[img_input], outputs=[description_out])
```
### P0-2 β€” OCR tab: upload scan/PDF β†’ text
**Impact:** Tesseract/TrOCR β€” a common real-world need
**Effort:** 45 min
**File to create:** `hearthnet/ui/tabs/ocr.py`
**Capability:** `ocr.image@1.0`, `ocr.pdf@1.0`
```python
def build_ocr_tab(bus):
ocr_input = gr.File(label="Upload image or PDF", file_types=[".png",".jpg",".pdf"])
ocr_btn = gr.Button("πŸ“„ Extract Text", variant="primary")
ocr_out = gr.Textbox(label="Extracted text", lines=10)
def _ocr(file_path):
cap = "ocr.pdf" if file_path.endswith(".pdf") else "ocr.image"
import base64
with open(file_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
result = _run(bus.call(cap, (1,0), {"input": {"file_b64": b64}}))
return result.get("output", {}).get("text", str(result))
ocr_btn.click(_ocr, inputs=[ocr_input], outputs=[ocr_out])
```
### P0-3 β€” Translation tab: text + language β†’ translated text
**Impact:** NLLB-200 covers 200 languages β€” real differentiator
**Effort:** 30 min
**File to create:** `hearthnet/ui/tabs/translation.py`
**Capability:** `trans.text@1.0`
```python
def build_translation_tab(bus):
src_text = gr.Textbox(label="Text to translate", lines=5)
src_lang = gr.Textbox(label="Source language", value="en")
tgt_lang = gr.Textbox(label="Target language", value="de")
translate_btn = gr.Button("🌍 Translate", variant="primary")
out_text = gr.Textbox(label="Translation", lines=5)
def _translate(text, src, tgt):
result = _run(bus.call("trans.text", (1,0),
{"params": {"source_lang": src, "target_lang": tgt},
"input": {"text": text}}))
return result.get("output", {}).get("text", str(result))
translate_btn.click(_translate, inputs=[src_text, src_lang, tgt_lang], outputs=[out_text])
```
---
## 🟠 P1 β€” High value, medium effort
### P1-1 β€” Apply styled HTML headers to ALL tabs
**Current state:** Only voice.py and nemotron.py have styled section headers. Ask, Chat, Mesh, etc. have plain Markdown.
**Fix:** Add a `gr.HTML(...)` styled section header at the top of each `build_*_tab()` function, matching the gradient style in `voice.py`.
### P1-2 β€” Rate limiting on `/bus/v1/call` and `/relay/v1/*`
**File:** `app.py` β€” `_mount_bus_endpoints()`
**Fix:** Wire `RateLimiter` from `hearthnet/bus/backpressure.py`
```python
from hearthnet.bus.backpressure import RateLimiter
_limiter = RateLimiter(max_calls=60, window_seconds=60)
@app.middleware("http")
async def _rate_limit(request, call_next):
ip = request.client.host if request.client else "unknown"
if request.url.path.startswith(("/bus/v1", "/relay/v1")):
if not _limiter.allow(ip):
return JSONResponse({"error": "rate_limited"}, status_code=429)
return await call_next(request)
```
### P1-3 β€” Capability token expiry enforcement
**File:** `hearthnet/bus/router.py`
**Fix:** Check `token.exp < time.time()` before routing β€” `exp` is stored in the token but never validated.
### P1-4 β€” E2E encryption as default in chat (M23)
**File:** `hearthnet/services/chat/service.py`
**Status:** X3DH + Double Ratchet implemented in `hearthnet/crypto/` but `ChatService.send()` sends plaintext
**Fix:** Thread ratchet state through `ChatService._sessions` dict, encrypt payload before bus dispatch.
### P1-5 β€” Wire `node.start()` properly in `app.py`
**File:** `app.py`
**Currently:** `node.install_services()` called manually β€” skips mDNS, transport start, gossip sync
**Fix:**
```python
# Replace:
_node.install_services(corpus="community")
# With (in a thread or via asyncio.get_event_loop):
loop.run_until_complete(_node.start(corpus="community"))
```
### P1-6 β€” Gossip sync between nodes (X02)
**File:** `hearthnet/node.py` β€” add to `node.start()` after step 9
**Fix:**
```python
from hearthnet.events.sync import SyncServer
self._sync_server = SyncServer(self._event_log, self.peers)
asyncio.create_task(self._sync_server.run())
```
Enables marketplace posts and RAG documents to auto-replicate across mesh nodes.
---
## 🟑 P2 β€” Medium value
### P2-1 β€” Evidence UI tab (M30)
**Capabilities:** `evidence.claim.add`, `evidence.claim.attest`, `evidence.claim.dispute`
**Service:** `EvidenceService` β€” registered in research mode (`install_extended_services(research=True)`)
**File to create:** `hearthnet/ui/tabs/evidence.py`
**Add to ui/app.py** once implemented.
### P2-2 β€” Civil Defense UI tab (M31)
**Capabilities:** `civdef.alert.issue`, `civdef.cert.*`, `civdef.audit.chain`
**Service:** `CivilDefenseService` β€” registered in research mode
**File to create:** `hearthnet/ui/tabs/civdef.py`
### P2-3 β€” Routing trace as flow chart (U2)
**Current state:** Routing trace shown as plain text in Ask tab
**Fix:** Render as HTML/Mermaid flow diagram showing scored candidates and winner.
### P2-4 β€” Peer capability matrix in Mesh tab (U5)
**File:** `hearthnet/ui/tabs/mesh.py`
**Add:** Table showing each discovered peer and their capabilities.
### P2-5 β€” Dark mode toggle
**File:** `hearthnet/ui/app.py`
**Note:** `hearthnet/ui/theme.py` already has `emergency_theme` as a dark variant. Add a toggle.
### P2-6 β€” Model selection UI in Settings tab
**Current state:** Model is fixed at startup via `MODEL_ID` env var
**Fix:** Add a dropdown in Settings showing available registered backends + restart hint.
### P2-7 β€” Relay hub rate limiting
**File:** `hearthnet/transport/relay_hub.py`
**Fix:** Max 5 join attempts per IP per minute to prevent roster flooding.
---
## 🟒 P3 β€” Future / research
### P3-1 β€” ShardServer.forward() for distributed inference (M26)
**File:** `hearthnet/distributed_inference/shard.py:75` β€” `NotImplementedError`
**Requires:** torch model slicing, attention head partitioning across nodes
### P3-2 β€” FedLearnCoordinator.aggregate() (M28)
**File:** `hearthnet/fedlearn/coordinator.py:95` β€” `NotImplementedError`
**Requires:** peft, LoRA delta accumulation, secure aggregation protocol
### P3-3 β€” LoRa hardware serial port (M29)
**File:** `hearthnet/lora/service.py:96` β€” silent stub without pyserial
**Fix:** `serial.Serial("/dev/ttyUSB0", 9600)` + add `pyserial>=3.5` to requirements
### P3-4 β€” Browser ↔ Python mesh bridge
**Files:** `webagent/src/mesh/browsermesh.js` (PeerJS/WebRTC)
**Status:** Browser mesh and Python relay run as separate isolated meshes
**Fix:** Bidirectional WebRTC↔mailbox translation, ICE/TURN server
### P3-5 β€” RelayDiscovery.start() (Phase 2 peer discovery)
**File:** `hearthnet/discovery/relay.py:8` β€” `NotImplementedError`
**Fix:** Poll `/relay/v1/roster` on a timer, add discovered peers to `PeerRegistry`
### P3-6 β€” Publish to PyPI
**Command:** `python -m build && twine upload dist/*`
**Note:** `pyproject.toml` is ready
### P3-7 β€” Docker image publish
**File:** `Dockerfile.slim` exists
**Command:** `docker build -t hearthnet:latest . && docker push ghcr.io/ckal/hearthnet:latest`
---
## πŸ“‹ All Tasks β€” Status at a Glance
| Priority | Task | Status |
|----------|------|--------|
| C1–C9 | Hackathon critical items | βœ… All done |
| W1–W8 | Service/UI wiring | βœ… All done |
| P0-1 | πŸ–Ό Image describe tab | βœ… Done |
| P0-2 | πŸ“„ OCR tab | βœ… Done |
| P0-3 | 🌍 Translation tab | βœ… Done |
| P1-1 | Styled headers on all tabs | βœ… Done |
| P1-2 | Rate limiting on bus/relay | ⏳ Open |
| P1-3 | Token expiry enforcement | ⏳ Open |
| P1-4 | E2E encryption default | ⏳ Open |
| P1-5 | node.start() wiring | ⏳ Open |
| P1-6 | Gossip sync | ⏳ Open |
| P2-1 | Evidence UI tab | ⏳ Open |
| P2-2 | CivilDefense UI tab | ⏳ Open |
| P2-3 | Routing trace flow chart | ⏳ Open |
| P2-4 | Peer capability matrix | ⏳ Open |
| P2-5 | Dark mode toggle | ⏳ Open |
| P2-6 | Model selection UI | ⏳ Open |
| P2-7 | Relay hub rate limiting | ⏳ Open |
| P3-1 | M26 distributed inference | ⏳ Future |
| P3-2 | M28 federated learning | ⏳ Future |
| P3-3 | M29 LoRa hardware | ⏳ Future |
| P3-4 | Browser↔Python mesh bridge | ⏳ Future |
| P3-5 | RelayDiscovery.start() | ⏳ Future |
| P3-6 | PyPI publish | ⏳ Future |
| P3-7 | Docker image publish | ⏳ Future |
---
## See Also
- [`docs/ENV.md`](../ENV.md) β€” all environment variables, secrets, and model reference
- [`docs/SECURITY_FINDINGS.md`](../SECURITY_FINDINGS.md) β€” SEC-1 through SEC-8 with fixes
- [`hackathon_final_step.md`](../../hackathon_final_step.md) β€” project status and prize tracking