<feed xmlns='http://www.w3.org/2005/Atom'>
<title>coinosv3.git/src/ark/proto.js, branch fdroid-v1.6</title>
<subtitle>coinos v3 — bitcoin, Ark, Lightning and nostr wallet (web + Android)</subtitle>
<id>https://git.coinos.io/coinosv3.git/atom?h=fdroid-v1.6</id>
<link rel='self' href='https://git.coinos.io/coinosv3.git/atom?h=fdroid-v1.6'/>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/'/>
<updated>2026-09-14T06:52:39Z</updated>
<entry>
<title>The renewal happens whether or not anyone opens the app.</title>
<updated>2026-09-14T06:52:39Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-14T06:52:39Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=4131e5ee24c4891caffdd8f4ca903ee973d104f7'/>
<id>urn:sha1:4131e5ee24c4891caffdd8f4ca903ee973d104f7</id>
<content type='text'>
Every coin expires, and until now the only thing standing between a
balance and its expiry was maybeAutoRefresh: it renews when the wallet is
opened inside the last 96 blocks, roughly sixteen hours. Bark's own
developers say the same premise fails on mobile — people don't reliably
open a wallet at the right hour, which is why every mobile Ark client
they know of has moved to delegated refreshes and why they are building
server-driven renewal.

captaind already has it, and ark.coinos.io has been carrying the column
unused: SubmitRoundParticipation takes a scheduled_height, and the server
holds that participation out of every round until the height arrives,
then runs it with nobody online — the round tree branch is signed by the
server, which is the whole point of a delegated refresh. The fee is
priced at the height it will run at, so a standing order lands in the
free bracket rather than today's.

So each spendable coin now gets one registered 36 blocks before its
expiry, six hourly rounds of slack. It is deliberately not an action:
the coin stays spendable, the wallet never reads as busy, and spending it
simply kills the order — the server drops a participation whose input is
gone, and _driveScheduled drops ours. The claim comes later, whenever the
wallet next opens: the mailbox breadcrumb we already post wakes it, and
the leaf cosign and forfeit are the ordinary refresh path from there.

Nothing about the input's expiry blocks that claim, which is what makes
this worth having. A wallet that goes quiet for a month comes back to a
coin already renewed and anchored in a fresh tree, instead of one the ASP
has swept and would have to honor out of goodwill.

One cycle per visit is all it can buy — the replacement's id doesn't
exist yet, so its own backstop waits for the next time the app is opened.

tools/ark/scheduled-refresh-test.js covers the five behaviours on
regtest: the server holds the order until its height, runs it while the
wallet is closed, the wallet claims on return, a spend voids the order,
and a claim still works after the input has EXPIRED (the last one mines a
coin's whole lifetime, so it also re-refreshes the funding wallet first —
an expired vtxo can be renewed but not sent).

Riding along: the expired-coin notice that was already sitting in the
working tree, and manager-test's tampered-vtxo check, which had been
reading base64 vtxo bytes as hex since they moved to IndexedDB.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_019fMdLypdg5JaU7bdeUFF8v
</content>
</entry>
<entry>
<title>Ark: persist vtxo bytes as base64 (a third smaller than hex); hex still read, hex still on the wire.</title>
<updated>2026-09-10T14:09:45Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-10T14:09:45Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=6113cf567fa0830233d5c3d524361f24a3f78caa'/>
<id>urn:sha1:6113cf567fa0830233d5c3d524361f24a3f78caa</id>
<content type='text'>
The live coins' signed bytes are most of a deep wallet's state — 560KB of
hex, which localStorage then holds as UTF-16. vtxoBytesToStr/FromStr in
proto.js persist base64 and read either form, _addVtxo writes base64, and
pruneArkState converts hex to base64 on every save, so an existing state
migrates itself the first time it saves. Measured on a live deep wallet:
904KB → 665KB (-26%), all 41 coins decoding identically.

The sync wire (slimArkForSync) keeps sending hex for now: a device still
on an older build would otherwise fail to decode every coin merged from a
newer one. Flip it once pre-2026-09-10 builds are gone.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Nw1Dijik1tLVKRM1WkU7xE
</content>
</entry>
<entry>
<title>A round's output can no longer strand: every refresh asks the server to drop its unlock hash in the mailbox, and any device that finds such a breadcrumb with no matching action adopts the claim itself (input ids now read from the status response — the rescue needs no operator). History learns to tell the whole truth and nothing more: coins the server saw spent elsewhere get a folded row (skipping coins a known renewal consumed), renewals fold across devices on their unlock hash and appear only when they cost something, and a rescue records the fee the round actually took. The account page keeps its header icons when reached from an open wallet; the messages page folds the control/guestbook replay once per burst instead of painting every intermediate state — no more blinking messages from superseded ban lists or member counts crawling to their total, which now paints from last session's settled figure</title>
<updated>2026-08-29T14:05:47Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-08-29T14:05:47Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=dd011c2c98033637829d83fc320ab4fdce4ede8c'/>
<id>urn:sha1:dd011c2c98033637829d83fc320ab4fdce4ede8c</id>
<content type='text'>
Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_019WboeoetMF3MTmgT7Ew8yi
</content>
</entry>
<entry>
<title>hal speaks pver 5: preimages prove their size, and fees round up as one</title>
<updated>2026-08-05T19:24:03Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-08-05T19:24:03Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=68fd614f8cb404904e1939ab711f7c7ba4350c32'/>
<id>urn:sha1:68fd614f8cb404904e1939ab711f7c7ba4350c32</id>
<content type='text'>
0.6.0 servers require the new hashlock clauses (OP_SIZE 32 guard) for
Lightning and rounds. New policies/transitions carry the v1 scripts and
type bytes; _v0 variants keep old vtxos reconstructing and exiting.
The retired third-party HTLC policy is dropped (upstream took 0x08).

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01M5kFodm9azZJVTQxnkDdKe
</content>
</entry>
<entry>
<title>Ark gifts go down to 1 sat; negative amounts can't OOM the encoder</title>
<updated>2026-08-04T15:31:21Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-08-04T15:31:21Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=9fd8887dcdd5e0fc1067a91032bfd18eb25f0202'/>
<id>urn:sha1:9fd8887dcdd5e0fc1067a91032bfd18eb25f0202</id>
<content type='text'>
Regtest-proven (tools/ark/dust-gift-test.js): the ASP cosigns all-dust
arkoor sends, so a sub-dust bearer vtxo sweeps to its claimer fine, and
dust folds into rounds alongside bigger siblings. The 330-sat gift
floor guarded a failure that can't happen — dropped to 1 sat.

The experiment also caught a live bug: refresh() of a balance smaller
than the round fee computed a negative output amount, and pbWriter's
varint sign-extends negatives into an infinite loop — auto-refresh
would OOM the tab of any wallet too small to renew. varint now throws
on negatives and refresh() refuses cleanly, before marking coins
pending, when balance minus fee is under the server's 330-sat output
minimum.

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_015QwAtBSpKvECqp1rHLHjJk
</content>
</entry>
<entry>
<title>Client support for third-party HTLC vtxos (trustless swap bridges)</title>
<updated>2026-07-31T19:05:59Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-31T19:05:59Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=142fa01322e624ac12dc24ee7af2583bda89e891'/>
<id>urn:sha1:142fa01322e624ac12dc24ee7af2583bda89e891</id>
<content type='text'>
Speaks the proposed VtxoPolicy::Htlc (type byte 0x08): policy encode/decode,
two-leaf taproot (claimer-with-preimage / refunder-after-expiry), preimage
field on the arkoor cosign request, and GetHtlcPreimage.

ArkManager gains the swap primitives: htlcLock (lock funds claimable only
against a payment hash, refundable by us after expiry), htlcClaim (reveal the
preimage), htlcCosignRefund (hand an unclaimable HTLC back — the server
constrains the outputs to the refunder, so this is safe to expose publicly)
and acceptHtlcRefund.

tools/ark/htlc-bridge-test.js drives a full submarine swap between two
independent wallets against a patched captaind, and asserts the ASP rejects
both a wrong-preimage claim and a preimage-less spend to the bridge itself.

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Add Ark &lt;-&gt; Lightning via the ASP (captaind-native submarine swaps)</title>
<updated>2026-07-31T07:49:21Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-31T07:49:21Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=78789e0b2eaf91e2de6b4e488324fbec0fcefb9d'/>
<id>urn:sha1:78789e0b2eaf91e2de6b4e488324fbec0fcefb9d</id>
<content type='text'>
The ASP is the HTLC counterparty (second.tech's protocol), so no external
swap provider is needed: paying a bolt11 converts pubkey vtxos into
ServerHtlcSend vtxos the server can only claim by revealing the invoice
preimage; receiving grants ServerHtlcRecv vtxos claimed by revealing ours.
Failure paths cosign the HTLC back (revocation) — proven end-to-end against
regtest captaind, which independently derives the HTLC taproots.

- ark/lightning.js: bolt11 decode, lightning fee schedules, all pay/receive
  gRPC-web wrappers (cosign, initiate, status, revocation, hold-invoice
  receive flow, anti-DoS attestation)
- ark/send.js: arkoor builder generalized to policy outputs (pubkey /
  serverHtlcSend / serverHtlcRecv), two-leaf taproot trees, HTLC clause
  scripts, reusable per-part cosign helpers for package RPCs
- ark/manager.js: ln-pay and ln-recv crash-safe action state machines
  (deterministic receive preimages from the seed, chain 5), mailbox
  lightning notifications, unpayable-invoice -&gt; instant revocation
- proto.js: lightning ArkInfo fields + mailbox lightning message decode
- validate/refresh: policy-aware vtxo validation and reconstruction
- features: bolt11 routes via ark when the balance covers it (Boltz stays
  the fallback), lightning-invoice receive on the Ark pane
- tools/ark/ln-test.js: harness proving HTLC cosign + revocation against
  captaind; manager-test.js updated for the consolidated bc-container chain

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Collaborative offboard: move the ark balance back on-chain</title>
<updated>2026-07-05T16:05:20Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-05T16:05:20Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=f9fc0bb3e38c3387f8af5d151aa03d83376ba9cc'/>
<id>urn:sha1:f9fc0bb3e38c3387f8af5d151aa03d83376ba9cc</id>
<content type='text'>
PrepareOffboard/FinishOffboard against captaind — per-input ownership
attestations, connector-based musig forfeits (with the deterministic
fanout tx when there is more than one input), and fee math that mirrors
the server bit-for-bit (weight fee rounds up, ppm-by-expiry rounds
down) since it rejects any other net amount. The receive pane gains a
'Move back on-chain' button that withdraws everything to the wallet's
own fresh address; the incoming tx is labeled 'Ark withdrawal' in
History and the ark side records the movement. Crash-safe: the txid
persists before FinishOffboard and resume asks GetVtxoStatus whether
the forfeits landed (the server broadcasts the tx itself).

Also fixes boarding to applySentTx after broadcasting the funding tx —
without it the spent coin lingered in the utxo set (a second board
double-spent it), and the post-board scan() could resurrect it by
racing the explorer's indexing.

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Ark: recover from stale vtxo state</title>
<updated>2026-07-05T02:29:08Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-05T02:29:08Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=1d9cac6ca92187718eafbd567562875d25fa772b'/>
<id>urn:sha1:1d9cac6ca92187718eafbd567562875d25fa772b</id>
<content type='text'>
Match captaind's actual 'not spendable' error so a crashed or foreign
spend fails the action instead of retrying forever, and reconcile
locally-spendable vtxos against the server (GetVtxoStatus + ownership
attestation) when an ark address lands in the send form. boardFee now
treats absent proto3 fee fields as zero (Second's signet boards free).

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Ark: real-time receives over the SubscribeMailbox gRPC stream</title>
<updated>2026-07-04T14:32:42Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-07-04T14:32:42Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=a28302eee33dab5f0829ff6310242beb0a744ef7'/>
<id>urn:sha1:a28302eee33dab5f0829ff6310242beb0a744ef7</id>
<content type='text'>
grpcStream() parses gRPC-web frames incrementally off fetch's body
stream; the manager keeps a long-lived SubscribeMailbox subscription
(auto-reconnect, 3s backoff) whose messages flow through the same
duplicate-safe processing as the poll. Receives now land the moment the
sender delivers (~sub-second) instead of on the next poll; the poll
stays as fallback and to drive in-flight boards/refreshes.

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
</content>
</entry>
</feed>
