Improve companion identity handling and persistence

- Introduced `normalize_companion_identity_key` function to standardize identity key formatting by stripping whitespace and removing the optional 0x prefix.
- Updated `RepeaterDaemon` to utilize the new normalization function when processing identity keys, improving consistency and error handling.
- Added a `stop` method in `CompanionFrameServer` to persist contacts and channels before stopping, ensuring data survives daemon restarts.
This commit is contained in:
agessaman
2026-03-07 14:41:31 -08:00
parent 3725d6eb21
commit 1b6f9df489
3 changed files with 25 additions and 3 deletions

View File

@@ -164,3 +164,13 @@ class CompanionFrameServer(_BaseFrameServer):
self.companion_hash,
channels,
)
async def stop(self) -> None:
"""Persist contacts and channels before stopping (so they survive daemon restart)."""
if self.sqlite_handler:
try:
await self._save_contacts()
await self._save_channels()
except Exception as e:
logger.warning("Failed to persist contacts/channels on stop: %s", e)
await super().stop()

View File

@@ -3,6 +3,14 @@
_INVALID_NODE_NAME_CHARS = "\n\r\x00"
def normalize_companion_identity_key(identity_key: str) -> str:
"""Strip whitespace and remove optional 0x prefix so fromhex() is consistent across installs."""
s = identity_key.strip()
if s.lower().startswith("0x"):
s = s[2:].strip()
return s
def validate_companion_node_name(value: str) -> str:
"""Validate node_name for config sync: non-empty, max 31 bytes UTF-8, no control chars."""
if not isinstance(value, str):

View File

@@ -4,7 +4,7 @@ import os
import sys
import time
from repeater.companion.utils import validate_companion_node_name
from repeater.companion.utils import validate_companion_node_name, normalize_companion_identity_key
from repeater.config import get_radio_for_board, load_config, save_config
from repeater.config_manager import ConfigManager
from repeater.engine import RepeaterHandler
@@ -385,6 +385,10 @@ class RepeaterDaemon:
sqlite_handler = None
if self.repeater_handler and self.repeater_handler.storage:
sqlite_handler = self.repeater_handler.storage.sqlite_handler
if not sqlite_handler and companions_config:
logger.warning(
"Companion persistence disabled: no storage (contacts/channels will not survive restart or disconnect)"
)
radio_config = (
self.repeater_handler.radio_config
@@ -404,7 +408,7 @@ class RepeaterDaemon:
if isinstance(identity_key, str):
try:
identity_key_bytes = bytes.fromhex(identity_key)
identity_key_bytes = bytes.fromhex(normalize_companion_identity_key(identity_key))
except ValueError as e:
logger.error(f"Companion '{name}' identity_key invalid hex: {e}")
continue
@@ -567,7 +571,7 @@ class RepeaterDaemon:
if isinstance(identity_key, str):
try:
identity_key_bytes = bytes.fromhex(identity_key)
identity_key_bytes = bytes.fromhex(normalize_companion_identity_key(identity_key))
except ValueError as e:
raise ValueError(f"Companion '{name}' identity_key invalid hex: {e}") from e
elif isinstance(identity_key, bytes):