Embed RNS cleanly: fix signal-thread crash, tame atexit/stdout, persist on stop

Root cause of setup failure: Reticulum.__init__ calls signal.signal() with no
main-thread guard, but we (correctly) initialise off the event loop in an
executor thread, where signal.signal() raises ValueError. It failed after
setting the __instance singleton, which both broke setup and caused the
"Attempt to reinitialise Reticulum" error on every retry.

- Temporarily neutralise signal.signal during RNS init so init completes in the
  executor, and so RNS does not hijack HA's SIGINT/SIGTERM (needed for clean
  shutdown under Kubernetes).
- Unregister RNS's and LXMF's atexit exit handlers, which otherwise persist
  state with blocking file I/O on the event-loop thread and detach HA's
  stdout/stderr at interpreter exit (the loop-blocking warnings seen in logs).
- Persist RNS/LXMF state ourselves off-loop on the homeassistant_stop event.

Documents the remaining non-daemon RNS worker threads as a known shutdown note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude
2026-07-22 18:46:06 +03:00
parent f75235f9df
commit a80f65c442
4 changed files with 95 additions and 17 deletions

View File

@@ -214,6 +214,14 @@ action:
apply on reload.
- The identity is stored in `config/reticulum/identity` — back it up to keep the
same address.
- RNS is a standalone-daemon-style library. To embed it cleanly the integration
suppresses RNS's own signal handlers (so Home Assistant keeps ownership of
SIGINT/SIGTERM — important under Kubernetes) and unregisters its blocking
`atexit` persistence, persisting RNS/LXMF state itself, off the event loop,
on the `homeassistant_stop` event. RNS still spawns a couple of non-daemon
worker threads, so on shutdown you may see a "non-daemonic threads" notice and
a short delay before the process exits (within the pod's termination grace
period).
- Assist replies use the standard conversation pipeline, so whichever agent you
select (built-in intents, a local LLM, a cloud LLM, …) is what answers.