Zeus Diagnostics and Tooling
Zeus is a React Native (RN) Bitcoin/Lightning wallet supporting 7 node backends. This skill is the measurement toolbox: where every log lives, how to read it, and runnable scripts that extract ground truth from the code instead of from stale docs.
When to use / When NOT to use
Use this skill when you need to:
- Find or tail node logs (embedded LND, LDK Node) on device, emulator, or in-app.
- Watch native-layer behavior via
adb logcat/ Xcode console. - Inspect what the app sends over the network (REST, WebSocket, Tor).
- Query the live capability matrix, settings defaults, or debt markers.
- Interpret a log: is this node healthy, syncing, or wedged?
Use a sibling skill instead for:
- Symptom → root-cause triage flowcharts: zeus-debugging-playbook (this skill tells you how to observe; that one tells you what the observation means for known failure classes).
- What
yarn verifyruns and how to add tests: zeus-validation-and-qa. - Backend quirks and the add-an-RPC recipe: zeus-backends-and-capabilities (this skill only ships the script that prints the matrix).
- Keychain/storage semantics and migration safety: zeus-storage-and-migrations (the Keychain Recovery tool is described here only as a diagnostic; never change storage behavior without that skill's gated process).
- Building the dev environment / running the app / connecting Polar nodes: zeus-build-and-env and zeus-run-and-operate.
- Formal race/dispatch analysis techniques: zeus-proof-and-analysis-toolkit.
Vocabulary (defined once)
| Term | Meaning here |
|---|---|
| Backend / implementation | One of 7 node-connection strategies selected per wallet: embedded-lnd, ldk-node, lnd (remote REST), lightning-node-connect (LNC), cln-rest, lndhub, nostr-wallet-connect (NWC). Dispatch lives in utils/BackendUtils.ts. |
| Embedded LND | A full LND Lightning node compiled to a mobile library (gomobile) running inside the app process tree. |
| LDK Node | An alternative embedded node built on the Lightning Dev Kit, integrated via uniffi (Rust→Kotlin/Swift FFI bindings). |
| Neutrino | LND's light-client chain backend (BIP-157/158 compact block filters); embedded LND uses it instead of a full bitcoind. |
| Metro | React Native's JS bundler/dev server (yarn start). In dev, all JS console.* output appears in the Metro terminal. |
| logcat | Android's system log stream, read with adb logcat. Native (Java/Kotlin) Log.d/i/e calls land here, filtered by TAG. |
| uniffi bindings | Machine-generated Kotlin/Swift glue code for Rust libraries — huge files you must exclude from greps. |
| Tor | Anonymity network; Zeus routes REST calls through it per-node via react-native-nitro-tor (0.6.0 as of 2026-07-06). |
1) Log access per node type
Summary table
| Node type | Node's own log | In-app viewer | Off-device access |
|---|---|---|---|
| embedded-lnd | lnd.log (paths below), debuglevel=info hardcoded |
Settings → Embedded node → "LND Logs" (views/Settings/EmbeddedNode/LNDLogs.tsx, tail 100 + live follow, Copy button) |
adb shell run-as (debug builds) / simulator container |
| ldk-node | ldk_node.log in the node dir, LogLevel.DEBUG hardcoded |
Settings → Embedded node → "LDK Node Logs" (views/Settings/EmbeddedNode/LDKLogs.tsx) |
same |
| lnd / cln-rest / lndhub (remote) | on the remote server, not the phone | — (use Developer Tools for RPC probes) | server-side (e.g. Polar, below) |
| lightning-node-connect | on the litd server | — | server-side |
| nostr-wallet-connect | remote wallet service | — | relay/service side |
| App itself (JS layer) | console.* |
— | Metro terminal (dev), ReactNativeJS logcat tag (Android), Xcode console (iOS) |
Embedded LND log file paths (verified in native code)
Android (android/app/src/main/java/com/zeus/LndMobileTools.java, tailLog/observeLndLogFile):
- Legacy first wallet (
lndDir == 'lnd'):<filesDir>/logs/bitcoin/<network>/lnd.log - Every other wallet (uuid dir):
<filesDir>/<lndDir>/logs/bitcoin/<network>/lnd.log
where <filesDir> = /data/user/0/app.zeusln.zeus/files and <network> is
mainnet or testnet. Note the asymmetry: the legacy 'lnd' wallet lives at
the files-dir root, not in a lnd/ subfolder.
iOS (ios/LndMobile/LndMobileTools.swift, tailLog): always
<Application Support>/<lndDir>/logs/bitcoin/<network>/lnd.log — the lndDir
subfolder is used even for 'lnd' (unlike Android).
lndDir per wallet: stores/SettingsStore.ts sets this.lndDir = node.lndDir || 'lnd'; new wallets get uuidv4() dirs
(views/Settings/WalletConfiguration.tsx). To map uuid → wallet, use Tools →
"Export/Import Wallet Configurations".
Verbosity: writeLndConfig in utils/LndMobileUtils.ts hardcodes
debuglevel=info in the generated lnd.conf. For a local debugging session you
may bump it to debug there (dev-only; never commit).
Trap — readLndLog is a stub. lndmobile/index.ts readLndLog returns
[''] with a TODO remove comment. Do not build tooling on it; the real
mechanisms are NativeModules.LndMobileTools.tailLog(numberOfLines, lndDir, network) and observeLndLogFile(lndDir, network) which emits lndlog
device events (used by both the LNDLogs view and SyncStore rescan tracking).
Pulling the file off a device (debug builds only — run-as requires
android:debuggable):
# legacy 'lnd' wallet on mainnet:
adb shell "run-as app.zeusln.zeus tail -n 200 files/logs/bitcoin/mainnet/lnd.log"
# list wallet dirs to find uuid dirs:
adb shell "run-as app.zeusln.zeus ls files/"
iOS simulator:
APP=$(xcrun simctl get_app_container booted com.zeusln.zeus data)
find "$APP/Library/Application Support" -name lnd.log
(com.zeusln.zeus = iOS bundle id; Android applicationId is
app.zeusln.zeus — they differ.)
LDK Node logs
- Path:
<DocumentDirectoryPath>/ldk-node/<ldkNodeDir-uuid>/ldk_node.log. The storage dir comes fromutils/LdkNodeUtils.tsgetLdkNodeStoragePath(nodeDir);ldkNodeDiris auuidv4()per wallet. On AndroidDocumentDirectoryPath=/data/user/0/app.zeusln.zeus/files; on iOS it is the app sandboxDocuments/folder. - Log level:
LogLevel.DEBUG/.debug, hardcoded atbuilder.setFilesystemLogger(...)inandroid/app/src/main/java/app/zeusln/zeus/LdkNodeModule.ktandios/LdkNodeMobile/LdkNodeModule.swift. Not user-configurable. - In-app:
views/Settings/EmbeddedNode/LDKLogs.tsxcallsNativeModules.LdkNodeModule.tailLdkNodeLog(100)and subscribes toldklogevents fromobserveLdkNodeLogFile()(backed byLogFileObserver.kt/LogFileObserver.swift). - The native module ALSO logs its own lifecycle to logcat under tag
LdkNodeModule(e.g.[timing] FFI buildWithDualStore completed in Nms) — often more useful than ldk_node.log for start/stop races.
adb logcat — verified filter tags
All tags below appear in the checked-in native sources
(git grep -n 'TAG = "' android/app/src/main/java to re-verify — with one
exception: LdkNodeModule has no TAG constant and uses inline
Log.d("LdkNodeModule", ...) literals; re-verify that one with
git grep -n '"LdkNodeModule"' android/app/src/main/java/app/zeusln/zeus/LdkNodeModule.kt):
| Tag | Source | What you see |
|---|---|---|
ReactNativeJS |
RN runtime (standard) | all JS console.* on device |
LndMobileService |
com/zeus/LndMobileService.java |
embedded LND process/IPC lifecycle |
LndMobile |
com/zeus/LndMobile.java |
RN↔LND bridge module |
LndMobileTools |
com/zeus/LndMobileTools.java |
log observation, chain tools |
LndScheduledSyncWorker |
com/zeus/LndMobileScheduledSyncWorker.java |
background sync worker |
LndMobileScheduledSync |
com/zeus/LndMobileScheduledSync.java |
sync scheduling |
LdkNodeModule |
app/zeusln/zeus/LdkNodeModule.kt |
LDK Node build/start/stop, VSS, timing |
LdkNodeService |
app/zeusln/zeus/LdkNodeService.kt |
LDK foreground service |
NostrConnectService / NostrConnectModule |
com/zeus/NostrConnect*.java |
NWC background service |
CashuDevKitModule |
com/zeus/cashudevkit/CashuDevKitModule.kt |
Cashu FFI |
MainActivity |
com/zeus/MainActivity.kt |
activity lifecycle, intents |
MobileTools |
com/zeus/MobileTools.java |
misc native tools |
StealthMode |
app/zeusln/zeus/StealthMode.java |
icon-disguise switching |
Copy-paste starters:
# Embedded LND session:
adb logcat -s LndMobileService:* LndMobile:* LndMobileTools:* ReactNativeJS:*
# LDK Node session (start/stop/delete races):
adb logcat -s LdkNodeModule:* LdkNodeService:* ReactNativeJS:*
# NWC background service:
adb logcat -s NostrConnectService:* NostrConnectModule:* ReactNativeJS:*
iOS console & Metro
- Xcode: run the
zeusscheme (ios/zeus.xcworkspace) — the debug console shows both nativeNSLog/os_logand JS console output. - Console.app: filter by process
zeuson a connected device (release-ish builds where Xcode isn't attached). Standard macOS tooling. - Metro:
yarn start(script =react-native start, verified in package.json). All dev-mode JS logs appear there. Pressjin the Metro terminal to open React Native DevTools (network + console + profiler) — standard RN ≥0.76 tooling, not Zeus-specific. - JS-side embedded-LND logging goes through
lndmobile/log.ts: a tiny wrapper that prefixesconsole.*output with a tag, e.g. lines startingutils/LndMobileUtils.ts: ...(seeconst log = Log('utils/LndMobileUtils.ts')). Grep Metro/logcat output for that prefix during embedded-LND triage.
2) In-app diagnostics
Developer Tools view
- File:
views/Tools/DeveloperTools.tsx; routeDeveloperTools; reached via Menu → Tools ("Developers" row). - Gating: shown when
BackendUtils.supportsDevTools()is true, which isisLNDBased() || call('supportsDevTools')(utils/BackendUtils.ts). Measured availability (2026-07-06):- Available:
lnd,embedded-lnd,lightning-node-connect(isLNDBased = true), pluscln-restandlndhub(declaresupportsDevTools = () => true). - Not available:
ldk-node,nostr-wallet-connect. - There is NO developer-mode setting; gating is purely capability-based.
- Available:
- Contents: 18 raw RPC probes in 4 accordions (General, Lightning, On-chain,
Channels):
getMyNodeInfo,getNetworkInfo,getLightningBalance,getInvoices,getPayments,getBlockchainBalance,getTransactions,getUTXOs,getNewAddress,listAccounts,listAddresses,getChannels,getPendingChannels,getClosedChannels,getChannelInfo,getFees,getForwardingHistory,abandonChannel. Each command carries acompatibleImplementationsallowlist, so what you see varies per backend. Responses render as copyable JSON — this is the fastest way to see exactly what a backend returns without adding temporary logging. - Danger rail:
abandonChannel(drops a channel without an on-chain close; can burn funds) requires an "I know what I am doing" switch AND a native confirm dialog. Do not script around it.
Keychain Recovery tool
- File:
views/Tools/NodeConfigExportImport.tsx("Export/Import Wallet Configurations" in Tools), backed byutils/KeychainRecoveryUtils.ts. - The "Recover Lost Configurations" flow scans four sources for orphaned
data:
current,unprefixed-local,unprefixed-cloud(old iCloud-synchronizable keychain entries), andencrypted-storage(legacyzeus-settingsblob). It exists because pre-migration keychain copies are deliberately never deleted — see zeus-storage-and-migrations for the whole saga and the rules before touching any of this. - As a diagnostic, a scan tells you which storage generation a user's data is in — run it (read-only until the user restores) before hypothesizing about "lost wallets".
- Same view's Export flow is also the sanctioned way to inspect a device's
wallet configuration set (per-wallet
lndDir/ldkNodeDiruuids included).
Activity / store inspection
- Tools → "Activity export" (
views/Tools/ActivityExport.tsx) dumps invoices/payments/transactions to CSV — use it to diff what the app thinks happened against node truth (Developer ToolsgetPaymentsetc.). - Sync observability is a store, not a screen:
stores/SyncStore.tspollsgetMyNodeInfoevery 2s while syncing, comparesblock_heightagainst a mempool.space tip (refetched every ~30s), and flipsisSyncingoff onsynced_to_chain. Rescans are tracked by parsing lnd.log lines matching/Rescanned through block.*\(height (\d+)\)/via thelndlogevent stream. - MobX stores are module singletons (
stores/Stores.ts); in a dev build you can inspect them from the React Native DevTools console via imports in scope of any breakpoint. No Reactotron/Flipper integration is configured (verified: no such deps in package.json).
3) Network debugging
How requests flow (verified in backends/LND.ts + utils/TorUtils.ts)
view → BackendUtils.<method> → call(funcName) → active backend class
REST (clearnet): ReactNativeBlobUtil.config({ trusty: !certVerification }).fetch(...)
REST (Tor): doTorRequest(...) [react-native-nitro-tor]
Streaming: wsReq(...) → new WebSocket(wss://host:port/route?method=...)
Embedded backends: no network at this layer (gomobile IPC / uniffi FFI)
Facts you need before reaching for a proxy:
- TLS verification is OFF by default for remote REST.
restReq'scertVerificationparameter defaults tofalse, and blob-util is called withtrusty: !certVerification— i.e. invalid/self-signed certs are accepted unless the user enabled "Certificate Verification" on the node config. Consequence: a MITM proxy (mitmproxy, Charles, Proxyman) can intercept remote-node REST traffic without installing its CA cert, as long as certVerification is off and you route the emulator/device through the proxy (Android emulator:-http-proxy; iOS simulator honors macOS system proxy). There is no cert pinning anywhere in the REST path. - WebSocket streams bypass Tor entirely.
wsReqand the othernew WebSocket(...)call sites inbackends/LND.tsbuildwss://URLs straight from host/port — even when the node has Tor enabled. When debugging "subscription works but payments leak clearnet" (or vice versa), remember these are two different transports. - Tor path cannot be trivially proxied. Tor requests go through the
embedded nitro-tor daemon (
utils/TorUtils.tsdoTorRequest), not the system proxy. TLS bypass on this path is scoped to HTTPS.onionURLs only (isOnionHttpsUrl); clearnet-over-Tor keeps strict validation anddoTorRequesttreats HTTP ≥ 300 as errors. To observe Tor traffic, instrument at the far end (your node) instead. - The request-dedup cache can replay failures.
backends/LND.tskeeps a module-levelcallsMap keyed by url+body. On the Tor branch the entry is only deleted on success, so a rejected promise is returned again for identical retries untilclearCachedCalls()runs — which happens on reconnect viaSettingsStore.setConnectingStatus(true). If "the same error keeps coming back instantly with no network activity", suspect this cache, not the network. (backends/CLNRest.tshas the same structure.) - Cheap first probe: Developer Tools →
getMyNodeInfoexercises the whole REST stack with one tap; React Native DevTools' Network panel shows fetch-based traffic in dev (blob-util requests may not appear there — fall back to the proxy or server-side logs).
Server-side (Polar) inspection
Polar (https://github.com/jamaljsr/polar) is the recommended local Lightning network for dev (CONTRIBUTING.md). For connecting Zeus to it, see zeus-run-and-operate. Diagnostics side (external tooling — verify against your Polar version):
- Each Polar node is a Docker container; view logs in the Polar UI (node →
Logs) or
docker logs -f <container>(containers are named per network/node, e.g.polar-n1-alice;docker pslists them). - lnd-side truth beats app-side guesses: watch the lnd container log while reproducing a payment to see whether the failure is client (Zeus) or node.
- LNC (
lightning-node-connect) requires a litd (Lightning Terminal) instance to pair against; its session/mailbox logs live on that server. (litd-in-Polar support depends on Polar version — unverified here.)
4) Grep hygiene
Generated and vendored files will bury your signal: the two uniffi bindings
alone are ~31k lines, proto/lightning.js is ~133k lines, and zeus_modules/
is vendored third-party code with its own node_modules. Canonical grep:
git grep -nIw -E 'YOUR_PATTERN' -- . \
':(exclude)zeus_modules' \
':(exclude)proto/lightning.js' \
':(exclude)proto/lightning.d.ts' \
':(exclude)ios/LdkNodeMobile/LDKNode.swift' \
':(exclude)android/app/src/main/java/org/lightningdevkit/ldknode/ldk_node.kt' \
':(exclude)android/app/src/main/java/uniffi/zeus_cashu_restore/zeus_cashu_restore.kt' \
':(exclude)ios/CashuDevKit/CashuDevKit.swift' \
':(exclude)ios/CashuDevKit/zeus_cashu_restore.swift'
Rules of thumb:
- Prefer
git grepovergrep -r:node_modules/,ios/Pods/, andandroid/app/build/are gitignored, so git grep skips them for free (verified withgit check-ignore). Plaingrep -rand somergsetups will wade straight into them. - Use
-w, not\b: this git's ERE engine (macOS) does not support\band fails silently with zero matches — a nasty way to conclude "no hits".-wgives whole-word matching portably. - Known false positive:
utils/AddressUtils.test.tscontainsXXXinside a base64 PSBT fixture (line ~923).-wfilters it; substring searches won't. fetch-libraries.shdefines a shell function literally namedjq()(a python3 wrapper) — a misleading hit when grepping for jq usage.- Config blind spots: tsconfig excludes
node_modules,zeus_modules,android,ios; ESLint additionally ignoresproto/; Prettier ignores ONLYzeus_modules/(per.prettierignore— so regeneratedproto/*.d.tscan still fail the Prettier CI check). Code in excluded dirs is neither linted nor type-checked, so absence of CI complaints proves nothing about it.
5) Scripts
All live in .claude/skills/zeus-diagnostics-and-tooling/scripts/, are
executable, safe (read-only), and were run against master c5fd094fb.
Run them from anywhere inside the repo; they cd to the git root themselves.
find-debt.sh — debt-marker census
.claude/skills/zeus-diagnostics-and-tooling/scripts/find-debt.sh # full listing
.claude/skills/zeus-diagnostics-and-tooling/scripts/find-debt.sh --summary # per-file counts
.claude/skills/zeus-diagnostics-and-tooling/scripts/find-debt.sh TODO # single marker
Sample output (2026-07-06, --summary, top of list):
5 android/app/src/main/java/com/zeus/LndMobile.java
4 lndmobile/wallet.ts
3 proto/lightning.proto
3 lndmobile/index.ts
3 android/app/src/main/java/com/zeus/LndMobileService.java
2 views/Wallet/Wallet.tsx
2 views/Activity/Activity.tsx
...
---
total markers: 63
Sample detail lines:
android/app/src/main/java/com/zeus/LndMobile.java:71:// TODO break this class up
android/app/src/main/java/com/zeus/LndMobile.java:350: // TODO unbind LndMobileService?
Interpretation: the debt concentrates in the Blixt-derived embedded-LND
native layer (com/zeus/*.java, lndmobile/*.ts) — expected; see
zeus-failure-archaeology for which of those TODOs have bitten before.
Standalone HACK/XXX markers: zero in hand-written code (as of the date
above).
capability-matrix.sh — live supports* matrix
.claude/skills/zeus-diagnostics-and-tooling/scripts/capability-matrix.sh # all flags
.claude/skills/zeus-diagnostics-and-tooling/scripts/capability-matrix.sh supportsOffers # filter (regex)
Cell legend: T/F literal, expr computed (usually LND version gates like
this.supports('v0.13.0')), ^T/^F/^expr inherited from LND.ts
(EmbeddedLND and LndHub extend LND — inherited T is the classic silent-bug
pattern), - not declared anywhere → BackendUtils.call() returns literal
false. Composite flags computed in utils/BackendUtils.ts
(supportsDevTools, supportsLightningAddress,
supportsForwardingHistoryChannelFilter) are listed in the script header,
not the matrix.
Sample output (2026-07-06, excerpt; 56 flags total):
FLAG LND eLND LNC LDK CLN Hub NWC
isLNDBased T T T F F F F
supportsCashuWallet F T F T F F F
supportsChannelFundMax T T T T T ^T F
supportsDevTools - - - - T T -
supportsOffers F F F T T F F
supportsTaproot expr expr expr T T F F
supportsWatchtowerClient T T T F - F -
Hand-verified against source (3 flags): supportsCashuWallet T only in
backends/EmbeddedLND.ts and backends/LdkNode.ts; supportsOffers T only
in backends/LdkNode.ts and backends/CLNRest.ts; supportsChannelFundMax
absent from backends/LndHub.ts hence inherited ^T from backends/LND.ts
(the known LndHub override gap).
list-settings-defaults.sh — defaults drift review
.claude/skills/zeus-diagnostics-and-tooling/scripts/list-settings-defaults.sh # whole block
.claude/skills/zeus-diagnostics-and-tooling/scripts/list-settings-defaults.sh 'Fee' # filtered
Prints the inline @observable settings: Settings = { ... } defaults object
of stores/SettingsStore.ts with real line numbers (located by brace
counting, not hardcoded lines — 147 lines spanning 1467–1613 as of
2026-07-06). Sample:
1467 @observable settings: Settings = {
1468 privacy: {
1469 defaultBlockExplorer: 'mempool.space',
...
1612 selectNodeOnStartup: false
1613 };
Why it matters: these literals are what FRESH installs get; existing users keep old values unless a migration runs. Diff this output between two commits to catch silent default drift. The catalog of every axis lives in zeus-config-and-flags; the migration recipe in zeus-storage-and-migrations.
6) Interpretation guides — healthy vs unhealthy
Embedded LND startup (the state machine Zeus itself watches)
utils/LndMobileUtils.ts subscribes to LND's state stream and drives on
lnrpc.WalletState: NON_EXISTING → LOCKED → UNLOCKED → RPC_ACTIVE → SERVER_ACTIVE. Readiness is declared at RPC_ACTIVE/SERVER_ACTIVE (after an
RPC-ready check); the hard ceiling is LND_READY_TIMEOUT_MS = 60000 (60s),
after which the slow-start path triggers.
Healthy session, in order:
- logcat
LndMobileService: process/service start, no restarts. - State log line
Current LND state: ...advancing through the machine (JS side, tag prefixutils/LndMobileUtils.ts:in Metro/ReactNativeJS). lnd.log: wallet opened/unlocked, neutrino peers connected, sync progress advancing. (Exact lnd.log wording is upstream-LND behavior and varies by version — treat specific phrasings as unverified here; the one pattern Zeus's own code depends on isRescanned through block ... (height N)instores/SyncStore.ts.)- App: Wallet screen leaves "connecting", balances populate.
Unhealthy signatures (all from utils/LndMobileErrors.ts, the canonical
error-code → raw-pattern map — read it before inventing new matching):
| Symptom / matched pattern | Code | Meaning |
|---|---|---|
wallet locked |
WALLET_LOCKED |
normal right after start; app should auto-unlock — persistent means unlock failed |
already started, already running |
LND_ALREADY_RUNNING |
informational; a stop didn't complete before a start (lifecycle race territory — see zeus-node-lifecycle-campaign) |
starting up, not yet ready |
RPC_NOT_READY |
transient; only a problem if still there near the 60s ceiling |
| stuck LOCKED→nothing, then timeout | RPC_READY_TIMEOUT / LND_READY_TIMEOUT |
RPC never came up; check lnd.log for the real cause |
no such file or directory etc. |
LND_FOLDER_MISSING |
wallet dir gone (post-delete or dir-mapping bug) |
unable to read TLS cert, connection refused during stop |
STOP_LND_EXPECTED |
expected shutdown noise — NOT an error |
Neutrino sync
stores/SyncStore.ts semantics: numBlocksUntilSynced = bestBlockHeight − currentBlockHeight where best comes from mempool.space and current from
getMyNodeInfo().block_height (polled every 2s).
- Healthy:
currentBlockHeightstrictly increasing between polls; gap shrinking; ends withsynced_to_chain: true. - Unhealthy: current height frozen across many polls with peers configured →
neutrino peer trouble (check
[Neutrino]section peers in generated lnd.conf,neutrino.connectvsaddpeeris controlled by thedontAllowOtherPeerssetting); best height 0/erroring → the mempool.space fetch failed and the store falls back to current height, which can mask remaining sync distance. - Recovery (seed restore) is separate:
getRecoveryInfopolled at 2s; healthy =progressmonotonically rising torecovery_finished.
LDK Node startup
Healthy (logcat, tag LdkNodeModule): applyBuilderSettings: Enabling filesystem logger → Building node with dual store (VSS + local): <url> →
[timing] FFI buildWithDualStore completed in <N>ms → started/running.
Unhealthy: [timing] FFI buildWithDualStore failed in <N>ms: <msg>;
JS-layer sentinels Node not initialized (native ref still rebuilding —
transient by design, retried) and LDK Node not running yet (both defined
in utils/LdkNodeUtils.ts). Repeated build attempts or a stop that never
returns → lifecycle race; gather the logcat trace FIRST, then switch to
zeus-node-lifecycle-campaign.
Remote backends (lnd REST / cln-rest / lndhub)
- Healthy: Developer Tools
getMyNodeInforeturns JSON quickly; Wallet populates. - Error-shaped success trap: LND REST
payLightningInvoicetimeouts resolve with{ payment_error: localeString('views.SendingLightning.paymentTimedOut') }(a locale-dependent "Payment timed out…" message) — a "successful" response that is actually a failure. Don't classify by promise resolution, and don't match the string literally. - Instantly repeating identical error with no traffic → stale dedup-cache rejection (section 3, item 4).
- Method returns literal
false(and.thencrashes with "false is not a function"-style errors) → the active backend simply lacks that method; check the capability matrix, not the network.
Provenance and maintenance
Facts verified 2026-07-06 against master c5fd094fb (v13.1.3-alpha) by
reading the cited files and running every command/script in this skill
(scripts run to completion with the sample outputs shown; adb/simulator/
proxy commands are standard platform tooling verified by source read of the
paths they target, not executed against a device).
One-line re-verification commands for volatile facts:
| Fact | Re-verify with |
|---|---|
readLndLog still a stub |
git grep -n -A3 'readLndLog = async' lndmobile/index.ts |
| Android lnd.log paths | git grep -n 'logs/bitcoin' android/app/src/main/java/com/zeus/LndMobileTools.java |
| iOS lnd.log path | git grep -n 'lnd.log' ios/LndMobile/LndMobileTools.swift |
lnd debuglevel=info |
git grep -n 'debuglevel' utils/LndMobileUtils.ts |
| LDK log path + DEBUG level | git grep -n 'setFilesystemLogger' android/app/src/main/java/app/zeusln/zeus/LdkNodeModule.kt ios/LdkNodeMobile/LdkNodeModule.swift |
| logcat TAG list | git grep -n 'TAG = "' android/app/src/main/java (exception: LdkNodeModule uses inline literals — git grep -n '"LdkNodeModule"' android/app/src/main/java/app/zeusln/zeus/LdkNodeModule.kt) |
| DevTools gating rule | git grep -n -A2 'supportsDevTools' utils/BackendUtils.ts backends/ |
| DevTools command list | git grep -n "name: '" views/Tools/DeveloperTools.tsx |
trusty: TLS default |
git grep -n 'trusty' backends/LND.ts |
| WS-bypasses-Tor | git grep -n 'new WebSocket' backends/LND.ts |
| Tor onion-only bypass | git grep -n 'isOnionHttpsUrl' utils/TorUtils.ts backends/LND.ts |
| nitro-tor version | git grep -n 'react-native-nitro-tor' package.json |
| dedup-cache clear site | git grep -n 'clearCachedCalls' stores/SettingsStore.ts backends/ |
| WalletState machine + 60s timeout | git grep -n 'LND_READY_TIMEOUT_MS|WalletState\.' utils/LndMobileUtils.ts |
| Rescan log regex | git grep -n 'Rescanned through block' stores/SyncStore.ts |
| defaults block location | .claude/skills/zeus-diagnostics-and-tooling/scripts/list-settings-defaults.sh | head -2 |
| capability matrix | rerun capability-matrix.sh and spot-check any 3 flags against backends/*.ts |
| app/bundle ids | git grep -n 'applicationId' android/app/build.gradle; git grep -n 'PRODUCT_BUNDLE_IDENTIFIER = com' ios/zeus.xcodeproj/project.pbxproj |
If any script errors with "pattern changed", the source file moved under it — fix the script in the same change and re-paste fresh sample output here.