d157d78707eeada06acc75b0cb39e7cd567a62bc
Replace the per-launch SecretKey::generate() in the core loop with a stable key loaded from ~/.config/peerspeak/identity.key, so a peer's node id now survives restarts. This is the foundation of the friends-first contacts model (docs/contacts-plan.md): friends are keyed by node id and reachability rests on a saved address per friend, both of which only mean anything if the id is stable. iroh never forced rolling ids — the old generate() was an unrevisited default. New src/identity.rs: load_or_create / regenerate / save over a 0600 hex key file (atomic tempfile+rename, perms set before rename), hand-written hex (no new dep). A malformed file is a hard error, not a silent regenerate, so a bad hand-edit can't orphan everyone who saved the old id. The fs logic is behind a path-injectable seam (load_or_create_at/save_at) tested in a temp dir: create+persist, malformed-errors, regenerate-changes-key, 0600 perms, plus hex round-trips. Core falls back to an ephemeral key only if the file can't be read/created, so a bad disk never blocks a call. regenerate() exists for the Settings 'Regenerate identity' control (next slice; needs live endpoint rebuild). 238 lib tests green (+8), clippy clean, release builds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>