Part of PR #3. Pairing flow still to come.
Devices screen — app/(app)/devices.tsx:
* Lists every active device from /api/devices/{self}.
* "THIS DEVICE" badge on our own row, Unlink button on every other.
* Unlink confirms + submits UNLINK_DEVICE tx, optimistic local removal.
* Pull-to-refresh; empty state when balance is too low for auto-link.
* Placeholder row for "Link new device" — wired in next commit.
Settings → Devices entry row: added under a new "Devices" section.
Self-wipe on revoke — lib/storage.ts + app/(app)/_layout.tsx:
* New AsyncStorage marker `dchain_device_registered` tracks whether
this install ever made it into the on-chain registry.
* wipeAllLocalState() zeroes secure-store key + contacts + settings +
chats cache + marker. Safe-idempotent.
* Bootstrap effect in app layout splits three branches by
(our_pub in chain's active list × marker_set):
- in list → mark registered, done.
- not in list + was registered → REVOKED → wipe + redirect to auth.
- not in list + never registered → first boot, LINK_DEVICE.
* Network errors never trigger wipe — only an explicit "pub missing
from chain response" decides it. Belt-and-suspenders against a
misbehaving node spuriously dropping records.
Next: pairing flow so a second device (desktop, tablet, new phone)
can come online, show a 6-digit code, receive master priv via a
one-shot relay envelope encrypted to its fresh device X25519 pub,
then self-link.