feat(theseus/settings): Ariadne's Thread on/off toggle for system-wide BCDN resolution
New card under Settings > Registries: shows whether the system-wide
resolver daemon is running, stopped, or not installed on this machine,
and lets the user turn it on/off without opening the installer.
Ariadne runs as two elevated Windows Scheduled Tasks ("BNS Resolver
Daemon" + "BNS Sia Bridge"). Toggling requires admin — main spawns an
elevated PowerShell (Start-Process -Verb RunAs) that UAC-prompts once
per action, then re-queries state. Query is unelevated
Get-ScheduledTask so status checks are silent.
Three surfaced states:
running - "every browser on this machine resolves BCDN names"
stopped - "only Theseus resolves BCDN names; other browsers won't"
not-installed - link to silentmode.st/tools to grab the standalone installer
Theseus's own resolver is unaffected either way — it lives in-process
and doesn't depend on Ariadne. This toggle only controls what non-
Theseus browsers on the same box can resolve.
This commit is contained in:
parent
5642959eca
commit
10f01644c1
3 changed files with 102 additions and 0 deletions
46
main.js
46
main.js
|
|
@ -2647,6 +2647,52 @@ ipcMain.handle("collision-set-policy", (_e, p) => {
|
||||||
return settings.collisionPolicy;
|
return settings.collisionPolicy;
|
||||||
});
|
});
|
||||||
ipcMain.handle("collision-reset", () => { collisions = { byName: {}, byTld: {} }; saveCollisions(); return true; });
|
ipcMain.handle("collision-reset", () => { collisions = { byName: {}, byTld: {} }; saveCollisions(); return true; });
|
||||||
|
// Ariadne's Thread system-wide resolver — installed by AriadneResolver-Setup.exe
|
||||||
|
// as two Windows Scheduled Tasks ("BNS Resolver Daemon" + "BNS Sia Bridge").
|
||||||
|
// Turning them off means non-Theseus browsers stop resolving BCDN names on
|
||||||
|
// this machine; Theseus itself uses its own in-process resolver so it's
|
||||||
|
// unaffected. Toggling requires admin (tasks run as SYSTEM) — start/stop go
|
||||||
|
// through an elevated powershell that UAC-prompts once per action.
|
||||||
|
const ARIADNE_TASKS = ["BNS Resolver Daemon", "BNS Sia Bridge"];
|
||||||
|
function ariadneQueryState() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const { spawn } = require("child_process");
|
||||||
|
// Get-ScheduledTask returns Ready | Running | Disabled | (missing → error).
|
||||||
|
// Print state or "MISSING" for each task; MISSING on the primary task
|
||||||
|
// means Ariadne isn't installed at all.
|
||||||
|
const ps = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command",
|
||||||
|
"$names=@('BNS Resolver Daemon','BNS Sia Bridge');" +
|
||||||
|
"$names | ForEach-Object { $t = Get-ScheduledTask -TaskName $_ -ErrorAction SilentlyContinue;" +
|
||||||
|
" if ($t) { \"$_=$($t.State)\" } else { \"$_=MISSING\" } }"], { windowsHide: true });
|
||||||
|
let out = "";
|
||||||
|
ps.stdout.on("data", (d) => { out += d; });
|
||||||
|
ps.on("close", () => {
|
||||||
|
const lines = out.trim().split(/\r?\n/).filter(Boolean);
|
||||||
|
const map = Object.fromEntries(lines.map((l) => { const i = l.lastIndexOf("="); return [l.slice(0, i), l.slice(i + 1)]; }));
|
||||||
|
const primary = map["BNS Resolver Daemon"];
|
||||||
|
if (!primary || primary === "MISSING") return resolve({ state: "not-installed" });
|
||||||
|
if (primary === "Running") return resolve({ state: "running" });
|
||||||
|
return resolve({ state: "stopped" });
|
||||||
|
});
|
||||||
|
ps.on("error", () => resolve({ state: "not-installed" }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function ariadneSetState(on) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const { spawn } = require("child_process");
|
||||||
|
// Elevated PowerShell — triggers UAC prompt. -Verb RunAs starts a separate
|
||||||
|
// elevated process; we can't easily capture its stdout, but we don't need
|
||||||
|
// to (re-query state afterwards).
|
||||||
|
const verb = on ? "Start-ScheduledTask" : "Stop-ScheduledTask";
|
||||||
|
const cmd = `foreach ($t in 'BNS Resolver Daemon','BNS Sia Bridge') { try { ${verb} -TaskName $t -ErrorAction Stop } catch {} }`;
|
||||||
|
const ps = spawn("powershell.exe", ["-NoProfile", "-Command",
|
||||||
|
"Start-Process powershell -Verb RunAs -Wait -WindowStyle Hidden -ArgumentList '-NoProfile','-Command',\"" + cmd.replace(/"/g, "`\"") + "\""], { windowsHide: true });
|
||||||
|
ps.on("close", (code) => { if (code === 0) resolve(true); else reject(new Error("UAC declined or task failed (exit " + code + ")")); });
|
||||||
|
ps.on("error", (e) => reject(e));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ipcMain.handle("ariadne-state", () => ariadneQueryState().catch(() => ({ state: "not-installed" })));
|
||||||
|
ipcMain.handle("ariadne-toggle", (_e, on) => ariadneSetState(!!on).catch((e) => ({ ok: false, error: e?.message || String(e) })));
|
||||||
// Storage: clear right now (any subset). "history" also drops the saved-session file.
|
// Storage: clear right now (any subset). "history" also drops the saved-session file.
|
||||||
// ---- Password vault -------------------------------------------------------
|
// ---- Password vault -------------------------------------------------------
|
||||||
// The vault lives at userData/passwords.vault (encrypted). Unlock state is
|
// The vault lives at userData/passwords.vault (encrypted). Unlock state is
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ contextBridge.exposeInMainWorld("cfg", {
|
||||||
collisionState: () => ipcRenderer.invoke("collision-state"),
|
collisionState: () => ipcRenderer.invoke("collision-state"),
|
||||||
setCollisionPolicy: (p) => ipcRenderer.invoke("collision-set-policy", p),
|
setCollisionPolicy: (p) => ipcRenderer.invoke("collision-set-policy", p),
|
||||||
resetCollisions: () => ipcRenderer.invoke("collision-reset"),
|
resetCollisions: () => ipcRenderer.invoke("collision-reset"),
|
||||||
|
ariadneState: () => ipcRenderer.invoke("ariadne-state"),
|
||||||
|
ariadneToggle: (on) => ipcRenderer.invoke("ariadne-toggle", !!on),
|
||||||
// Add-ons management (Settings > Add-ons tab).
|
// Add-ons management (Settings > Add-ons tab).
|
||||||
listAddons: () => ipcRenderer.invoke("addons-list"),
|
listAddons: () => ipcRenderer.invoke("addons-list"),
|
||||||
setAddonEnabled: (id, enabled) => ipcRenderer.invoke("addons-set-enabled", id, !!enabled),
|
setAddonEnabled: (id, enabled) => ipcRenderer.invoke("addons-set-enabled", id, !!enabled),
|
||||||
|
|
|
||||||
|
|
@ -314,6 +314,29 @@
|
||||||
<div id="colSummary" class="pmuted" style="font-size:12.5px"></div>
|
<div id="colSummary" class="pmuted" style="font-size:12.5px"></div>
|
||||||
<div><button id="resetCollisions" class="btn">Reset remembered choices</button></div>
|
<div><button id="resetCollisions" class="btn">Reset remembered choices</button></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row" style="flex-direction:column;align-items:stretch;gap:8px">
|
||||||
|
<div class="txt">
|
||||||
|
<div class="t">System-wide resolver — Ariadne's Thread</div>
|
||||||
|
<div class="d">
|
||||||
|
A local daemon that resolves BCDN names for <b>every browser on this machine</b>
|
||||||
|
(Chrome, Edge, Firefox, etc.), not just Theseus. Turning it off means non-Theseus
|
||||||
|
browsers stop resolving <code>.bch</code> / <code>.x</code> / other BCNR TLDs;
|
||||||
|
Theseus keeps working either way, since it has its own built-in resolver.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="ariadneStatus" class="pmuted" style="font-size:12.5px">checking…</div>
|
||||||
|
<div style="display:flex;gap:8px">
|
||||||
|
<button id="ariadneStart" class="btn" hidden>Turn on</button>
|
||||||
|
<button id="ariadneStop" class="btn" hidden>Turn off</button>
|
||||||
|
<button id="ariadneRefresh" class="btn">Refresh</button>
|
||||||
|
</div>
|
||||||
|
<div id="ariadneMissing" class="pmuted" style="font-size:12px" hidden>
|
||||||
|
Ariadne's Thread isn't installed on this machine. If you skipped it during
|
||||||
|
Theseus setup, you can install it separately from
|
||||||
|
<a href="https://silentmode.st/tools/">silentmode.st/tools</a> — grab
|
||||||
|
<code>AriadneResolver-Setup</code>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<!-- PERFORMANCE -->
|
<!-- PERFORMANCE -->
|
||||||
<section id="performance" hidden>
|
<section id="performance" hidden>
|
||||||
|
|
@ -892,6 +915,37 @@
|
||||||
C.resetCollisions().then(refreshCollisions);
|
C.resetCollisions().then(refreshCollisions);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ---- Ariadne's Thread system-wide resolver toggle -----------------------
|
||||||
|
// Ariadne runs as two elevated Scheduled Tasks ("BNS Resolver Daemon" +
|
||||||
|
// "BNS Sia Bridge"). Toggling requires admin — main spawns an elevated
|
||||||
|
// powershell for start/stop, which prompts UAC once per action.
|
||||||
|
const arStatus = document.getElementById("ariadneStatus");
|
||||||
|
const arStart = document.getElementById("ariadneStart");
|
||||||
|
const arStop = document.getElementById("ariadneStop");
|
||||||
|
const arRefresh = document.getElementById("ariadneRefresh");
|
||||||
|
const arMissing = document.getElementById("ariadneMissing");
|
||||||
|
async function refreshAriadne() {
|
||||||
|
arStatus.textContent = "checking…";
|
||||||
|
arStart.hidden = true; arStop.hidden = true; arMissing.hidden = true;
|
||||||
|
try {
|
||||||
|
const r = await C.ariadneState();
|
||||||
|
if (r.state === "running") {
|
||||||
|
arStatus.innerHTML = 'Status: <b style="color:var(--acid)">running</b> — every browser on this machine resolves BCDN names.';
|
||||||
|
arStop.hidden = false;
|
||||||
|
} else if (r.state === "stopped") {
|
||||||
|
arStatus.innerHTML = 'Status: <b>stopped</b> — only Theseus resolves BCDN names; other browsers won\'t.';
|
||||||
|
arStart.hidden = false;
|
||||||
|
} else {
|
||||||
|
arStatus.textContent = "Status: not installed on this machine.";
|
||||||
|
arMissing.hidden = false;
|
||||||
|
}
|
||||||
|
} catch (e) { arStatus.textContent = "Status check failed: " + (e?.message || e); }
|
||||||
|
}
|
||||||
|
arStart.onclick = async () => { arStart.disabled = true; try { await C.ariadneToggle(true); } finally { arStart.disabled = false; refreshAriadne(); } };
|
||||||
|
arStop.onclick = async () => { arStop.disabled = true; try { await C.ariadneToggle(false); } finally { arStop.disabled = false; refreshAriadne(); } };
|
||||||
|
arRefresh.onclick = refreshAriadne;
|
||||||
|
refreshAriadne();
|
||||||
|
|
||||||
// ---- Passwords section: three states (setup / locked / unlocked) ---------
|
// ---- Passwords section: three states (setup / locked / unlocked) ---------
|
||||||
// The vault lives in main.js — this UI just calls IPC. No plaintext ever
|
// The vault lives in main.js — this UI just calls IPC. No plaintext ever
|
||||||
// sits in this DOM except the value produced by a specific Show/Copy click.
|
// sits in this DOM except the value produced by a specific Show/Copy click.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue