fix: overhaul container lifecycle — recovery, health, uninstall, UI state
Some checks failed
Build Archipelago ISO (dev) / build-iso (push) Failing after 13m44s
Container Orchestration Tests / unit-tests (push) Failing after 7m30s
Container Orchestration Tests / smoke-tests (push) Has been skipped

Container recovery:
- Health monitor: MAX_RESTART_ATTEMPTS 3→10, interval 60s→120s
- Dependency-aware restarts: won't restart services before their deps
- Reset dependent counters when a dependency recovers
- Handle "created" state containers (were invisible to health monitor)
- Added IndeedHub, mempool-api, mysql to tier system
- Crash recovery: podman start timeout 30s→120s with retry
- Podman client: socket timeout 5s→30s, added restart policy

UI state representation:
- Exit code 0 shows "stopped" (gray), not "crashed" (red)
- Exit code 137 shows "killed (OOM)"
- Non-zero exit shows "crashed" (red)
- Added exit_code field to PackageDataEntry

Install/uninstall fixes:
- Install returns error when container doesn't start (was silent success)
- Post-install hooks awaited instead of fire-and-forget tokio::spawn
- Uninstall: graceful rm before force, volume prune, network cleanup
- Uninstall returns error on partial failure (was 200 OK)

Config consistency:
- DB passwords read from /var/lib/archipelago/secrets/ (was hardcoded)
- Bitcoin: added ZMQ ports 28332/28333 for LND block notifications
- IndeedHub port 7777→8190 (was conflicting with strfry)
- Marketplace versions: LND 0.17.4→0.18.4, Mempool 2.5.0→3.0.0

Performance:
- Metrics collector interval 60s→300s (was duplicating health monitor)
- Podman client: proper error propagation instead of unwrap_or_default

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-31 07:03:57 +01:00
parent cdff10a8bc
commit 64b57dca7d
65 changed files with 3950 additions and 298 deletions

View File

@@ -142,18 +142,21 @@ Row 2: [C] Files [C] Peer1 [C] Peer2 (etc)
No nav bar.
### Grid `[C]` (2-col)
### Grid `[C]`
```
Row 1: [C] Local Network [C] Web3
Row 2: [C] Quick Actions (etc)
Row 1: [C] Quick Actions (full-width, contains Restart/Check Tor/Auto-Sync/Logs)
Row 2: [C] Local Network [C] Web3
Row 3: [C] Network Interfaces [C] Tor Services
```
| Position | Up | Down | Left | Right | Enter |
|----------------|-----------|---------------|-----------|-----------|------------------|
| Local Network | nothing | Quick Actions | Sidebar | Web3 | Drill into [Y] |
| Web3 | nothing | Quick Actions | Local Net | nothing | Drill into [Y] |
| Quick Actions | Local Net | nothing | Sidebar | nothing | Drill into [Y] |
| Position | Up | Down | Left | Right | Enter |
|----------------------|-----------------|---------------------|---------------|-------------------|------------------|
| Quick Actions | nothing | Local Network | Sidebar | nothing | Drill into [Y] |
| Local Network | Quick Actions | Network Interfaces | Sidebar | Web3 | Drill into [Y] |
| Web3 | Quick Actions | Tor Services | Local Network | nothing | Drill into [Y] |
| Network Interfaces | Local Network | nothing | Sidebar | Tor Services | Drill into [Y] |
| Tor Services | Web3 | nothing | Net Interfaces| nothing | Drill into [Y] |
---
@@ -179,23 +182,28 @@ Standard spatial grid nav. Left from leftmost = Sidebar. Enter = drill into [Y]
### Nav bar `[N]`
```
[N] My Apps [N] App Store [N] Services | [N] Category filters (etc)
[N] My Apps [N] App Store [N] Services | [N] Discover [N] Categories... | [N] Search
```
### Grid `[C]` (3-col)
Down from nav bar → first container. Nav bar remembers last-focused tab — Up from cards returns to it.
### Grid `[C]`
```
Row 0: [C] Featured1 [C] Featured2 [C] Featured3
Row 1: [C] App1 [C] App2 [C] App3
(etc)
Featured (2-col): [C] Featured1 [C] Featured2
All Apps (3-col): [C] App1 [C] App2 [C] App3
[C] App4 [C] App5 [C] App6
(etc)
```
| Position | Up | Down | Left | Right | Enter |
|--------------|-------------|----------|-----------|------------|---------------|
| [N] tabs | nothing | Featured1| left tab | right tab | Switch/filter |
| Featured1 | [N] bar | App1 | Sidebar | Featured2 | View details |
| App1 | Featured1 | App4 | Sidebar | App2 | Install |
| (etc) | above | below | left/side | right | Install |
Cards use same style as My Apps: `glass-card transition-all hover:-translate-y-1`.
| Position | Up | Down | Left | Right | Enter |
|--------------|----------------|----------|-----------|------------|--------------------|
| [N] tabs | nothing | Featured1| left tab | right tab | Switch/filter |
| Featured1 | remembered [N] | App1 | Sidebar | Featured2 | View details |
| App1 | Featured1 | App4 | Sidebar | App2 | Install / details |
| (etc) | above | below | left/side | right | Install / details |
---
@@ -204,11 +212,19 @@ Row 1: [C] App1 [C] App2 [C] App3
### Grid `[C]`
```
Row 1: [C] Device Status [C] Chat Panel
Row 2: [C] Peers List [C] Tab Panel (Bitcoin/Dead Man/Map)
Left column: [C] Device Status [C] Actions [C] Peers List
Right column: [C] Chat Panel [C] Tools (Bitcoin/Dead Man/Map)
```
Spatial grid nav. Enter = drill into controls.
| Position | Up | Down | Left | Right | Enter |
|-----------------|---------------|-----------|-----------|-------------|--------------------------------|
| Device Status | nothing | Actions | Sidebar | Chat Panel | Drill into [Y] |
| Actions | Device Status | Peers | Sidebar | Chat Panel | Drill into [Y] buttons |
| Peers List | Actions | nothing | Sidebar | Chat Panel | Drill into peer rows |
| Chat Panel | nothing | Tools | Device | nothing | Drill into [Y] |
| Tools | Chat Panel | nothing | Peers | nothing | Drill into [Y] |
**Chat flow:** Select a peer/channel (Enter on peer row) → focus auto-jumps to message input → type → Enter sends.
---
@@ -227,23 +243,72 @@ Spatial grid nav. Enter = view node details.
## SETTINGS `/dashboard/settings`
### Grid `[C]` (vertical stack)
**Mixed page:** Two containers ([C] Server Name, [C] Interface Mode) + linear buttons.
Up/Down steps through elements. Right navigates paired items on the same row. Left → sidebar.
Enter on containers → drill in. Enter on buttons → activate. Escape → exit container / sidebar.
`[C]` = Container `[B]` = Button `[I]` = Input `[T]` = Toggle
### Account Section (glass-card)
```
Row 1: [C] Account Info
Row 2: [C] Change Password
Row 3: [C] Two-Factor Auth
Row 4: [C] System Info
Row 5: [C] Danger Zone
1. [C] Server Name → Enter: edit name, Enter: save, Escape: cancel
[B] What's New → right of Server Name
2. [B] Copy DID
3. [B] Copy Onion Address
4. [B] Change Password → opens modal
5. [B] Enable 2FA / Disable 2FA → opens modal
6. [B] Logout
```
| Position | Up | Down | Left | Right | Enter |
|-------------------|-----------------|------------------|---------|---------|------------------|
| Account Info | nothing | Change Password | Sidebar | nothing | Drill into [Y] |
| Change Password | Account Info | Two-Factor | Sidebar | nothing | Drill into [Y] |
| Two-Factor | Change Password | System Info | Sidebar | nothing | Drill into [Y] |
| System Info | Two-Factor | Danger Zone | Sidebar | nothing | Drill into [Y] |
| Danger Zone | System Info | nothing | Sidebar | nothing | Drill into [Y] |
### System Section
```
7. [C] Interface Mode → Enter: drill in, Left/Right between Easy/Gamer/Chat, Enter: select, Escape: exit
[B] Language buttons → below Interface Mode
8. [B] Login with Claude → opens modal
9. [T] Enable All (AI data) + per-category [T] toggles
10. [B] Manage Updates
11. [I] Webhook URL
12. [I] Webhook Secret
13. [T] Container Crash [T] Update Available
14. [T] Disk Space Warning [T] Backup Complete
15. [B] Save Configuration [B] Send Test
16. [T] Enable Beta Telemetry
17. [B] Create Backup
18. [B] Export Channel Backup
19. [B] Network Diagnostics → navigates to /dashboard/server
20. [B] Reboot → opens confirm modal
21. [B] Factory Reset → opens confirm modal
```
| Position | Up | Down | Left | Right | Enter |
|---------------------------|-------------|-------------|---------------|----------------|--------------------|
| 1. Server Name | nothing | Copy DID | Sidebar | What's New | Edit name |
| 1b. What's New | nothing | Copy DID | Server Name | nothing | Show release notes |
| 2. Copy DID | Server Name | Copy Onion | Sidebar | nothing | Copy to clipboard |
| 3. Copy Onion | Copy DID | Change PW | Sidebar | nothing | Copy to clipboard |
| 4. Change Password | Copy Onion | Enable 2FA | Sidebar | nothing | Open modal |
| 5. Enable 2FA | Change PW | Logout | Sidebar | nothing | Open modal |
| 6. Logout | Enable 2FA | Language | Sidebar | nothing | Logout |
| 7. Language | Logout | Claude Login| Sidebar | nothing | Select language |
| 8. Login with Claude | Language | AI toggles | Sidebar | nothing | Open modal |
| 9. AI toggles (each row) | above | below | Sidebar | next toggle | Toggle on/off |
| 10. Manage Updates | AI toggles | Webhook URL | Sidebar | nothing | Open updates |
| 11. Webhook URL | Updates | Secret | Sidebar | nothing | Edit field |
| 12. Secret | Webhook URL | Crash toggle| Sidebar | nothing | Edit field |
| 13a. Container Crash | Secret | Disk Space | Sidebar | Update Avail | Toggle on/off |
| 13b. Update Available | Secret | Backup Done | Container Crash| nothing | Toggle on/off |
| 14a. Disk Space Warning | Crash | Save Config | Sidebar | Backup Done | Toggle on/off |
| 14b. Backup Complete | Update Avail| Send Test | Disk Space | nothing | Toggle on/off |
| 15a. Save Configuration | Disk Space | Telemetry | Sidebar | Send Test | Save |
| 15b. Send Test | Backup Done | Telemetry | Save Config | nothing | Send test webhook |
| 16. Telemetry | Save/Test | Create Bkup | Sidebar | nothing | Toggle on/off |
| 17. Create Backup | Telemetry | Export Chan | Sidebar | nothing | Open modal |
| 18. Export Channel | Create Bkup | Net Diag | Sidebar | nothing | Export |
| 19. Network Diagnostics | Export Chan | Reboot | Sidebar | nothing | → /dashboard/server|
| 20. Reboot | Net Diag | Factory Rst | Sidebar | nothing | Open confirm |
| 21. Factory Reset | Reboot | nothing | Sidebar | nothing | Open confirm |
---
@@ -526,3 +591,70 @@ Default focus: `[B] Set Password`
6. Inside [Y]: arrows move between inner controls. Escape → back to [C].
7. Escape from [C] → Sidebar.
8. No dead ends.
---
## Implementation Notes (for future sessions)
### Key files
- **Navigation logic**: `neode-ui/src/composables/useControllerNav.ts`
- **Controller store**: `neode-ui/src/stores/controller.ts`
- **Nav sounds**: `neode-ui/src/composables/useNavSounds.ts`
- **Focus styles**: `neode-ui/src/style.css` (lines ~53-142, search `focus-visible`)
### Data attributes
| Attribute | Purpose |
|-----------|---------|
| `data-controller-zone="main"` | Main content area (`<main>` in Dashboard.vue) |
| `data-controller-zone="sidebar"` | Sidebar nav |
| `data-controller-container` + `tabindex="0"` | Focusable card tile — gamepad can land on it, Enter drills in |
| `data-controller-install` | Container has Install button (Enter prioritizes it) |
| `data-controller-launch` | Container has Launch button (Enter prioritizes it) |
| `data-controller-install-btn` | The actual Install button inside a container |
| `data-controller-launch-btn` | The actual Launch button inside a container |
| `data-controller-ignore` | Skip element and descendants from gamepad nav |
| `tabindex="-1"` | Remove from gamepad focus order (used on ToggleSwitch) |
### Focus memory keys
| Key | Purpose | Cleared on |
|-----|---------|------------|
| `sidebar` | Last sidebar item focused | never (persists) |
| `main` | Last container/element in main zone | route change |
| `navBar` | Last nav bar tab (for Up return from containers) | route change |
### Navigation handler order (handleKeyDown)
1. **Text inputs** — special handling (Enter submits, Up/Down exits field)
2. **Escape** — close overlays → exit inner controls → exit to sidebar → back on detail pages
3. **Enter** — container actions (install/launch/link/inner) → regular click
4. **Sidebar** — Up/Down wrap, Right → main (containers or first focusable)
5. **Inside container** — arrows move between inner controls, can't leave via arrows
6. **Nav bar items** — Left/Right between tabs, Down/Up to nearest focusable (containers + buttons)
7. **Main zone** — spatial nav through containers + standalone focusables, fallbacks for edges
### Mixed pages (containers + standalone buttons, e.g. Settings)
- `isNavBarItem()` returns false on container-free pages (lets main zone handler do linear nav)
- Both nav bar handler and main zone handler search containers + standalone focusables together
- This prevents "jumping" where Down skips standalone buttons to reach the next container
- The filter `el.hasAttribute('data-controller-container') || !el.closest('[data-controller-container]')` excludes inner buttons
### Container-free pages (e.g. Settings if all containers removed)
- Sidebar → Right: checks `zone.querySelector('[data-controller-container]')` — if none found, focuses first focusable immediately (no 1s poll delay)
- `isNavBarItem()` returns false (prevents nav bar handler from catching everything)
- Main zone handler's spatial nav through all focusables handles Up/Down/Left/Right
### ToggleSwitch component
- Has `tabindex="-1"` and `data-controller-ignore` — invisible to gamepad nav
- Parent button handles the toggle click, so the switch doesn't need its own focus
- Without this, nav gets stuck bouncing between parent button and toggle switch
### Focus glow styles (Chromium gotchas)
- `box-shadow: 0 0 0 Npx` (spread-based ring) does NOT follow `border-radius` on composited layers (`translateZ(0)`)
- `outline` doesn't follow `border-radius` in Chrome < 94
- Safe approach: use blurred `box-shadow` (`0 0 6px 2px`) or `border-color` change for focus rings
- All `[data-controller-container]` have `outline: none !important` to kill browser defaults
- Cards use `glass-card transition-all hover:-translate-y-1` for consistent hover/focus lift
### Mesh chat auto-focus
- `openChat()`, `openChannelChat()`, `openArchChannel()` all call `nextTick(() => chatInputEl.value?.focus())`
- Message input has `@keydown.enter.exact.prevent="handleSendMessage"` — Enter sends immediately
- Ref: `chatInputEl` on the `<input>` element in Mesh.vue