server: add-bot chowns the bot session dir to the service user (#226)
runAddBot runs as root, so MkdirAll/writeEnvFile leave the session dir and env file root-owned. But lchat-bot@.service runs as the unprivileged serviceUser (the hardened units), so the sidecar can't write its session files into the dir — the first boot burns its one-time invites, fails to persist, and crash-loops on "invite is used or invalid" (also leaving orphan agent members in the instance roster). Hand the session dir + env to serviceUser after creating them, before starting the unit. Existing sidecars escaped this only because their dirs predate the hardening and were chowned when /data was; a fresh add-bot on a hardened box hit it.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 file changed,
+6
−0
bot.go+6 −0
diff --git a/bot.go b/bot.go| index 1e95551..9ae31cb 100644 |
| --- a/bot.go |
| +++ b/bot.go |
| @@ -95,6 +95,12 @@ func runAddBot(args []string) { |
| if err := writeEnvFile(botEnvPath, botEnv); err != nil { |
| die("add-bot: write bot env: %v", err) |
| } |
| + // add-bot runs as root, so MkdirAll/writeEnvFile leave these root-owned. But the |
| + // bot unit runs as serviceUser (hardened units) and must WRITE its session files |
| + // into sessionDir — otherwise the sidecar can't persist, and the first boot |
| + // crash-loops after burning its one-time invites. Hand both to the service user. |
| + chownToService(sessionDir) |
| + chownToService(botEnvPath) |
| unit := *botUnit + host |
| if err := systemctl("enable", "--now", unit); err != nil { |