diff options
| author | Adam Soltys <[email protected]> | 2026-09-21 19:32:54 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-21 19:32:54 -0700 |
| commit | d128ee51c292c331ea1f26d745a5fc280fb3f439 (patch) | |
| tree | 408aea3bdbf9eeecab74f238faa70c89259f5d64 /routes | |
| parent | 38f3baf94d08b0e3ec40e6bacc4e330459e2a4d3 (diff) | |
| download | coinos-server-d128ee51c292c331ea1f26d745a5fc280fb3f439.tar.gz coinos-server-d128ee51c292c331ea1f26d745a5fc280fb3f439.zip | |
funds: close deposits, withdrawals and authorizations
The fund mechanism is retired. Deposits (POST /payments with `fund`),
withdrawals (POST /take) and pre-authorizations (POST /authorize) now
refuse unconditionally instead of consulting the `fund:disabled` flag,
so the door cannot be reopened by clearing a key.
The deposit path never had a kill-switch of its own, so with the flag
set users could still pay into funds they were unable to withdraw from.
Read paths are left intact: GET /fund/:id and the manager endpoints
still serve existing funds so balances stay visible and can be settled
out of band.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Diffstat (limited to 'routes')
| -rw-r--r-- | routes/payments.ts | 33 |
1 files changed, 16 insertions, 17 deletions
diff --git a/routes/payments.ts b/routes/payments.ts index 78c0660b..5fc927b2 100644 --- a/routes/payments.ts +++ b/routes/payments.ts @@ -34,6 +34,19 @@ import rpc from "@coinos/rpc"; import got from "got"; import { v4 } from "uuid"; +// Funds are closed. The mechanism let a caller pull unbacked balance out of a +// fund (the /take drain, 2026-08-25) and its deposit path never had a +// kill-switch of its own, so with `fund:disabled` set users could still pay +// INTO funds they were then unable to withdraw from. Every value-moving entry +// point — deposit (POST /payments with `fund`), withdrawal (POST /take) and +// pre-authorization (POST /authorize) — now refuses unconditionally, rather +// than depending on a redis flag someone can clear by accident. +// +// Read paths are deliberately left working: GET /fund/:id and the manager +// endpoints still serve existing funds so balances stay visible and can be +// settled out of band. +const fundsClosed = () => fail("Funds are no longer supported"); + export default { async info(_, res) { res.send(await ln.getinfo()); @@ -86,16 +99,7 @@ export default { sender: user, }); } else if (fund) { - p = await debit({ - hash, - amount, - memo: fund, - user, - type: PaymentType.fund, - }); - await db.incrBy(`fund:${fund}`, amount); - await db.lPush(`fund:${fund}:payments`, p.id); - l("funded fund", fund); + fundsClosed(); } } @@ -322,11 +326,7 @@ export default { // }, async authorize(req, res) { - // Kill-switch for the fund/authorize/take mechanism (SECURITY 2026-08-25: - // the /take fund-claim path could pay out unbacked balance). Set redis - // `fund:disabled` to fail all - // fund authorizations closed while the mechanism is audited/rewritten. - if (await g("fund:disabled")) fail("Fund transfers temporarily disabled"); + fundsClosed(); const { id: uid } = req.user; const { id, fiat, currency, amount } = req.body; @@ -361,8 +361,7 @@ export default { user, } = req; try { - // Kill-switch — see authorize(). - if (await g("fund:disabled")) fail("Fund transfers temporarily disabled"); + fundsClosed(); amount = Number.parseInt(amount); if (!Number.isFinite(amount) || amount <= 0) fail("Invalid amount"); |
