Phase 2 of docs/npub-sign-in.md: the library half is lightning-kmp-app
01962f3 (claude/nostr-credentials), bumped in here; this is the app half.
nostr-keys.dat becomes nostr-credentials.dat, one typed entry per public
key -- a secret, or only the public key -- so that a profile signed in to
read-only and the same profile with its nsec pasted later are one entry in
one file. StoredIdentity gains NostrPublic, whose id is the one its secret
would have (hash160 of the x-only key), and merge reads the typed map. It
keeps the one precedence it needs: a public entry for a key a seed already
derives lists the wallet only, which is the state the seed writer's two-file
upgrade can leave behind if it dies between its writes. A secret for a
seed's key is still listed twice, as before -- that is a duplicate the
writers refuse, not a state the listing hides.
IdentityWriter: writeNostrPublicKey beside writeNostrKey, both refusing a
duplicate by public key against the seeds and the credentials, with the one
exception that is the point of the file -- the nsec of a key held read-only
is not a duplicate, since the device does not have that secret. writeNostrKey
replaces the public entry with a secret one in a single write, under the id
it already had, so the read-only identity's preferences are the ones the
signing identity keeps. writeMnemonic has to span two files for the same
upgrade and removes the credential first, then writes the seed: a crash
between the two loses the read-only identity, which the npub pasted again
restores, rather than listing one npub twice under two ids. forgetNostrKey
becomes forgetNostrCredential and removes an entry of either kind;
NotABareKey becomes NotACredential and still means a mnemonic.
The startup screen's third branch is here rather than in Phase 3 because
StoredIdentity is sealed and the compiler asked for it: a read-only
identity is active the moment it is read, as the nsec one is.
NostrSecretViewModel reads the secret entry and answers a public one with
"no key for this identity", which the screen it belongs to will never show
once Phase 5 hides the row.
Tests: the writer's upgrade in both directions -- the nsec of a read-only
key accepted under the same id, the npub of a secret refused -- and forget
of either kind; merge listing all three kinds, the shared id of a public
key and its secret, and the seed-over-public precedence.
Replayed onto Mantra by docs/curated-to-mantra.md: gitlink -> 84cc44c: upstream pinned 01962f3, a branch commit since rebased onto the library's master as 84cc44c with an identical tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pulled-From: curated/curated@5efeae769f