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;
|
||||
});
|
||||
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.
|
||||
// ---- Password vault -------------------------------------------------------
|
||||
// The vault lives at userData/passwords.vault (encrypted). Unlock state is
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ contextBridge.exposeInMainWorld("cfg", {
|
|||
collisionState: () => ipcRenderer.invoke("collision-state"),
|
||||
setCollisionPolicy: (p) => ipcRenderer.invoke("collision-set-policy", p),
|
||||
resetCollisions: () => ipcRenderer.invoke("collision-reset"),
|
||||
ariadneState: () => ipcRenderer.invoke("ariadne-state"),
|
||||
ariadneToggle: (on) => ipcRenderer.invoke("ariadne-toggle", !!on),
|
||||
// Add-ons management (Settings > Add-ons tab).
|
||||
listAddons: () => ipcRenderer.invoke("addons-list"),
|
||||
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><button id="resetCollisions" class="btn">Reset remembered choices</button></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>
|
||||
<!-- PERFORMANCE -->
|
||||
<section id="performance" hidden>
|
||||
|
|
@ -892,6 +915,37 @@
|
|||
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) ---------
|
||||
// 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.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue