Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Static Node Identity: Key Loading, Generation, and Persistence

File: kinetic-node/src/identity.rs Crate: kinetic-node | Stage: 12


What Is This?

Important

The infrastructure node must maintain the same PeerId across every restart. Other peers hardcode bootstrap node addresses as <multiaddr>/<PeerId> in their config. If the Peer ID changes, those hardcoded entries become unreachable.

identity.rs provides one function — load_or_generate_key(path) — that either loads an existing keypair from disk or generates a fresh one if the file is missing or corrupted.


How It Works

Load path (file exists and is valid)

#![allow(unused)]
fn main() {
std::fs::read(key_path)
Keypair::from_protobuf_encoding(&bytes)
}

-> See: kinetic-node/src/identity.rs — Lines 15–26

std::fs::read(key_path) reads the raw bytes. Keypair::from_protobuf_encoding(&bytes) decodes the Ed25519 keypair from its protobuf-encoded form. If decoding fails (corrupted file), it falls through to generating a new key.


Generate path (file missing or corrupted)

#![allow(unused)]
fn main() {
Keypair::generate_ed25519()
to_protobuf_encoding()
write_secret()
}

-> See: kinetic-node/src/identity.rs — Lines 27–36

Keypair::generate_ed25519() mints a fresh keypair. The keypair is re-encoded with to_protobuf_encoding() and written to disk via write_secret().


write_secret() — secure atomic write

#![allow(unused)]
fn main() {
0o600
std::fs::rename()
f.sync_all()
std::fs::write()
}

-> See: kinetic-node/src/identity.rs — Lines 39–58

On Unix, the file is written with three security properties:

  1. Mode 0o600 — readable only by the owner.

    Important

    The private key must never be world-readable.

  2. Atomic rename — written to a .tmp file first, then std::fs::rename() atomically moves it to the final path. Prevents a partially written key file surviving a crash.
  3. f.sync_all() — flushes the write to the OS disk buffer before rename, ensuring the data survives a power loss.

On non-Unix, a plain std::fs::write() is used (no file permission API is available cross-platform).


Why Ed25519?

Ed25519 keys produce Peer IDs that encode to base58 strings starting with 12D3Koo. libp2p uses Ed25519 as its primary key type. The keys are stored in protobuf format (the standard libp2p key serialization) so they are portable across versions.


Test Coverage

The test suite covers every meaningful failure path:

  • test_generate_new_key_if_missing: file doesn’t exist → generates, saves, reload matches.
  • test_load_existing_key: load twice → same Peer ID.
  • test_fallback_on_corrupted_key: garbage bytes in file → generates new key without panic.
  • test_fallback_on_unwritable_directory: path is inside a non-directory → generates in memory, doesn’t panic.
  • test_fallback_on_empty_file_overwrites: empty file treated like corruption → new key written.
  • test_keypair_is_ed25519: Peer ID starts with 12D3Koo (Ed25519 marker).
  • test_generate_unique_keys: two separate calls produce different Peer IDs.
  • Fuzz: doesnt_crash_on_corrupted_identity_files: proptest random bytes → never panics.

-> See: kinetic-node/src/identity.rs — Lines 60–218


Quick Reference

ScenarioResult
node.key exists, validLoads and returns keypair
node.key exists, corruptedGenerates new, saves, returns
node.key missingGenerates new, saves, returns
Cannot write to diskGenerates new in-memory, logs warning

Key format: Ed25519, protobuf-encoded bytes File permissions (Unix): 0o600 (owner read/write only) Write strategy: .tmpsync_all()rename()