aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-22 12:29:32 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-22 12:29:32 +0200
commit6d1ece3156c3ee7ce3fe1c19aabeb45b34d6436a (patch)
tree1a739d995b97e0d7fddb632b62d6f13da75348a7 /packages
parent79130a9d4eb95db35b1d84f14f9b1b18d18299a7 (diff)
downloadmeshbay-6d1ece3156c3ee7ce3fe1c19aabeb45b34d6436a.tar.gz
feat: node:start auto-provisions config and unlock key
When the wizard calls node:start with {hubUrl, username}, the handler writes a minimal node.toml and a random unlock.key if they don't exist. The daemon then creates the keystore on first start using the unlock file — fully non-interactive. Existing configs are left untouched: if node.toml or unlock.key already exist, provisioning is skipped and the node starts as before. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'packages')
-rw-r--r--packages/meshbay-client/src/main.js35
-rw-r--r--packages/meshbay-client/src/preload.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/app.js6
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/platform.js4
4 files changed, 40 insertions, 7 deletions
diff --git a/packages/meshbay-client/src/main.js b/packages/meshbay-client/src/main.js
index 22e0162..8dcc2ae 100644
--- a/packages/meshbay-client/src/main.js
+++ b/packages/meshbay-client/src/main.js
@@ -744,7 +744,36 @@ function registerBridge() {
} catch { return null; }
}
- ipcMain.handle('node:start', async () => {
+ function provisionNode(hubUrl, username) {
+ const configDir = path.join(os.homedir(), '.config', 'meshbay');
+ fs.mkdirSync(configDir, { recursive: true });
+
+ const configFile = nodeConfigPath();
+ if (!fs.existsSync(configFile)) {
+ const toml = [
+ '[hub]',
+ `url = "${hubUrl}"`,
+ `username = "${username}"`,
+ '',
+ '[node]',
+ 'quic_port = 19010',
+ 'ui_port = 18000',
+ '',
+ '[keystore]',
+ `unlock_file = "${path.join(configDir, 'unlock.key')}"`,
+ '',
+ ].join('\n');
+ fs.writeFileSync(configFile, toml, { mode: 0o600 });
+ }
+
+ const unlockFile = path.join(configDir, 'unlock.key');
+ if (!fs.existsSync(unlockFile)) {
+ const key = crypto.randomBytes(32).toString('base64url');
+ fs.writeFileSync(unlockFile, key + '\n', { mode: 0o600 });
+ }
+ }
+
+ ipcMain.handle('node:start', async (_e, opts) => {
const already = await probeNode();
if (already) return { started: true, ...already };
@@ -752,6 +781,10 @@ function registerBridge() {
throw new Error('Automatic node start is only supported on Linux');
}
+ if (opts && opts.hubUrl && opts.username) {
+ provisionNode(opts.hubUrl, opts.username);
+ }
+
const deadline = Date.now() + 15000;
const configFile = nodeConfigPath();
let launched = false;
diff --git a/packages/meshbay-client/src/preload.js b/packages/meshbay-client/src/preload.js
index c8553a5..f240995 100644
--- a/packages/meshbay-client/src/preload.js
+++ b/packages/meshbay-client/src/preload.js
@@ -87,7 +87,7 @@ contextBridge.exposeInMainWorld('meshbay', {
node: {
detect: () => ipcRenderer.invoke('node:detect'),
installed: () => ipcRenderer.invoke('node:installed'),
- start: () => ipcRenderer.invoke('node:start'),
+ start: (opts) => ipcRenderer.invoke('node:start', opts),
call: (method, path, body) => ipcRenderer.invoke('node:call', method, path, body),
pairingCode: () => ipcRenderer.invoke('node:pairing-code'),
setPairingCode: (code) => ipcRenderer.invoke('node:set-pairing-code', code),
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js
index 4b5ccd3..595674d 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js
@@ -1149,7 +1149,7 @@ function CreateGroupFormSimple({ token, onCreated }) {
// ── Create Group Wizard (Electron-only) ─────────────────────────────────────
-function CreateGroupWizard({ token, onCreated }) {
+function CreateGroupWizard({ token, username, onCreated }) {
const [step, setStep] = useState(0); // 0=node check, 1=details, 2=setup, 3=done
const [nodeStatus, setNodeStatus] = useState(null); // null=loading, object=result
const [nodeStarting, setNodeStarting] = useState(false);
@@ -1201,7 +1201,7 @@ function CreateGroupWizard({ token, onCreated }) {
setNodeStarting(true);
setError('');
try {
- const result = await platform.node.start();
+ const result = await platform.node.start({ hubUrl: HUB, username });
await linkNodeKey(result.pk_node_ed25519);
setNodeStatus({ detected: true, ...result });
setNodeStarting(false);
@@ -6441,7 +6441,7 @@ function App() {
page = html`<${ExplorePage} token=${user.token}
myGroupIds=${groups.map(g => g.id)} />`;
} else if (route === '/create-group') {
- page = html`<${CreateGroupPage} token=${user.token}
+ page = html`<${CreateGroupPage} token=${user.token} username=${user.username}
onCreated=${() => {
hubFetch('/v1/groups/mine', { token: user.token })
.then(data => setGroups(data.groups || []))
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/platform.js b/packages/meshbay-hub/src/meshbay_hub/static/platform.js
index eeffd49..a8e17bf 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/platform.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/platform.js
@@ -227,9 +227,9 @@ export const node = {
async installed() {
return bridge && bridge.node ? bridge.node.installed() : { installed: false };
},
- async start() {
+ async start(opts) {
if (!bridge || !bridge.node) throw new Error('Node bridge not available');
- return bridge.node.start();
+ return bridge.node.start(opts);
},
async call(method, path, body) {
if (!bridge || !bridge.node) throw new Error('Node bridge not available');