<feed xmlns='http://www.w3.org/2005/Atom'>
<title>coinos-server.git/lib/invoices.ts, branch master</title>
<subtitle>coinos server — legacy coinos.io backend (Lightning, on-chain, Liquid)</subtitle>
<id>https://git.coinos.io/coinos-server.git/atom?h=master</id>
<link rel='self' href='https://git.coinos.io/coinos-server.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/'/>
<updated>2026-10-09T06:16:37Z</updated>
<entry>
<title>bolt12: put the requested expiry and single-use on the offer itself</title>
<updated>2026-10-09T06:16:37Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-09T06:16:37Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=0cbfab4779545afcc621d019ec93b30e5aec99df'/>
<id>urn:sha1:0cbfab4779545afcc621d019ec93b30e5aec99df</id>
<content type='text'>
The expiry was only stored on our invoice record, so CLN kept issuing
invoices for the offer after it had expired. Pass it as absolute_expiry,
and accept single_use to cap an offer at one payment.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>invoices: a supplied account must belong to the invoice's user</title>
<updated>2026-10-06T22:53:18Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-06T22:53:18Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=7727b97c71c11b4b65f47bcc68460ea981c1b22c'/>
<id>urn:sha1:7727b97c71c11b4b65f47bcc68460ea981c1b22c</id>
<content type='text'>
generate() took any existing account id as the credit target, so an
invoice could carry one user's name while paying into someone else's
account. A supplied aid now has to be in the invoice user's own account
list; otherwise the invoice credits that user's main account.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01KCxLg8s1g8yBwjRwPN4uJR
</content>
</entry>
<entry>
<title>invoices: don't let a supplied id overwrite another user's invoice</title>
<updated>2026-09-29T15:37:47Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-29T15:37:39Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=e96ad1cea4192a608a7f15896e4b690b57bf8fc9'/>
<id>urn:sha1:e96ad1cea4192a608a7f15896e4b690b57bf8fc9</id>
<content type='text'>
generate() writes invoice:&lt;id&gt; wholesale with the caller's uid, and the old
bolt11 pointer keeps resolving to that id, so passing someone else's invoice
id re-pointed their invoice at the caller's account. Invoice ids aren't
secret (LUD-21 verify URLs, invoice pages, receipts), so refuse an existing
id that belongs to a different user, both in generate() and in POST /invoice
for anonymous pay-page callers. Both fail with "invoice not found" so ids
can't be probed for existence.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>security: drop the admin endpoint surface and land pending hardening</title>
<updated>2026-09-14T03:44:48Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-14T03:44:48Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=d8a361bdcfaa8cc5b39d9675e74b0e1d2e42be2c'/>
<id>urn:sha1:d8a361bdcfaa8cc5b39d9675e74b0e1d2e42be2c</id>
<content type='text'>
Removes the admin-gated endpoints and the account flags they set, plus a
batch of hardening that had accumulated uncommitted in the tree. These were
deployed and verified together, so they land as one change.

admin surface:
  - drop POST /hidepay, POST /unlimit and GET /users, their handlers, and
    the now-unused admin preValidation export
  - `unlimited` was only ever written, never read; `hidepay` only appeared
    in output projections. Both flags go, along with their whitelist,
    public-fields and type entries.

request log:
  - redactBody now recurses. It only masked top-level keys, so a body that
    nests credentials one level down wrote them to the log in plaintext,
    and pin fields were not on the list at all.

payments:
  - sendOnchain/build gate `aid` through requireAccount, so a caller can
    only spend from an account it owns
  - sendOnchain signs only a tx we composed, matched against a short-lived
    per-account build receipt
  - raise xpay retry_for to 60s; route-finding preferred a drained zero-fee
    hop and burned the whole budget on it. check()'s reversal window keeps
    its 2x margin.

ecash / invoices / webhooks:
  - resolve a token's mint through the IP-pinning fetcher instead of plain
    fetch, closing an unauthenticated SSRF path into the internal network
  - reject non-positive amounts before the balance write
  - /sign requires the caller to own the address, rather than signing under
    any address the hot wallet controls
  - webhook delivery no longer disables TLS verification; the body carries
    the merchant's shared secret

registration:
  - refuse a username the v3 registrar holds unless the caller's own nostr
    key is the holder (lib/names.ts)

liquid:
  - honour the deposit kill switch at invoice generation and at credit

elements image:
  - pin 23.3.4 and verify the release tarball checksum

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_0112TGi9GGNWqji1XQ5jZRzc
</content>
</entry>
<entry>
<title>mint/lightning: refuse caller-supplied invoices, tiered fees, log hygiene</title>
<updated>2026-07-30T17:38:43Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-30T17:28:23Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=fd1617ea66e01557e71debc7e45ca08cfe01bf70'/>
<id>urn:sha1:fd1617ea66e01557e71debc7e45ca08cfe01bf70</id>
<content type='text'>
- invoices: reject caller-supplied bolt11/bolt12; the server always mints its
  own. Closes the invoice-registration path behind the shared-node credit and
  payment-hijack vectors.
- lightning: skip mint-owned invoices (lbl* label) in the paid-invoice listener.
- payments: lightning free-tier fee credit, withheld for flagged high-throughput
  (ln:nofree) accounts which pay an elevated rate; the failed-send reversal
  restores credit at the same rate. freezeCheck made crash-resilient. Lightning
  and bolt12 sends stay pending until the preimage settles.
- payments (route): validate payment-request / invoice input types.
- logging: collapse repeated xpay failure lines into one.

Co-Authored-By: Claude Opus 4.8 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>bolt12 zaps: pay offers, NWC pay/receive, per-user offers, NIP-44</title>
<updated>2026-07-30T15:51:36Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-30T15:51:36Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=add3ad93ff37f1daaedab5788f00663be299555c'/>
<id>urn:sha1:add3ad93ff37f1daaedab5788f00663be299555c</id>
<content type='text'>
- sendLightning fetches an invoice from lno1 offers (payer_note passthrough
  for NIP-177 zap intents) and pays it through the existing bolt12 path
- NWC: new nwc#2 pay/receive methods (BIP-321 URIs, payer proofs stubbed
  until CLN 26.06 createproof), nip44_v2 alongside nip04 with per-request
  scheme detection, dual 23196/23197 notifications, 13194 refreshed when
  its content or encryption tag goes stale
- standing per-user bolt12 offer via GET /offer; incoming bolt12 payments
  record invreq_payer_note as memo
- parse/fetchinvoice routes handle offers; zap display parses kind 9736
  receipts (amount tag + embedded intent) and drops a call to a sync()
  helper that never existed

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>preimages: support fiat prices, converted at invoice-creation time</title>
<updated>2026-07-15T05:06:25Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-15T05:06:25Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=81c3593f1eec7f94dc9cbc22edc3456fa57b95cb'/>
<id>urn:sha1:81c3593f1eec7f94dc9cbc22edc3456fa57b95cb</id>
<content type='text'>
POST /preimages now accepts fiat + optional currency (defaults to the
merchant's) as an alternative to a sat price. The fiat amount is stored
per entry and converted to sats at the moment a usePreimage invoice is
created, so the price tracks the exchange rate instead of freezing at
upload time. The invoice also takes on the entry's currency and rate so
receipts display the fiat price exactly as the merchant set it. If no
rate is available for the entry's currency, the preimage is pushed back
onto the queue and creation fails rather than issuing an any-amount
invoice.

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>preimages: enforce a merchant-set price per preimage batch</title>
<updated>2026-07-14T03:22:06Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-14T03:21:49Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=0d55c0ccceb378a9c1992f6e7d82a6786cdd19b8'/>
<id>urn:sha1:0d55c0ccceb378a9c1992f6e7d82a6786cdd19b8</id>
<content type='text'>
Invoice creation is unauthenticated, so buyers could name their own
price for a queued preimage (create the usePreimage invoice with
amount: 1). POST /preimages now takes an optional price (sats) that is
stored per entry and overrides the requested amount when the preimage
is consumed; omitting it keeps the old any-amount behavior. Also gate
the stored-preimage reveal in credit() on the invoice being fully paid,
since internal sends can settle partial amounts. Bare-hex entries
queued before this change still parse (treated as priceless).

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Merge remote-tracking branch 'origin/master'</title>
<updated>2026-07-11T16:17:05Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-11T16:17:05Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=2e90d709178ac718e4f32229951e6ea38dfcc371'/>
<id>urn:sha1:2e90d709178ac718e4f32229951e6ea38dfcc371</id>
<content type='text'>
# Conflicts:
#	lib/payments.ts
</content>
</entry>
<entry>
<title>invoices: reject sub-300-sat on-chain invoice amounts</title>
<updated>2026-07-11T05:56:12Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-11T05:56:12Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=7d8cd769e6cf91dda70fdf06db72a0b03e263dd7'/>
<id>urn:sha1:7d8cd769e6cf91dda70fdf06db72a0b03e263dd7</id>
<content type='text'>
On-chain deposits below 300 sats are dropped by the dust filter in the
confirm/credit path, so a specific-amount bitcoin/liquid invoice under
that threshold can never be credited. Reject creating one at generation
time and point the user at lightning. Any-amount (0) addresses are
unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
