Files
AetherForge/PROBLEMS.md
AetherForge e65753ce49
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
fix: second-wave bug fixes, security hardening, visual polish, UX improvements
Bug fixes:
- SRV-B1 through B5: server config, router, server_info, syscheck corrections
- BA-03 through BA-06: builder path and universal build cleanup (BLD-D1 through D3)
- Frontend WS race condition and useVisibleInterval hook fixed
- Drive-root path bug in PathForge resolved
- Upload error handling improved

Security:
- PathForge path traversal guard added
- Script injection escaping applied throughout

Visual (DV-01 through DV-15):
- Cyan design tokens and page headers standardised
- Dead CSS removed from Pages.css, PathTracerPage.css, wealth-deck.css
- SacredPageHeader deleted (replaced inline); sidebar version display fixed

UX:
- Emberwake request storm fixed (EmberwakePage.tsx)
- Help blurbs UH-01, UH-02, UH-06 added
- HelpTips wired into Crucible, Fleet (FleetGroupsStrip, FleetToolbar), Settings

Build:
- vite.config.ts: webroot auto-sync on build
- build_universal.go: universal build artifact cleanup
- PROBLEMS.md tracking updated
2026-06-06 17:08:15 -07:00

663 lines
29 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### Open — medium / by design
| Issue | Notes |
|-------|-------|
| **`bof_execute` disabled** | Agent returns explicit error — in-memory BOF execution disabled for safety (`client.go`). |
| **Process hollowing AMSI/ETW** | Relocation patching done; Defender/ETW detection causes ~50% failure — bypass not implemented (`hollow_windows.go`). |
| **Cloudflared on Linux server** | In-process tunnel start is Windows-only; Linux/macOS builds use no-op stub — use external connector (`AF_TUNNEL_EXTERNAL`) or add cross-platform launcher. |
| **macOS camera / GPU miner** | Stubs return "not supported"; Linux has V4L2 + nvidia-smi path. |
| **Agent WireGuard auto-download** | Windows `ensureWGExe()` downloads WireGuard on first Path Tracer use — heavy side effect; pre-install recommended. |
| **Non-Windows Path Tracer agent parity** | `pathtracer_stub.go` returns error on `wg_setup`; Linux/macOS agents cannot join WireGuard chains. |
| **KEV heuristics** | Non-Windows agents return catalog with `Status: n/a` — Windows-only CVE matching.
### Open — large / deferred
| Issue | Notes |
|-------|-------|
| **Non-Windows agent parity** | `pathtracer_stub.go` returns error JSON on `wg_setup`; Linux/macOS cannot join chains. UI filters platform; server does not validate `platform` field. |
| **In-memory sessions only** | `TraceSession` map in handler RAM — server restart loses session state while agents may still hold tunnels; no DB persistence or startup sweep. |
| **WireGuard auto-download** | Windows `ensureWGExe()` silently downloads/installs WireGuard on first use — heavy, needs admin, no progress UI. |
| **No PathTracerPage Vitest** | Page logic (polling, chain selection, QR modal) untested; only `uiHelp.test.ts` covers help keys. |
| **No agent-side pathtracer tests** | `pathtracer_windows.go` / stub have zero Go tests. |
| **NAT / symmetric UDP** | UPnP + DB IP fallback may still yield unreachable endpoints; no STUN/TURN or connectivity probe after configure. |
| **Fixed listen port 51820** | All hops use same UDP port — multiple agents behind one NAT may conflict; no per-hop port allocation. |
| **Agent display name vs hostname** | WS auth `UpsertAgent` overwrites `name` with hostname on every connect — operator-renamed fleet labels may not appear in Path Tracer unless re-saved after connect. |
### Open — medium / UX
| Issue | Notes |
|-------|-------|
| **Visual island (green vs deck)** | Page uses `#00ffaa` WireGuard chrome while `data-operator-deck='pathtracer'` sets blue accent (`operatorDeck.css`); intentional branding split (see DV-07). |
| **Status polling only** | UI polls `/status` every 2s — no WebSocket push for hop progress; acceptable latency but stale during orchestration. |
| **Error path leaves tracing UI** | On `status.error`, `tracing` clears but session remains server-side until operator clicks End or TTL — hops show failed state in chain panel. |
---
### Open — large / deferred
| Issue | Notes |
|-------|-------|
| **No CI-automated HTTP forge** | `scripts/e2e-validate.ps1 -ForgeAgent` still manual (multipart fusion, long compile). Live compile gated behind `LIVE_FORGE=1` + `-tags liveforge`. |
| **PathForge test gaps** | Only `TestPathForgePlacedExcludesHintFile`; no skipped-counter, lock-original, or frontend UI tests. |
| **Forge cancellation / batch races** | Cancel token API tested in isolation; concurrent batch forge + mid-compile cancel not covered. |
| **Non-Windows forge host limits** | PE disguise (`disguise_stub.go`) and Windows signing (`sign_stub.go` / osslsigncode) are platform-constrained by design. |
| **Mac PathForge runtime dependency** | `.command` launchers curl `/api/download/agent-mac` at runtime — requires reachable `server_url` and pre-placed agent binary. |
### Open — low
| Issue | Notes |
|-------|-------|
| ~~\pathforge_test.go\ dead loop~~ | **Fixed.** Replaced the no-op loop with meaningful assertions: each entry Files must contain the hint file and at least one launcher companion, confirming the hint is placed but not counted in Placed. Pre-existing err := redeclaration compile error in pathforge.go L128 also fixed. |
### Open (document-only / deferred)
| Issue | Notes |
|-------|--------|
| Dual storage without sync policy | Complex cross-tab sync — session preferred over local; `aetherforge-auth` event on logout |
| Flaky progress simulation vs. real compile time | Cosmetic — stage timeline caps at 94% until server responds (45 min client timeout) |
| Path Forge / batch fusion test gaps | Cancellation, partial batch failure, cancel-token races — needs dedicated tests |
| DashboardPage tests emit ECONNREFUSED stderr | Failure-path tests; happy-dom hits `localhost:3000`; tests pass |
| DownloadButton mock aliasing pattern | Document for new download helpers — shared mock fn already in `components.test.tsx` |
--
### Open (2026-06-06 audit — LARGE)
- **`tunnel_stream` not implemented** — server-side TCP reverse relay documented as future work (`README.md`).
- **Server `cloudflared` launcher no-op on non-Windows** — Linux server deploy cannot auto-launch tunnels (`cloudflared/launcher_stub.go`).
### Low (open)
- ~~**`mergeConfig` partial-PUT `UseTLS` legacy behavior**~~ **Clarified.** The API PUT handler uses `mergeConfigExplicit` with a field-mask so absent keys never reset booleans. The legacy `mergeConfig` (file-load fallback only) unconditionally copies bools -- documented with a header comment in config.go. Test at config_test.go L526-528 updated to assert the expected behavior and explain the distinction.
### High (open)
- **Non-Windows Path Tracer agent parity** — `pathtracer_stub.go` returns error on `wg_setup`; Linux/macOS agents cannot join WireGuard chains.
### Medium (open)
- **Agent WireGuard auto-download:** Windows agent `ensureWGExe()` downloads and silently installs WireGuard from `download.wireguard.com` on first Path Tracer use if not already present (`agent/client/pathtracer_windows.go`). Heavy side effect; no server-side fix — operator should pre-install WireGuard on fleet hosts or accept first-run download latency.
- Mac PathForge `.command` still depends on `server_url` + `/api/download/agent-mac` at runtime (now validated at forge time).
| Op | Command | Notes |
|----|---------|-------|
| SMB share enumeration | `smb_shares` | Windows + Remote Aggressive; ARP/subnet hosts ? `net view` JSON |
| Spread status | `spread_status` | In-memory last sweep (`deploy/spread_status.go`); read-only |
| Credential names | `credential_vault_list` | Win Credential Manager / macOS Keychain / Linux secret-tool + `~/.ssh` paths — names only |
| Secure wipe | `secure_wipe` | Overwrite-then-delete folder; system-root guards; confirm in UI |
| Port-forward matrix | `tunnel_ssh_forward` × N | `CruciblePortForwardMatrix` — multi-row grid on selected Windows nodes |
UI: Phase C controls in `CrucibleExpandedOps.tsx` Fleet Maintenance (replaces “coming soon” stubs).
---
### Linux / macOS parity
| Area | Status |
|------|--------|
| **Mining + hashrate** | RandomX pure-Go engine works on Linux/macOS; stats loop sends `hashrate_15s/1m/15m` + shares over WS. |
| **Idle schedule guard** | **Fixed** — `SystemCPUPercent` was always 0 on Unix (`reporter_unix.go`), blocking idle-mode mining; Linux uses `/proc/stat`, macOS uses `sysctl kern.cp_time`. |
| **Screenshot** | Linux: scrot / ImageMagick `import` / gnome-screenshot. macOS: `screencapture`. |
| **Camera** | Linux V4L2 via ffmpeg/fswebcam (`camera_linux.go`). macOS: stub. |
| **File ops** | Cross-platform (`file_ops_unix.go` / `file_ops_windows.go`). |
| **Posture** | Unix collectors return firewall/AV/patch data (`posture_unix.go`), not all n/a. |
| **Spread** | SSH path on Linux/macOS (`autospread_unix.go`); SMB/WinRM Windows-only by design. |
| **Firewall ops** | Linux ufw/iptables (`firewall_linux_ops.go`); **macOS pf + socketfilterfw** (`firewall_darwin_ops.go`); other Unix stubs. |
| **GPU miner** | Windows-only T-Rex path; Linux/macOS stub with detect-only. |
| **Docker E2E** | `docker/docker-compose.yml` — isolated agent + server; see `docker/README.md`. |
### Open
- **Client:** WebSocket/beacon paths integration-only in CI (Docker Tier 2 closes Linux slice).
- **macOS:** camera, GPU miner — stubs or partial; firewall aggressive ops implemented (see Backend BE-04).
- **Linux screenshot:** headless containers need `xvfb` + scrot or custom `command` field.
- **`bof_execute` permanently disabled** — handler always fails (`client.go`); product/safety decision.
- **Linux/macOS GPU RVN mining broken** — `detectGPU()` may find NVIDIA, but `spec()` downloads Windows `.exe` miners (`gpu_miner.go`, `gpu_detect_stub.go`).
- **Mesh P2P without `-tags p2p`** — default build reports 0 peers; UI exposes `mesh_status` with re-forge hint (`mesh_p2p_stub.go`).
- **Process hollowing AMSI/ETW bypass not implemented** — documented ~50% failure rate (`hollow_windows.go`).
- **KEV exposure scan non-Windows** — all CVEs marked `n/a` (`cve_scan_stub.go`).
- **Unknown Unix CPU stats stub returns 0** — can break idle-mining guard on exotic platforms (`cpu_stub.go`).
### Open (document-only / deferred)
| Issue | Notes |
|-------|--------|
| Dual storage without sync policy | Complex cross-tab sync — session preferred over local; `aetherforge-auth` event on logout |
| Flaky progress simulation vs. real compile time | Cosmetic — stage timeline caps at 94% until server responds (45 min client timeout) |
| Path Forge / batch fusion test gaps | Cancellation, partial batch failure, cancel-token races — needs dedicated tests |
| DashboardPage tests emit ECONNREFUSED stderr | Failure-path tests; happy-dom hits `localhost:3000`; tests pass |
| DownloadButton mock aliasing pattern | Document for new download helpers — shared mock fn already in `components.test.tsx` |
| ~~`server/webroot` not auto-synced on `npm run build`~~ | **Fixed.** Changed `vite.config.ts` `build.outDir` from `dist` to `../webroot` (with `emptyOutDir: true`). `npm run build` now writes directly to `server/webroot/` -- no manual copy step required. |
| Vitest stderr noise | `FleetTopologyMap` three.js tags warn in happy-dom — tests pass |
---
### Open (needs product copy or broader pass)
| ID | Issue | Notes |
|----|-------|-------|
| ~~UH-01~~ | ~~Crucible expanded ops (spread/tunnels/recon buttons)~~ | **Fixed:** Added `HelpTip` to the four most confusing individual buttons (Spread Now, Subnet Scan, Hole Punch, Start Tunnel) in `CrucibleExpandedOps.tsx`, plus 10 new keys in `uiHelp.ts`. All section headers already carry `helpField` via `CrucibleCollapsibleSection`. |
| ~~UH-02~~ | ~~Fleet Roster / Agents page bulk toolbar~~ | **Fixed:** Added `HelpTip` to filter row (`fl_filter_chips`), bulk-action bar (`fl_bulk_actions`) in `FleetToolbar.tsx`, and Groups label (`fl_groups`) in `FleetGroupsStrip.tsx`. |
| UH-03 | Emberwake / War Room campaign widgets | Funnel stages need `HelpTip` parity with Command Deck funnel |
| UH-04 | Mission Deck | Page has minimal operator guidance |
| UH-05 | Builder mission wizard chips | Inline blurbs exist on chips; not all advanced forge sections have `HelpTip` (see `docAnchors.test` gap list) |
| ~~UH-06~~ | ~~Settings tabs beyond Calibrate/Forge~~ | **Fixed:** Added `HelpTip` to Fleet Alerts heading (`set_alerts`), Alert Notifications heading (`set_alert_notifications`), and Webhook URL field (`set_webhook`) in `SettingsPage.tsx`. |
### Open (visual / UX debt)
| ID | Issue | Files / notes |
|----|-------|----------------|
| DV-01 | **Neon cyan fragmentation** — canonical token is `--neon-cyan: #00e8f5` but components hard-code `#00f5ff`, `#00e5ff`, and `#0ff` fallbacks | `AgentRemoteActions.css`, `HashrateChart.tsx`, `BuildManagerPage.tsx`, `ProtocolTunnelPanel.css`, `FileManager.css` (fixed), sacred geometry SVGs |
| DV-02 | **Page header patterns diverge** — most pages use `deck-hero` + eyebrow; Build Manager uses `bm-header` / `bm-title`; Path Tracer uses green `.pt-title` (`#00ffaa`); Forge says “The Forge” vs nav “Forge” | `BuildManagerPage.tsx/css`, `PathTracerPage.css`, `BuilderPage.tsx`, `Layout.tsx` NAV |
| DV-03 | **`SacredPageHeader` unused** — component + `.page-header--sacred` CSS exist but no page imports it; dead design path | `SacredPageHeader.tsx`, `sacred-geometry.css` |
| DV-04 | **`Pages.css` duplicate / conflicting rules** — two `.empty-state` blocks (L192 vs L1078); second `.page-header h1` block overrides `visual-polish` gradient when page CSS loads after global polish | `Pages.css`; load order via per-page imports |
| DV-05 | **Default dashboard subtitle** — out-of-box copy is “security is just an emotion” until Calibrate overrides `dashboard_subtitle`; reads as placeholder to new operators | `DashboardPage.tsx` L70; `SettingsPage.tsx` L131 |
| DV-06 | **Path Tracer buried in nav** — desktop sidebar lists it last; mobile hides it under “More” while Crucible/Forge are primary tabs | `Layout.tsx` MOBILE_PRIMARY vs MOBILE_MORE |
| DV-07 | **Path Tracer visual island** — green WireGuard aesthetic (`#00ffaa`) does not use operator-deck page classes or brass/neon deck chrome | `PathTracerPage.css`, `PathTracerPage.tsx` (missing `operator-deck-page`) |
| DV-08 | **Chart lazy-load placeholder is invisible** — `ChartPlaceholder` is an empty div at 35% opacity; advanced charts pop in with layout shift | `DashboardPage.tsx` L51–53 |
| DV-09 | **Dead chart badge styles** — `.chart-live.sample` / `.blend` in wealth-deck CSS; `resolveChartSeries` no longer emits sample mode | `wealth-deck.css`; `chartSampleData.ts` |
| DV-10 | **Docs vs in-app naming drift** — wiki says “Calibrate (Settings)”, “Command Deck”, “Forge / Builder”; nav uses “Calibrate”, “Command Deck”, “Forge”; Emberwake route was `/spread` redirect | `public/docs/index.html`, `Layout.tsx`, `App.tsx` |
| DV-11 | **Public spread landing vs Emberwake** — `/spread/` static kit uses `aether.css` deck tokens (good) but typography/spacing differs slightly from in-app Emberwake cards | `public/spread/assets/aether.css`, `EmberwakePage.css` |
| DV-12 | **Emoji in status bar / actions** — ?? DOCS pill, agent action buttons (?? ? ?) inconsistent with otherwise SVG-icon nav | `SystemStatusBar.tsx`, `AgentRemoteActions.tsx` |
| DV-13 | **No light theme** — entire product is dark-only; docs wiki matches but no `prefers-color-scheme` path | global styles |
| DV-14 | **Sidebar version hard-coded** — footer shows `v0.0.1` regardless of server build | `Layout.tsx` L321 |
| DV-15 | **Mission Deck formatting** — source file has excessive blank lines (likely formatter artifact); harder to maintain, no runtime impact | `MissionDeckPage.tsx` |
### Remaining doc gaps (need product copy / user input)
| Topic | Notes |
|-------|-------|
| Default dashboard subtitle | “security is just an emotion” until Calibrate override — marketing copy decision (DV-05) |
| SocGholish / fake-update lander | Documented in spread playbook `#third-party` tab; no shipped branded HTML template |
| OAuth redirect abuse playbook | Research only — no Entra app wizard in Emberwake |
| Earnings USD quote | `TEST_RESULTS.md` notes low priority; not wired |
| Screenshot placeholders in wiki | `[Screenshot: …]` divs — need real captures from operator deck |
| Sidebar version `v0.0.1` | Hard-coded in `Layout.tsx` — should read server build version (DV-14) |
| Light theme | Dark-only documented; no `prefers-color-scheme` path (DV-13) |
---
### Deferred — needs refactor, heavy mocks, or external deps
| Area | Why deferred |
|------|--------------|
| `agent/cmd/mine-validate` | Standalone CLI (`main` package); exercises RandomX + live Stratum; run manually or in mining CI |
| Cloudflared `Start` with real token | Spawns/downloads binary + network; only empty-token path covered |
| `FleetScheduler.Start` loop | 1-minute ticker; logic covered via direct `tickInterval`/`tickCron` in same-package tests |
| CruciblePage full integration | Large page; helpers + `CrucibleExpandedOps` + `FileManager` tested separately |
| Fleet panel widgets | Partially covered via `components.test.tsx`; full panel flows need WS mocks |
| Path Forge / batch fusion cancel races | Needs dedicated cancel-token harness |
| Playwright E2E (`server/web/e2e/`) | Phase 8 of test-suite; requires build + temp server |
| Docker mining E2E | `.github/workflows/ci-docker-mining.yml` — separate tier |
| Mesh P2P | Requires `-tags p2p` build tag |
| Platform-specific fusion launchers | OS-gated; crypto path covered in `media_crypto_test.go` |
---
| Suite | Result | Notes |
|-------|--------|-------|
| `go test ./...` (repo root) | **N/A** | No root `go.mod`; use per-module dirs (documented in Integration audit). |
| `agent/` | **PASS** | All packages ok (`client` ~3s). |
| `server/` | **PASS** | `internal/api` ~69s, `internal/builder` ~32s. |
| `fusion/` | **PASS** | Includes `media_crypto_test.go` (untracked in git at time of pass). |
| `server/web` Vitest | **PASS** | 66 files / 587 tests (3 consecutive full runs). |
| Item | Notes |
|------|-------|
| Vitest stderr `ECONNREFUSED 127.0.0.1:3000` | Failure-path / bulk-command tests in `DashboardPage`, `AgentsPage`, etc. (see Dashboard section). |
| `FleetTopologyMap` three.js ref warnings | happy-dom; cosmetic stderr. |
| Vite build chunk size warnings | `three` / vendor bundles > 500 kB; not a test failure. |
---
## Bugs — Builder/Security (Bug Team 4 audit, 2026-06-06)
*Scope: `server/internal/builder/`, `fusion/`, `server/config.go`, cross-cutting API input validation and auth edge cases.*
### Fixed in this pass
| ID | Fix |
|----|-----|
| BLD-01 | **PowerShell injection in uninstaller** — `processName` and `persistenceKey` were embedded in single-quoted PS1 strings without escaping apostrophes. A `WorkerName` like `foo'; Invoke-Expression …; '` would break out of the string. Now uses `strings.ReplaceAll(…, "'", "''")` for both fields, consistent with how `installRel` was already escaped (`uninstall.go`). |
| BLD-02 | **JSON build request body unbounded** — non-multipart `POST /builder/build` decoded `r.Body` without a size limit; a 1 GiB JSON body would buffer entirely. Fixed: `http.MaxBytesReader` capped to 512 KiB before `json.Decode` (`handler.go`). |
| BLD-03 | **Unbounded backup pool/URL arrays** — `BackupServerURLs`, `BackupPools`, and `RVNBackupPools` from user input are concatenated verbatim into the generated Go source (`generateBuiltinConfig`). A request with 10 000 entries would produce a multi-MB `.go` file, slowing or crashing `go build`. Fixed: arrays truncated to 10 entries each in `normalizeRequest` (`handler.go`). |
| BLD-04 | **Partial build dir not cleaned on failure** — when `compileWorker`, `buildFusionFromRequest`, `writeUninstallScript`, or `MkdirAll` fail mid-build, the entire `builds/<uuid>/` tree (containing a copy of agent source + uploaded fusion payload) was left on disk. Added `cleanupBuild()` closure that calls `os.RemoveAll(buildDir)` on each failure return path (`handler.go`). |
| BLD-05 | **Weak random password entropy** — `generateRandomPassword()` used only 4 random bytes (8 hex chars, 32-bit entropy), guessable in ~4 billion attempts. Increased to 8 bytes (16 hex chars, 64-bit entropy) (`router.go`). Test updated. |
| BLD-06 | **Malformed `config.json` silently ignored** — `json.Unmarshal` failure was swallowed; operator saw no indication their config was rejected and defaults were running instead. Added `fmt.Fprintf(os.Stderr, …)` warning on parse failure (`config.go`). |
### Deferred / large
| ID | Severity | Location | Description |
|----|----------|----------|-------------|
| ~~**BLD-D1**~~ | ~~High~~ | `server/internal/builder/pathforge.go` | **FIXED.** Added `validateRootPath` to `PathForgeHandler.ServeHTTP`: rejects any `root_path` containing `..` segments and enforces an allowlist of safe prefixes (server `dataDir`, user home directory, OS temp directory) via `isAllowedRootPath` / `isPathUnder`. Paths outside these prefixes return HTTP 400. |
| ~~**BLD-D2**~~ | ~~Medium~~ | `server/internal/builder/pathforge.go` `batContent` / `macContent` | **FIXED.** Added four escaping helpers — `escapeBat` (`%``%%`, `"``\"`), `escapeBatPS` (adds `'``''` for PowerShell single-quoted strings inside a cmd.exe `-Command` argument), `escapeShDouble` (`\`, `"`, `$`, `` ` `` backslash-escaped for bash double-quoted strings), `escapeShSingle` (`'`→`'\''` for bash single-quoted strings). Applied: `batContent` uses `escapeBat` for `ren`/`start` arguments and `escapeBatPS` for the embedded PowerShell `-Command` string; `macContent` uses `escapeShDouble` for filenames and `escapeShSingle` for `serverURL`. |
| ~~**BLD-D3**~~ | ~~Low~~ | `server/internal/builder/build_universal.go` | ~~**No partial build cleanup for universal builds.**~~ **Fixed.** Added `cleanupBuild := func() { _ = os.RemoveAll(buildDir) }` at the top of `buildUniversalAgent`, `finishSpreadKit`, and `finishUniversalFusion`, and called it on every failure return, matching the BLD-04 pattern. Also fixed a pre-existing `err :=` → `err =` redeclaration compile error in `pathforge.go` L128 that was blocking all builder test compilation. |
---
## Bugs — Server (Bug Team 1 audit, 2026-06-06)
### Deferred / large
| ID | Severity | Location | Description |
|----|----------|----------|-------------|
| ~~**SRV-B1**~~ | ~~High~~ | `server/main.go` | ~~**No graceful shutdown on SIGINT/SIGTERM.**~~ **Fixed.** `http.ListenAndServe` replaced with `http.Server` + goroutine; `signal.NotifyContext(syscall.SIGINT, syscall.SIGTERM)` drives a `srv.Shutdown(ctx)` with 10 s timeout on signal, allowing all `defer` calls (`cloudflared.Stop()`, `database.Close()`, `maintenance.StopRetentionJobs()`, `fleetSched.Stop()`) to run cleanly. |
| ~~**SRV-B2**~~ | ~~Medium~~ | `server/internal/db/sqlite.go` | ~~**SQLite max-connections not configured.**~~ **Fixed.** `db.SetMaxOpenConns(1)` added immediately after `sql.Open`; the single-connection pool eliminates concurrent-writer WAL-lock contention and `SQLITE_BUSY` errors under load. |
| ~~**SRV-B3**~~ | ~~Medium~~ | `server/internal/scheduler/fleet_scheduler.go` | ~~**O(tasks × agents) DB queries per minute.**~~ **Fixed.** `BulkLastFleetTaskRuns` added to the `db` package; `tickInterval` now pre-fetches all relevant `fleet_task_runs` rows in a single query and checks an in-memory `map[string]time.Time` (keyed `"agentID:taskID"`) in the nested loop — 1 query per tick instead of tasks × agents. |
| ~~**SRV-B4**~~ | ~~Medium~~ | `server/internal/api/websocket.go` `broadcastDashboard` | ~~**Stale-conn cleanup races with `HandleDashboardWS` teardown.**~~ **Fixed.** `broadcastDashboard` no longer spawns a goroutine to delete the dashboard map entry on write failure. It only closes the connection; `HandleDashboardWS` already owns all map cleanup via its existing `defer`, so the double-delete and the spurious `presence_update{online:false}` are eliminated. |
| ~~**SRV-B5**~~ | ~~Low~~ | `server/internal/api/agent_ws_limiter.go` | ~~**Rate-limiter map never purges zero-entry keys.**~~ **Fixed.** `delete(agentWSRateLim.attempts, clientIP)` called when `len(filtered) == 0` after the expiry sweep; map keys are reclaimed as IP addresses churn out of the window. |
---
## Backend
- **`db.New` / `MkdirAll`:** Already returns error on failure (`server/internal/db/sqlite.go`); remove stale “Low (open)” note in Server API section when editing that doc block.
---
## Frontend
- **`RemoteDirBrowser` removal:** No broken imports. UI logic lives in `FileManager.tsx` + `src/help/remoteDirBrowser.ts`; `remoteDirBrowser.test.ts` covers helpers. Deleted `RemoteDirBrowser.tsx` / `.css` are not referenced elsewhere.
- **`FileManager.test.tsx`:** Untracked but picked up by Vitest (`src/**/*.test.{ts,tsx}`); 10 tests pass — add to git when committing Fleet work.
- **Download test mocks:** Prefer separate `vi.fn()` per export when mocking `api/download` (real module aliases `downloadApiFile` to `downloadAuthedFile`; shared mock caused order-dependent flakes).
---
### Open — large / architecture
| Issue | Notes |
|-------|-------|
| **Monolithic WebSocket context** | Every `useWebSocket()` consumer re-renders on any WS state change. Split into `FleetContext` / `EventsContext` or selector hook (`useAgents()`) for true isolation. |
| **`CruciblePage` size (~2k lines)** | Single component owns terminal, fleet list, tabs, file manager — hard to memoize subtrees; consider section components + `React.memo` boundaries. |
| **Per-agent `stats_update` broadcast** | Backend sends one dashboard message per agent stats tick; no batching/coalescing in `websocket.go`. Fleet of N agents ? N JSON parses/frame on client. |
| **`SystemStatusBar` REST poll duplicates WS** | Polls `listAgents` every 15s though fleet already streams via WebSocket — wire readout to WS or drop agent poll. |
| **Vite chunk size** | `three` (~600 kB) and vendor bundles trigger build warnings; FleetTopologyMap loads three on Dashboard — already lazy but still heavy first open. |
| **No terminal virtualization** | DOM cap at 400 lines helps; full virtual list (react-window) needed for 2000-line scrollback without mount cost. |
| **MatrixRain always mounted in Layout** | Runs on every route including mobile (hidden sidebar but component still mounts on desktop). Consider `content-visibility` or route-gated mount. |
| **Path Tracer 2s REST poll** | No WS push for hop progress; acceptable but adds load during orchestration. |
### Open — medium
| Issue | Notes |
|-------|-------|
| **Emberwake war room double feed** | 15s client poll + 30s server `runWarRoomBroadcast` — redundant; prefer WS-only with poll fallback. |
| **CursorFire + SacredGeometry on all routes** | Desktop-only effects still mount with Layout; gate on `VisualEffectsContext` or route. |
| **Earnings estimate on every hashrate change** | Dashboard debounces via `totalHashrate` effect — could share chart sampler interval. |
---
### Deferred — large / complex
| Issue | Location | Notes |
|-------|----------|-------|
| ~~**WebSocketProvider async race**~~ | `context/WebSocketProvider.tsx` | **Fixed (2026-06-06).** Added `openingRef` (`useRef(false)`) in-flight guard -- a second `connect()` call while a ticket fetch is in progress returns early. Added `AbortController` (`ticketAbortRef`) to cancel any prior in-flight fetch; aborted invocations bail before creating a `WebSocket`. `openingRef` is always reset in a `finally` block. |
| ~~**`useVisibleInterval` calls `fn` on every dep change**~~ | `hooks/usePageVisible.ts` | **Fixed (2026-06-06).** `fn` is now stored in a `fnRef` (`useRef`). The effect depends only on `ms`, `enabled`, and `visible` -- an unstable `fn` reference no longer re-runs the effect or triggers an extra immediate call. The interval always invokes `fnRef.current()` so callers always see the latest `fn` without extra renders. |
---
### Deferred — large / medium
| ID | Issue | Location | Notes |
|----|-------|----------|-------|
| ~~BA-03~~ | ~~**`write()` has no write deadline**~~ | `client/client.go` — `write()` | **Fixed.** `c.conn.SetWriteDeadline(time.Now().Add(15*time.Second))` is now called immediately before `WriteJSON` (and cleared afterward), so a stalled TCP socket cannot hold `c.mu` indefinitely and deadlock share submission, stats, and command-result goroutines. |
| ~~BA-04~~ | ~~**SSH/SCP spread commands have no overall timeout**~~ | `deploy/autospread_unix.go` — `attemptSSHSpread()` | `ConnectTimeout=3` limits only the TCP handshake; after a successful connection, `scp.Run()` and `ssh … start.Run()` have no deadline. A slow or unresponsive host stalls the goroutine indefinitely, holding a slot in `spreadSem` (16 total). With 16 such hangs in flight, all future spread goroutines block waiting on the semaphore. Fix: use `exec.CommandContext` with a ~30 s deadline wrapping the whole SCP + SSH sequence. **Fixed:** `exec.CommandContext` with `context.WithTimeout(30s)` wraps the full SCP + SSH sequence; a hung host releases its semaphore slot after 30 s. |
| ~~BA-05~~ | ~~**GPU miner binary download has no HTTP timeout or body-size cap**~~ | `client/gpu_miner.go` — `downloadAndExtract()` | `http.Get(url)` with no timeout and `io.ReadAll(resp.Body)` with no size limit. A slow redirect or a response that trickles bytes forever will hang the goroutine; a gigabyte-scale response could OOM the agent. Fix: use an `http.Client` with a 5-min overall timeout, and wrap the body in `io.LimitReader(resp.Body, 512<<20)`. **Fixed:** `http.Client{Timeout: 5*time.Minute}` + `io.LimitReader(resp.Body, 512<<20)`. |
| ~~BA-06~~ | ~~**`CollectFullSysCheck` blocks for 45+ s on empty subnets**~~ | **Fixed (simpler approach):** Reduced `maxHosts` from 56 to 20 in `ScanLocalSubnet()` call in `syscheck.go`. Caps worst-case scan at ~16 s on an empty /24. No API change needed. |
---
## Infrastructure
- **Root `fix.py`, `fix3.py`, `fix4.py`:** Untracked one-off Python string/regex editors targeting `CruciblePage.tsx` (ProtocolTunnel `onDispatch`, GPU hashrate label). Intended changes appear already applied in `CruciblePage.tsx`. Safe to delete after review; not run in CI — do not rely on them for builds.
- **CI:** Only `.github/workflows/ci-docker-mining.yml` (Docker mining); no root Makefile test target found.
- **`data/login-credentials.json`, `data/users.json`:** Untracked; do not commit (secrets/local data).