Operator hit this in the wild: keys/node.json mounted into a container
as 600 root:root while the node process runs as an unprivileged user.
os.ReadFile returned a permission error, loadOrCreateIdentity fell
through to "generate a new identity", and genesis allocation (21M
tokens) was credited to the auto-generated key — which then vanished
when the container restarted because the read-only mount also
couldn't be written.
The symptom was a 0-balance import: operators extracted node.json
from the host keys dir, imported it into the mobile client, and
wondered why the genesis validator's wallet was empty.
Fix: distinguish "file doesn't exist" (first boot, generate) from
"file exists but can't be read" (operator error, log.Fatalf with a
hint about permissions / read-only mount). Also fail loudly on JSON
parse errors and decode errors instead of silently generating.
When the new-identity path is taken and the save fails (read-only
mount), the warning now explicitly says the key is ephemeral and the
node's identity will change on restart — operators can catch this
before genesis commits to a throwaway key.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>