After the keyset-id fix, a Minibits paid download still failed and the
buyer lost the sats. What happened, 2026-09-29, amishparadise:
1. The seller redeemed the token, then failed to read the file. It was a
FileBrowser upload owned by the container subuid (100999) with mode
0640. The handler mapped that Err to 404.
2. The buyer's FIPS dial treats 404 as "fall back to Tor" and resent the
request with the same, now spent, token. The seller answered 402, and
the buyer showed "seller doesn't accept your Cashu mint".
Fixes:
- serve_content checks the file is readable before the paid gate. If it
isn't, it grants read with `podman unshare chmod a+r`, which matches
the other shared files. If that also fails it returns Unavailable (503)
without taking payment.
- The content handler returns 500 on internal errors and logs them,
instead of a silent 404.
- New PeerRequest::single_delivery(), used for the paid download: the
FIPS answer is final, FIPS retries only when it never connected, and
there's no Tor replay once the request may have been delivered.
- The buyer shows the seller's error text for non-402 failures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paid cloud downloads paid with Minibits ecash were always rejected. The
buyer sends a cashuB token, which carries NUT-02 v2 keyset ids in their
8-byte short form. Minibits rotated its active keyset to a v2 id, and the
seller's verify_and_receive_payment called MintClient::swap directly,
skipping the short->full id repair that only receive_token applied. The
mint answered 422 ("ID length invalid"). The buyer then showed the
misleading "seller doesn't accept your Cashu mint" hint.
Move the repair into swap() so every caller is covered: payment verify,
streaming gate, send change, and cross-mint swaps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>