From 8c184a7f9a332247032143e733b989a82b743f22 Mon Sep 17 00:00:00 2001 From: William Mantly Date: Mon, 10 Aug 2026 20:53:47 -0400 Subject: [PATCH] feat(mesh): GET /api/mesh/self for local bootstrap self-IP discovery A no-inbound spoke's join script (theta-suite's bootstrap/site-join.js) needs its own gateway's mesh IP to hand to sso-manager-node's /api/site/join, but the only existing read (GET /api/mesh/gateways) requires a full jump-admin session -- unusable from an unattended bootstrap script. Add a narrower read gated only by a valid jmp_ API token (any self-service token, same as theta-proxy's prx_ tokens for proxy_client.js), exposing just this gateway's own mesh IP. --- nodejs/routes/mesh.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/nodejs/routes/mesh.js b/nodejs/routes/mesh.js index 6908c0a..15a2811 100644 --- a/nodejs/routes/mesh.js +++ b/nodejs/routes/mesh.js @@ -154,6 +154,24 @@ router.post('/join', middleware.auth, middleware.requireJumpAdmin, async (req, r } catch (e) { next(e); } }); +// This gateway's own mesh address, for a LOCAL bootstrap script to discover +// (e.g. theta-suite's site-join, running on the same host as this gateway) +// without needing full jump-admin session auth -- any valid jmp_ API token +// (middleware.auth, no requireJumpAdmin) is enough, same service-to-service +// pattern as theta-proxy's prx_ tokens for proxy_client.js. Not a peer +// listing, so no admin-only audit/config data is exposed here. +router.get('/self', middleware.auth, async (req, res, next) => { + try { + const self = conf.wireguard || {}; + let meshIp = null; + if (self.serverPublicKey) { + const entry = await meshGateway.findByPublicKey(self.serverPublicKey); + if (entry) meshIp = meshCidrFor(entry.meshIndex).split('/')[0]; + } + res.json({ status: 'ok', meshIp, joined: !!meshIp, iface: IFACE }); + } catch (e) { next(e); } +}); + router.get('/gateways', middleware.auth, middleware.requireJumpAdmin, async (req, res, next) => { try { const gateways = await meshGateway.list();