Thread

🛡️
Nostr doesn’t need protocol changes for clients to stop treating the root key like a login credential. Cold Root Lineage gives users a safe, durable identity model with almost zero implementation cost.

Replies (16)

🛡️
FROST solves a different problem. It keeps a hot key safe by splitting it across participants. Cold Root solves the opposite problem. The root key never touches any online device at all. You create the key you actually use by doing it offline: - Use the cold root to deterministically derive a new epoch key. - Export only the epoch key and a signed lineage proof. - The root never leaves cold storage. The client only ever sees the hot epoch key. All continuity comes from the signature the root made offline.
You keep saying "no protocol changes required", but then propose a bunch of protocol changes (yes, new tags and client recommendations count). I'm also confused by your use of "deterministic", are you using HD keys or adding a link? the latter isn't deterministic, and HD keys don't really work for this (blinded addresses can't be linked, and compromise of an unblinded private key compromises the root). Are you familiar with NIP 26? That was basically this, but was never adopted except by Minds because of the complexity it imposed on clients. This approach also only works for people who understand cold storage and how to safely derive keys, which is ultimately going to be a very small minority. It's also helpless against key material loss, or an attacker getting the root key. A few weeks ago a group of people were able to meet in person and talk through this problem (from admittedly a different angle). We came up with this: This allows for making your root identity disposable by advertising a migration event based on a precommit that an attacker can't get access to. It's better I think, but of course there is really no silver bullet in this domain. Everything comes with huge trade offs.
🛡️
Appreciate the response. A few clarifications because I think we’re talking about different layers. 1. No protocol changes A lineage event is just a normal NIP 01 note with the current pubkey as the author. The root and sig tags are client semantics, not protocol extensions. The protocol doesn’t need to understand them. Relays don’t need to do anything. Clients can ignore them completely and nothing breaks. That’s what I mean by “no protocol changes required”. 2. Deterministic != HD chain. I’m not proposing BIP32 style unhardened chains or blinded address links. The derivation in the prototype uses HKDF(root_seed, “epoch:label”). No parent–child leakage. No public derivation path. No hardened/unhardened distinction. It’s just a reproducible function that stays entirely offline. The root never signs online. The root never publishes anything. Clients never derive keys. Only the user’s offline environment does. 3. This doesn’t try to solve the “lost root key” problem. Nothing does except backups or threshold schemes. The model is about containment, not availability. If the epoch key leaks, you lose that epoch. If the root leaks, you lose the lineage. Same as Bitcoin. Same as PGP. Different threat model than NIP-26. 4. NIP 26 solves a different class of problem. NIP 26 is about delegation. Giving another key permission to sign for you. It requires clients and relays to understand delegation. And, as you mentioned, it saw limited adoption because of complexity. Cold root identity isn’t delegation. It’s ancestry. Clients only need to verify: sig_root(epoch_pubkey) Everything else stays NIP-01. 5. Adoption burden on clients is intentionally tiny. A client only needs to add: - detect lineage tag - verify sig_root(epoch_pubkey) - follow the newer key If they don’t implement it, nothing breaks. If they do, users get survivable identity. It's completely optional for the clients. 6. Advanced users always adopt first. Just like PGP, Bitcoin cold wallets, miniscript, Lightning channels, etc. Power users adopt best practices early. Clients catch up as patterns stabilize. The purpose of this model is to demonstrate that survivable identity can exist without NIP changes, relay changes, or delegation semantics. Everything else can evolve around it. Happy to collaborate or cross compare with the key migration NIP. We’re all circling the same core problem with different threat models.
> A lineage event is just a normal NIP 01 note with the current pubkey as the author. The root and sig tags are client semantics, not protocol extensions. The protocol doesn’t need to understand them. Relays don’t need to do anything. Clients can ignore them completely and nothing breaks. This is a backwards-compatible protocol change (it introduces a new data format and semantics). Also, I would recommend using a specific event kind, rather than overloading an existing one with new semantics. > Cold root identity isn’t delegation. It’s ancestry. Clients only need to verify: sig_root(epoch_pubkey) I'm not sure I completely understand how the link works and what clients are expected to do with it. Say I pull a kind 0 signed by key A' with a link to A. Does A' re-publish a kind 0 (for example), or are clients expected to map the key and fetch A's kind 0? Or vice versa? Or, given a key A'', how do you fetch events published by sibling keys? Or are clients only expected to migrate follows etc when a new epoch happens?
🛡️
> This is a backwards-compatible protocol change (it introduces a new data format and semantics). Also, I would recommend using a specific event kind, rather than overloading an existing one with new semantics. I think we still differ on what counts as a protocol change. A NIP-01 event with extra tags is not a protocol change. Clients already ignore tags they don’t care about. Relays already store opaque JSON. Nothing here requires new relay behavior, new permissions, or any protocol level understanding of lineage. That’s all I mean by “no protocol change.” If a client wants to support rotation, it only needs to: - discover a lineage event for the pubkey it’s currently using - verify sig_root(epoch_pubkey) - switch its notion of “current key” to the new one That’s it. No republishing. No rewriting old events. No sibling traversal. No multi directional crawling. Events stay exactly where they were signed. This is not a delegation graph and not a multi hop merge. It’s a single step ancestry link. Given a key A, the client only needs to ask: “Is there a valid lineage event pointing to A’ that supersedes it?” If yes → follow A’. If no → stay on A. Old posts stay with old keys. New posts come from new keys. There is no expectation that a new epoch key republishes content from prior epochs. If a client doesn’t implement this, nothing breaks. If it does, identity becomes survivable. > I'm not sure I completely understand how the link works and what clients are expected to do with it. Say I pull a kind 0 signed by key A' with a link to A. Does A' re-publish a kind 0 (for example), or are clients expected to map the key and fetch A's kind 0? Or vice versa? Or, given a key A'', how do you fetch events published by sibling keys? Or are clients only expected to migrate follows etc when a new epoch happens? Epoch rotation is not about remapping past events or merging siblings. Clients don’t need to crawl anything or reconcile historical state. The model is intentionally simple: - old events stay under the key that signed them - new events come from the new epoch key - the lineage event tells the client which key to treat as “current” Nothing is republished. Nothing is rewritten. Nothing is fetched from siblings. A client doesn’t need to fetch events from A1, A2, A3 or merge timelines. It only needs to know which key is the active identity for new actions. If a client sees a valid lineage event saying A → A’, it just switches its posting identity and UI identity to A’. Historical fetch stays unchanged: - events(pubkey=A) returns old posts - events(pubkey=A') returns new posts This mirrors Bitcoin wallet key rotation and PGP subkey rotation. Identity moves forward, history stays with the key that signed it. No republishing. No follow migration by clients. No multi key merge logic. The only responsibility of the client is: - detect the lineage event - verify the root signature - follow the newer key going forward Everything else is intentionally untouched.
> Nothing is republished. Nothing is rewritten. Nothing is fetched from siblings. If all a client does is know that A is an ancestor of A' but doesn't do anything with that knowledge, I don't see what the point is. How are you expecting this to affect the UX (to be clear, for readers of a given user's content)?
🛡️
The point isn’t to rewrite history. It’s to give users a safe, forward moving identity. A client uses the lineage event to know which key represents the user now. Readers see the active key (A'), new posts come from A', and old posts stay where they were signed under A. Nothing needs to be merged or remapped for the UX to make sense. This is the same model used everywhere else: PGP subkeys, SSH key rotation, Bitcoin wallets. Identity advances, history remains immutable. Rotation becomes survivable, and the user keeps continuity without any protocol changes or heavy client logic. That’s the entire purpose.
🛡️
ok 1. Compromise recovery without losing your identity Alice’s phone is stolen. Her current epoch key (A1) is compromised. She goes to her offline root, derives A2 publishes a lineage event, and every client that supports rotation simply shows: “Alice is now using a new key.” Her followers don’t need to refollow and Alice doesn’t have to burn her whole account. Old posts stay signed by A1, new posts come from A2 Identity continuity is preserved. 2. Routine rotation without breaking the social graph Bob rotates keys yearly for hygiene. He derives A1 -> A2 -> A3 Clients that support rotation automatically follow A3 as Bob’s active identity. Readers see Bob’s profile, posts, and interactions exactly as before. They don’t need to understand the rotation. His timeline remains continuous because the client knows A1 -> A2 -> A3 belong to the same lineage. Nothing is merged. Nothing is rewritten. The UX change is simply that Bob can rotate keys safely without losing his audience or starting over. That’s the whole point: survivable identity without heavy protocol mechanics.
> “Alice is now using a new key.” > > Her followers don’t need to refollow and Alice doesn’t have to burn her whole account. Old posts stay signed by A1, new posts come from A2 Identity continuity is preserved. So A1 has published a kind 0, which clients have fetched. A2 proves it is descended from A, indicating the A1 -> A2 migration. A2 therefore inherits the name "Alice", which means linking is happening on the client side, correct? Who is the end user following in this scenario, A or A1?
🛡️
Clients follow the current key for an identity, not the historical ones. A1 was Alice’s active key and followers followed A1. When a valid lineage event shows A -> A2, the client simply updates the active pubkey for “Alice” to A2 going forward. No different from how clients already update profile metadata, relays, mutes, pins, etc. Users are following the identity, so the client treats A2 as the live key and uses it for new posts, profile info, and interactions. A1 remains only as the signer of old events. That’s the entire model.
> client treats A2 as the live key and uses it for new posts, profile info, and interactions So the next time Bob mentions Alice, they use A2 instead of A1? In order to do that, Bob has to 1. fetch A2's association with A, and 2. maintain the mapping somehow (most likely by updating the follow list). One problem that comes to mind: how does anyone know that A2 comes after A1? If A1 is compromised, it can always update the timestamp on its proof of ancestry to be more recent than the real user's.
🛡️
Yes, when Bob interacts with Alice after a rotation, he uses A2 because that is now Alice’s active key. Clients already maintain follow lists, so updating the entry from A1 -> A2 is just a metadata update on the client side, similar to when a user changes their relay list or update profile fields. As for ordering: a compromised epoch key cannot create a valid “I come after you” lineage event, because the lineage link is signed by the root, not by the epoch key. A1 can’t produce sig_root(A2) or any future link unless it has the offline root seed, so it can’t forge an ordering or jump ahead. Timestamps don’t matter. The cryptographic signature enforces the direction. That’s all clients need to know.
Got it, makes sense. I feel like we could have gotten there more quickly, saying things like "nothing is fetched from siblings" but also having to migrate from A1 -> A2 by fetching something from A1 is contradictory. But I understand now. Like I said at the top, I think cold storage is not the right UX for most users, but the migration scheme is certainly very simple because it doesn't compromise the authority of the root key.