aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/payments.ts25
1 files changed, 25 insertions, 0 deletions
diff --git a/lib/payments.ts b/lib/payments.ts
index 380b5e7b..01492975 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -1,6 +1,21 @@
import config from "$config";
+import { existsSync } from "fs";
import api from "$lib/api";
import { archive, db, g, ga, gf, s, sa } from "$lib/db";
+
+// External-withdrawal lockfiles. Out-of-band emergency stop: nobal.sh (or a
+// human) can `touch /home/adam/locks/<type>.locked` on the host, which is
+// bind-mounted into this container at /locks. Existence of /locks/<type>.locked
+// (or /locks/ALL.locked) blocks all external sends of that type. Lives outside
+// db so it can't be disabled by a db-write vector (May 18 /gateway lesson).
+//
+// Internal payments are never blocked by these — receives and internal
+// transfers between coinos users keep working with the site fully online.
+const isWithdrawLocked = (type: string): string | null => {
+ if (existsSync("/locks/ALL.locked")) return "ALL";
+ if (existsSync(`/locks/${type}.locked`)) return type;
+ return null;
+};
import { generate } from "$lib/invoices";
import ln from "$lib/ln";
import { err, l, warn } from "$lib/logging";
@@ -102,6 +117,16 @@ export const debit = async ({
(await g("hardfreeze")) ||
((await g("freeze")) && type !== PaymentType.internal);
+ // Out-of-band withdrawal lock (file-based, can't be defeated by db writes).
+ // Internal sends are exempt — they don't touch external wallets.
+ if (type !== PaymentType.internal) {
+ const lockedKind = isWithdrawLocked(type);
+ if (lockedKind) {
+ warn("Blocking", user.username, amount, hash, user.id, type, "withdraw-lock", lockedKind);
+ fail("External withdrawals temporarily disabled");
+ }
+ }
+
if (frozen || (amount > userLimit && !whitelisted)) {
warn("Blocking", user.username, amount, hash, user.id, type, frozen, userLimit);
fail("Problem sending payment");