<feed xmlns='http://www.w3.org/2005/Atom'>
<title>coinos-server.git, branch regtest-fulcrum</title>
<subtitle>coinos server — legacy coinos.io backend (Lightning, on-chain, Liquid)</subtitle>
<id>https://git.coinos.io/coinos-server.git/atom?h=regtest-fulcrum</id>
<link rel='self' href='https://git.coinos.io/coinos-server.git/atom?h=regtest-fulcrum'/>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/'/>
<updated>2026-06-27T18:00:08Z</updated>
<entry>
<title>regtest: add Fulcrum (Electrum/WS) with ZMQ push for Hal realtime</title>
<updated>2026-06-27T18:00:08Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-27T18:00:08Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=e0a4a5e9d332f998456b637f5d8ba38563d19827'/>
<id>urn:sha1:e0a4a5e9d332f998456b637f5d8ba38563d19827</id>
<content type='text'>
Adds a regtest Fulcrum service matching what prod runs, replacing the old
electrs + ews bridge as the data/realtime backend for the Hal browser wallet.
It connects to `bc` and gets instant block/mempool notifications over bitcoind's
ZMQ (hashblock/hashtx), serving them to the browser over an Electrum WebSocket
(:50003) — sub-second deposit + confirmation updates on regtest.

- docker/fulcrum.Dockerfile: Fulcrum on ghcr.io/coinos/base (shares layers with
  the rest of the stack instead of a separate image tree).
- compose.fulcrum.yml: opt-in overlay (`-f compose.fulcrum.yml up -d`), config +
  datadir under the gitignored data/ (seeded from sampledata by setup.sh).
- sampledata/fulcrum/fulcrum.conf: regtest config pointed at `bc`, ZMQ enabled.
- sampledata/bitcoin/bitcoin.conf: add zmqpubhashblock/hashtx (18508/18509) so
  Fulcrum can subscribe for instant notifications.

Co-Authored-By: Claude Opus 4.8 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>scripts: add dedup-invoices cleanup for already-duplicated :invoices</title>
<updated>2026-06-18T14:41:21Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-18T14:41:21Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=45a988b3554f44abd03a2d89f1b9087c993186c1'/>
<id>urn:sha1:45a988b3554f44abd03a2d89f1b9087c993186c1</id>
<content type='text'>
The source fix (LPOS guard in generate, 2d2f9856) stops NEW duplicate
entries but doesn't remove the ones already accumulated. This one-time
script scrubs them: scans every &lt;id&gt;:invoices list and removes duplicate
ids, keeping each id's original creation-order position. Dry-run by
default; pass --apply to write. Idempotent.

Scope on a recent prod snapshot: 2,785 of 8,735 lists had dupes
(16,363 duplicate entries).

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>invoices: don't re-list an invoice id on update (dedupe :invoices)</title>
<updated>2026-06-18T14:37:04Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-18T14:37:04Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=2d2f9856b31ac98665f008a00550efa224761f9a'/>
<id>urn:sha1:2d2f9856b31ac98665f008a00550efa224761f9a</id>
<content type='text'>
generate() unconditionally lPush'd the invoice id to `${aid}:invoices`,
but it's also called from the update() handler (tip/webhook changes, and
repeated bolt12 offer creation) re-using the existing id — so each update
re-appended the same id and the user's :invoices accumulated duplicate
entries, rendering the same offer/invoice multiple times (kaliyi's OCEAN
offer showed 2x, another 6x). Only push when the id isn't already in the
list (LPOS guard).

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>lnurl: SSRF-guard user-supplied URL fetches + validate zap events</title>
<updated>2026-06-16T06:35:50Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-16T06:35:50Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=497277ef7b48c86aa2f44b613e0a35a0c3b0c238'/>
<id>urn:sha1:497277ef7b48c86aa2f44b613e0a35a0c3b0c238</id>
<content type='text'>
The /lnurl/proxy, /decode and /encode handlers fetched an arbitrary
user-supplied URL via got() and returned the body — an unauthenticated
SSRF reaching internal services. Route those fetches through a new
safeGot() that rejects hosts resolving to loopback/private/link-local/
CGNAT/metadata ranges, re-validates on each redirect hop, restricts the
scheme to http(s), and caps timeout + response size. Also validate the
NIP-57 zap request (kind 9734 + signature) before storing it, instead of
trusting the client-supplied event.

These endpoints can't be removed — they back lightning-address/LNURL
parsing and resolution in the wallet UI.

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Push broadcast txs directly to public explorers</title>
<updated>2026-06-15T20:01:41Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-15T20:01:41Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=35df18174ab61c6d4a72bf8f1157584adb14bc4f'/>
<id>urn:sha1:35df18174ab61c6d4a72bf8f1157584adb14bc4f</id>
<content type='text'>
After broadcasting an on-chain send via bitcoind, also POST the raw tx to
mempool.space and blockstream (fire-and-forget, non-blocking) so those
explorers see it immediately instead of waiting ~8s for p2p propagation.
Skipped on regtest/testnet.

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>setup override warning</title>
<updated>2026-06-14T15:50:05Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-04T07:09:18Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=525556b45d042de6bf565a9bc153290243ac6404'/>
<id>urn:sha1:525556b45d042de6bf565a9bc153290243ac6404</id>
<content type='text'>
</content>
</entry>
<entry>
<title>scripts: add wiped-history audit and rebalance-economics model</title>
<updated>2026-06-13T21:21:53Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-13T21:21:53Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=03f68bedddb18e92b487cc6c1358225ce9b40fe6'/>
<id>urn:sha1:03f68bedddb18e92b487cc6c1358225ce9b40fe6</id>
<content type='text'>
audit-wiped-history.ts (read-only) finds accounts whose :payments index
was wiped by the changeid bug — balance&gt;0 with empty history in both db
and arc. rebalance-economics.ts models whether the lightning-&gt;liquid
bridge throughput is fee-positive against on-chain rebalance cost.

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>users: add authenticated self-delete endpoint</title>
<updated>2026-06-13T21:21:49Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-13T21:21:49Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=2548f7578ab879bc7eb292462b13f41e7f3112e5'/>
<id>urn:sha1:2548f7578ab879bc7eb292462b13f41e7f3112e5</id>
<content type='text'>
POST /delete (auth) wipes every key tied to the calling user, mirroring
scripts/delall-legacy.ts: user records, payments/invoices, sub-accounts,
NWC apps, nostr follow graph + nip5. Gated by typing the username and
refuses while balance+pending &gt; 10k sats so funds can't be destroyed by
accident.

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>payments: guard /replay double-credit race with SET NX idempotency</title>
<updated>2026-06-13T21:21:44Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-13T21:21:44Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=ca28c19fe26d3f81ae62d2d3f0dc8b175849a9a8'/>
<id>urn:sha1:ca28c19fe26d3f81ae62d2d3f0dc8b175849a9a8</id>
<content type='text'>
getPayment() pre-check in the lightning listener/replay isn't atomic, so
concurrent re-credits of the same settlement each ran incrBy. Claim each
lightning/bolt12 settlement with an atomic SET NX on credited:&lt;ref&gt;
(ref = preimage) and bail if already claimed. Also disable the /replay
route that triggered it.

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>payments: freeze matches uid as well as username (rename-proof)</title>
<updated>2026-06-13T21:21:39Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-06-13T21:21:39Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinos-server.git/commit/?id=43427afe5b0b16d887163bd788360ac29105d69c'/>
<id>urn:sha1:43427afe5b0b16d887163bd788360ac29105d69c</id>
<content type='text'>
The blacklist (send freeze) only matched the current username, so a
compromised account could shake the freeze by renaming itself. Match on
either the lowercased username OR the immutable uid; add the uid to the
`blacklist` set to freeze durably.

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