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

29 KiB
Raw Blame History

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.gowrite() | 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).