<feed xmlns='http://www.w3.org/2005/Atom'>
<title>coinos-server.git/lib/ecash.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-09-22T02:23:57Z</updated>
<entry>
<title>ecash: identify our own tokens by keyset, not by the mint url</title>
<updated>2026-09-22T02:23:57Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-20T04:53:30Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=a2216320de0d6b4eb25cc699456d5d995f267590'/>
<id>urn:sha1:a2216320de0d6b4eb25cc699456d5d995f267590</id>
<content type='text'>
The mint url in a cashu token is a label the sending wallet writes in, and
tokens of ours turn up carrying stray ports and paths. Fetching it to compare
pubkeys therefore rejected perfectly good tokens, and meant every /cash entry
point made an outbound request to a user-supplied address.

Keyset ids are derived from the mint's public keys, so a token whose proofs
are all on our keysets is ours whatever url it names; a foreign one still
fails, and now with a clear message instead of cashu-ts's short-id mapping
error. A miss refreshes the cached keyset list once, in case of a rotation.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>ecash: redeem tokens by melting to the user's own invoice</title>
<updated>2026-09-16T11:53:22Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-16T11:53:22Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=c72fa2a5ccf1cc04073f9ff12606491971dadd36'/>
<id>urn:sha1:c72fa2a5ccf1cc04073f9ff12606491971dadd36</id>
<content type='text'>
Redeeming ecash is now a plain NUT-05 melt into a Lightning invoice owned
by the redeeming user, credited by the regular lightning listener. Drop
the house proof pool and its lock, the /mint and /melt routes, and the
special-casing of a mint account in NWC and payment routing. Tokens are
decoded against the mint's keyset list so short v1 ids resolve, and an
empty keyset list is never cached.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>ecash: make claims work against the v1 keyset mint</title>
<updated>2026-09-14T15:50:31Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-14T15:50:31Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=cdda2d34d26bd8fb310867022130e4a59f67d13c'/>
<id>urn:sha1:cdda2d34d26bd8fb310867022130e4a59f67d13c</id>
<content type='text'>
cashu-ts 2.9.0 only derives v0 keyset ids, so CashuWallet rejected the
mint's current keyset and receive() could never run. Build the wallet
from keys preloaded off our own mint instead of verifying, decode tokens
with the keysets (2.9.0 shortens v1 ids in the tokens it encodes, and
won't expand them otherwise), and treat an unreadable house-pool value
as empty rather than throwing — the pool key had been found holding a
bare integer, which surfaced as "n.startsWith is not a function" on
every redeem. The pool write is awaited so a dropped set can't strand
freshly swapped proofs.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_0166Ci2XdCair1B9ych7w8dq
</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>ecash: serialize the shared cash-key read-modify-write (S13)</title>
<updated>2026-08-08T06:29:27Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-08-08T06:00:33Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=bfba77d22a85ffb021a8a0299326c3833c2cafaf'/>
<id>urn:sha1:bfba77d22a85ffb021a8a0299326c3833c2cafaf</id>
<content type='text'>
The house Cashu holdings live as one token under redis `cash`. claim()
and mint() decoded the current proofs, swapped at the mint, then wrote
back — with no lock. Two concurrent claims read the same proofs and the
later write clobbered the earlier, so already-swapped proofs vanished
from the token while both users stayed credited (an insolvency leak,
triggerable via the unauthenticated /ecash/:id callback). Wrap both
read-swap-write sequences in an in-process mutex.

Co-Authored-By: Claude Opus 4.8 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>remove logs</title>
<updated>2026-03-17T06:53:13Z</updated>
<author>
<name>Adam Soltys</name>
<email>asoltys@gmail.com</email>
</author>
<published>2026-03-17T06:53:13Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=92e7d1564deb25f9e58e3b268c077018c4b2c4c4'/>
<id>urn:sha1:92e7d1564deb25f9e58e3b268c077018c4b2c4c4</id>
<content type='text'>
</content>
</entry>
<entry>
<title>remove total user balances</title>
<updated>2025-03-28T18:02:47Z</updated>
<author>
<name>Adam Soltys</name>
<email>asoltys@gmail.com</email>
</author>
<published>2025-03-28T18:02:38Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=425e712177de6f2d47e7d0486a347f0cbf4e0d0d'/>
<id>urn:sha1:425e712177de6f2d47e7d0486a347f0cbf4e0d0d</id>
<content type='text'>
</content>
</entry>
<entry>
<title>picture not profile</title>
<updated>2025-01-01T00:52:14Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2025-01-01T00:52:14Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=e2db9a0e0fad4cb107e1bb4fee32b0618f6bae26'/>
<id>urn:sha1:e2db9a0e0fad4cb107e1bb4fee32b0618f6bae26</id>
<content type='text'>
</content>
</entry>
<entry>
<title>api</title>
<updated>2024-11-23T06:59:29Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2024-11-23T06:59:29Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=20504aa76fc607f2682834253e2f9e4dcf15be0e'/>
<id>urn:sha1:20504aa76fc607f2682834253e2f9e4dcf15be0e</id>
<content type='text'>
</content>
</entry>
<entry>
<title>rm tags</title>
<updated>2024-11-23T06:49:36Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2024-11-23T06:49:36Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=29f77e795fe2b42fb508f0b1c0c91079eb0a4620'/>
<id>urn:sha1:29f77e795fe2b42fb508f0b1c0c91079eb0a4620</id>
<content type='text'>
</content>
</entry>
</feed>
