Summary
After a cosigner-runtime restart, FCM vtxo_received push notifications are silently dropped until at least one client makes an authenticated call that populates the shared ASP-info cache (e.g. GetArkInfo on connect). The incoming VTXO is still discovered via the indexer path (balance updates), but the push that drives background auto-delegate / auto-settle is never sent.
Root cause
vtxo_stream::process_notification reads the shared ASP-info cache directly and bails on a miss:
cosigner-runtime/src/vtxo_stream.rs:60-72
let guard = asp.lock().await;
match guard.info.as_ref() {
Some(i) => i.clone(),
None => {
tracing::warn!("ASP info not cached, skipping VTXO stream notification");
return;
}
}
The cache (AspClient.info behind shared.asp_client) is only populated lazily by fetch_asp_info (cosigner-runtime/src/cosigner/handlers/ark.rs:34), which runs from authenticated handlers such as get_ark_info. On a fresh restart with no connected clients, the cache stays empty, so every stream notification is skipped → no pushes.
Note the inconsistency: fetch_asp_info handles a cache miss by calling get_info(), but process_notification does not — it just skips.
Evidence (regtest)
Server log shortly after a restart:
60.13s INFO VTXO stream: connected to arkd, listening for events
1190.11s WARN ASP info not cached, skipping VTXO stream notification
1191.93s WARN ASP info not cached, skipping VTXO stream notification
The 1190s warning (≈19.8 min after the ~start) coincided exactly with an incoming off-chain receive. No push was sent and the device's background auto-delegate never fired. Opening the app once (which calls GetArkInfo → populates the cache) immediately restored pushes for subsequent receives.
Impact
- Push-on-receive — and therefore background auto-delegate and the auto-settle that depends on a stored delegate — is dead from server start until some client calls
GetArkInfo.
- Usually masked in production (clients connect frequently), but a user whose app is fully closed across a cosigner restart would not be woken for incoming VTXOs until another
GetArkInfo lands. That's exactly the "auto-delegate works in the lab but not in production" failure mode.
Proposed fix (either, ideally both)
- Pre-warm on startup — call
get_info() once when the VTXO stream connects to arkd (cosigner-runtime/src/main.rs:176 → vtxo_stream::run_vtxo_stream), before processing notifications.
- Fetch-on-miss in the stream — make
process_notification fetch-and-cache on a miss (mirror fetch_asp_info's get_info() fallback) instead of skipping. Robust against cache eviction too.
Repro
- Start
cosigner-runtime fresh (ASP configured), with no clients connected.
- Send an off-chain VTXO to a user that has a registered FCM token.
- Observe
ASP info not cached, skipping VTXO stream notification and no push.
- Connect a client (any
GetArkInfo call), repeat the send → push now fires.
Files
cosigner-runtime/src/vtxo_stream.rs (process_notification)
cosigner-runtime/src/cosigner/handlers/ark.rs:34 (fetch_asp_info)
cosigner-runtime/src/main.rs:176 (vtxo stream task start)
Summary
After a
cosigner-runtimerestart, FCMvtxo_receivedpush notifications are silently dropped until at least one client makes an authenticated call that populates the shared ASP-info cache (e.g.GetArkInfoon connect). The incoming VTXO is still discovered via the indexer path (balance updates), but the push that drives background auto-delegate / auto-settle is never sent.Root cause
vtxo_stream::process_notificationreads the shared ASP-info cache directly and bails on a miss:cosigner-runtime/src/vtxo_stream.rs:60-72The cache (
AspClient.infobehindshared.asp_client) is only populated lazily byfetch_asp_info(cosigner-runtime/src/cosigner/handlers/ark.rs:34), which runs from authenticated handlers such asget_ark_info. On a fresh restart with no connected clients, the cache stays empty, so every stream notification is skipped → no pushes.Note the inconsistency:
fetch_asp_infohandles a cache miss by callingget_info(), butprocess_notificationdoes not — it just skips.Evidence (regtest)
Server log shortly after a restart:
The
1190swarning (≈19.8 min after the ~start) coincided exactly with an incoming off-chain receive. No push was sent and the device's background auto-delegate never fired. Opening the app once (which callsGetArkInfo→ populates the cache) immediately restored pushes for subsequent receives.Impact
GetArkInfo.GetArkInfolands. That's exactly the "auto-delegate works in the lab but not in production" failure mode.Proposed fix (either, ideally both)
get_info()once when the VTXO stream connects to arkd (cosigner-runtime/src/main.rs:176→vtxo_stream::run_vtxo_stream), before processing notifications.process_notificationfetch-and-cache on a miss (mirrorfetch_asp_info'sget_info()fallback) instead of skipping. Robust against cache eviction too.Repro
cosigner-runtimefresh (ASP configured), with no clients connected.ASP info not cached, skipping VTXO stream notificationand no push.GetArkInfocall), repeat the send → push now fires.Files
cosigner-runtime/src/vtxo_stream.rs(process_notification)cosigner-runtime/src/cosigner/handlers/ark.rs:34(fetch_asp_info)cosigner-runtime/src/main.rs:176(vtxo stream task start)