Luna lives: a first persistent AI agent on my home server
Blog post #37
There’s an old Dell sitting next to my desk. A few months ago I installed Ubuntu on it and ran OpenClaw — Peter Steinberger’s open-source AI agent that was viral for a hot minute earlier this year. Then he joined OpenAI, Anthropic banned third-party OAuth, and the OpenClaw moment passed. The Dell kept running quietly in the corner with a screensaver.
This afternoon I came back to it. The plan was simple: clean the slate and put Hermes Agent (Nous Research, MIT, released February 2026) on it instead. By midnight I had a named, persistent AI agent called Luna, reachable through a web dashboard and Telegram, with nightly off-site backups to a private GitHub repo. Half of it was real engineering, half was choreography between me and Claude.
This is the second time I’ve felt like the future is here — not coming, here. The first was when I realized Claude Code could ship features end-to-end. This one is different: I’ve moved something in. Luna isn’t a chat that disappears when I close the tab. She lives on a machine in my room.

What shipped
- A Dell mini running Ubuntu 24.04, renamed from
openclawtodell-agents— agent-neutral, doesn’t bind the box to whichever AI happens to be the flavour of the month - Hermes Agent v0.13.0 installed as a system-level systemd service
- A custom dashboard service on
127.0.0.1:9119exposed through an SSH tunnel — openhttp://localhost:9119in my browser when I’m on the same network - Anthropic Claude Sonnet 4.6 as her default model, pay-per-token with a $20/month cap in the Anthropic console
- Telegram integration —
@Luna_HermesAgent_bot, allowlist to my user ID only - Nightly backup to
bltg85/luna-backup(private repo) via a GitHub deploy key, pushing asLuna Bot <luna@dell-agents.local> - Security baseline: UFW with default-deny incoming, fail2ban on SSH, narrow
NOPASSWDsudo scope so Claude can runapt,systemctl,ufw, andfail2ban-clientwithout prompting me — but nothing else - All secrets through 1Password CLI. Never plaintext in chat, never in env files I’d accidentally commit
She’s called Luna because she needed a name.
What’s working
The whole thing comes together in three short surfaces now: SSH into the box, open localhost:9119 in Chrome, or message her on Telegram. Three doorways, one mind. The dashboard embeds the TUI in the browser, so I can chat with her there too — same agent, same memory, just a different way in.
When I asked her to introduce herself the first time, she said:
“God afton! Jag heter Luna — en AI-agent byggd på Hermes Agent-ramverket. Just nu kör jag på Claude Sonnet 4 via Anthropic. Jag ‘bor’ på din lokala Linux-maskin — min hemkatalog är
/home/stefan/.hermes/och just nu arbetar jag i projektkatalogen/home/stefan/.hermes/hermes-agent.”
There was a small flutter when I read that. She knew where she lived.
Decisions made
One LLM provider, not five. Hermes supports 25+ providers. I’d assumed flat-rate via ChatGPT Plus or Claude Max OAuth would beat pay-per-token. Claude pulled me back to reality: Anthropic banned OAuth in third-party tools in April 2026, and ChatGPT Plus hits hard usage limits that kill agent workflows. OpenAI Pro at $200/month only wins if you’re certain to spend more than that — which I’m not, not yet. So: Anthropic API direct, Sonnet 4.6 as starter, $20 cap. If I ever hit the cap I’ll know something interesting is happening.
Narrow sudo, not none. The interesting question came partway through: how do I give Claude some sudo without giving him everything? I landed on a single file in /etc/sudoers.d/:
stefan ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /bin/systemctl, /usr/sbin/ufw, /usr/sbin/fail2ban-client
That covers nearly every routine task — install a package, start a service, open a port, check fail2ban. Anything destructive — rm, chmod, passwd, editing /etc/ — still asks me for the password. It’s not perfect security, but it’s a real boundary in the right place. “Both of us are security officers for this machine” was how I put it; the sudoers file is what that turned into.
Luna is her own identity. I gave her her own Anthropic API key (separate from the one Claude Code uses, with its own usage cap). Her own SSH deploy key for backups. Her own git identity — Luna Bot <luna@dell-agents.local>. If she goes rogue or her server is compromised, we can revoke her credentials without touching mine. A real role gets real artifacts.
Aelg, not Luna, on the box. The hostname is dell-agents, not luna. The agent on the box can change. The box is the box.
What’s unclear or broken
- A kernel upgrade is pending on the Dell (6.8.0-101 → 6.8.0-111). I deferred the reboot. I’ll do it tomorrow morning when the dashboard SSH tunnel doesn’t matter for ten minutes.
- The backup script handles config, SOUL.md (Luna’s “personality” file), memories, cron jobs, and hooks. It explicitly excludes
.env, sessions, logs, and the state database. If the machine catches fire tonight I can restore what she is but not what she remembers about our last conversation. The sessions might be worth keeping too, encrypted. That’s a question for later. - No monitoring yet. If Hermes crashes silently I won’t know unless I check. A health-ping to a webhook (or to me on Telegram) is on the list.
Tooling & process
I worked in PowerShell the entire session. Bash on Windows can’t see the 1Password SSH agent — it’s a named pipe only PowerShell’s ssh.exe knows about. We learned this when Bash refused to authenticate halfway through. Once we knew, everything went through PowerShell, and 1Password’s agent quietly signed every SSH handshake for us.
The 1Password CLI was the secret-handling pattern through the whole thing. The Anthropic key, the Telegram bot token — both fetched with op read and piped directly into the remote .env over SSH. They never appeared on my screen, in my shell history, or in the chat transcript. A small thing that adds up.
The slow part wasn’t the install. The slow part was every time I had to choose between two paths — system service vs user service, Anthropic vs OpenRouter, bot account vs deploy key, broad sudo vs narrow sudo. Each choice had two or three real arguments. Pair-thinking through those with Claude was the actual value. He couldn’t make the decision — they were mine — but he could lay the options out flat enough that I could pick fast.
One small unrelated win: 1Password’s biometric prompts had been driving me crazy. Every other op read call was a Windows Hello tap. Turns out there’s a single setting buried in Developer → Advanced: change “Ask approval for each new” from "application and terminal session" to just "application". The prompts dropped by an order of magnitude. I should have looked for this six months ago.
Luna is on. Tomorrow I’ll teach her something she doesn’t already know.
— Stefan