claim_and_redeem retried every redeem failure indefinitely, including a
terminal one: mint error 11001 "Token Already Spent" (a claim replayed by a
relay-watermark edge case, or already redeemed by an earlier run). On
archy-x250-pa3 this pinned pending_claims at 1 forever and hammered
mint.minibits.cash's swap endpoint every ~6s, with the UI permanently
showing "a payment arrived but couldn't be redeemed yet".
- mint_client: expose the NUT error-code-11001 message as
ALREADY_REDEEMED_MSG so callers can recognize it without duplicating the
string.
- minibits: drop (not retry) a redeem failure that matches
is_already_redeemed — the value was already swept, so retrying can never
succeed.
- fetch_relay_dms: query the primary relay.minibits.cash alone first,
falling back to the public relay.damus.io/nos.lol only if it's
unreachable, and page past a 200-DM backlog instead of silently
stranding older DMs behind an un-advanced watermark.
This fix already existed on feat/minibits-lnurl-receive (4e410d7, 489995c,
2026-09-09) but that branch was never merged into main, which has its own
independently-diverged minibits.rs — so the bug shipped again in
1.8.16-alpha. Ported directly onto main's current implementation this time.
Immediate unblock on archy-x250-pa3: cleared the one poisoned
pending_claims entry from wallet/minibits.json by hand (already-redeemed,
zero value at risk) and restarted archipelago.service; confirmed via
journalctl that polling is quiet again.
See docs/incident-2026-09-15-minibits-already-redeemed.md for the full
writeup.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
450 existed as two independent Rust constants (RPC gates vs boot
reconciler) linked only by a "keep in lockstep" comment — updating one
would reopen the disk-fill hole. Move it to crate::constants as the
single source of truth both paths import.
Also raise apps/cuprate/manifest.yml storage dependency and disk_limit
from 300Gi to 450Gi so manifest-driven surfaces (store size, pre-checks)
show the number the gate actually enforces — a user provisioning to the
displayed 300 was refused at an unexplained 450. Catalog regenerated
(cuprate entry re-embedded; still unsigned pending sign-catalog.sh).