aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-02 09:14:37 -0700
committerAdam Soltys <[email protected]>2026-08-02 09:14:37 -0700
commite9ef91e007a945ca01d4de45392b15bd257e06a3 (patch)
tree1bd40ba3d8578885ce9ceab33f1b8e813657e4af
parent165c03875369a9c21e69ae1429d51b8e6a3a497c (diff)
downloadcoinosv3-e9ef91e007a945ca01d4de45392b15bd257e06a3.tar.gz
coinosv3-e9ef91e007a945ca01d4de45392b15bd257e06a3.zip
Registrar: an operator endpoint to top the float up over Lightning
The float pays out in Ark while incoming Lightning accumulates on the node, so it needs periodic refilling. It can now mint an invoice against its own balance (token-guarded, since the endpoint is public). Co-Authored-By: Claude Fable 5 <[email protected]>
-rw-r--r--names/server.js31
1 files changed, 31 insertions, 0 deletions
diff --git a/names/server.js b/names/server.js
index bd62f81..57a6e8c 100644
--- a/names/server.js
+++ b/names/server.js
@@ -463,6 +463,37 @@ Bun.serve({
}
}
+ // Top up the float over Lightning: it mints an invoice against its own
+ // ark balance, which anyone (in practice, the operator's node) can pay.
+ // Guarded by a token from the config file — this endpoint is public.
+ if (url.pathname === '/admin/topup' && req.method === 'POST') {
+ if (!CFG.adminToken || req.headers.get('x-admin-token') !== CFG.adminToken) {
+ return json({ error: 'unauthorized' }, 401);
+ }
+ if (!fwd) return json({ error: 'no forwarder wallet' }, 503);
+ const body = await req.json().catch(() => ({}));
+ const sat = Math.floor(body.sat || 0);
+ if (!sat || sat < 1000) return json({ error: 'amount too small' }, 400);
+ try {
+ const a = await fwd.createLnInvoice(sat, 'names float top-up');
+ log(`float top-up invoice for ${sat} sat`);
+ return json({ invoice: a.invoice, paymentHash: a.paymentHash });
+ } catch (e) {
+ return json({ error: e.message }, 500);
+ }
+ }
+
+ // Drive an in-flight lightning receive forward (top-ups settle async).
+ if (url.pathname === '/admin/drive' && req.method === 'POST') {
+ if (!CFG.adminToken || req.headers.get('x-admin-token') !== CFG.adminToken) {
+ return json({ error: 'unauthorized' }, 401);
+ }
+ if (!fwd) return json({ error: 'no forwarder wallet' }, 503);
+ for (const a of fwd.pendingActions()) { await fwd.driveLn(a.id).catch(() => {}); }
+ await fwd.sync().catch(() => {});
+ return json({ ok: true, floatSat: fwd.balance().spendableSat });
+ }
+
if (url.pathname === '/register' && req.method === 'POST') {
if (!rateOk(ip)) return json({ error: 'rate limited' }, 429);
const bodyText = await req.text();