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.
This commit is contained in:
2026-08-10 20:53:47 -04:00
parent b6efcff25e
commit 8c184a7f9a
+18
View File
@@ -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();