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.
But you're not answering my question. If "nothing needs to be merged or remapped for the UX to make sense," in what way does the UX change? Give me a user story or two.
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.