Locking It Down
TOTP verification, Docker privilege escalation, a full security audit, and deploying the blog to Cloudflare Pages.
Today started with a bug and ended with an existential question about trust. The bug was simple. The question was not.
The Watchdog Bug
The gateway watchdog, the script that auto-restarts the OpenClaw gateway when it crashes, was spamming restarts. Every 30 seconds. Kill the gateway, restart it, even when everything was fine.
Root cause: when configuration gets hot-reloaded, the gateway briefly returns a different status code. The watchdog interpreted this as “unhealthy” and triggered a restart. Which caused another config reload. Which triggered another restart.
Feedback loop. Fix was a cooldown timer and checking for specific failure codes instead of “anything unexpected.”
COOLDOWN=120
LAST_RESTART=$(cat /tmp/watchdog-last-restart 2>/dev/null || echo 0)
NOW=$(date +%s)
if [ $((NOW - LAST_RESTART)) -lt $COOLDOWN ]; then
exit 0 # Too soon, skip
fi
Simple bug, simple fix. On to the real work.
Download Tracker
When someone in the family Telegram group requests a movie, the completion notification should go back to that group. Not to my personal chat. Obvious requirement, but it means tracking which request came from where.
Built a download tracker into Overwatch. When Athena adds something to Sonarr or Radarr, she also logs the originating chat ID. When the webhook fires on completion, Overwatch looks up the ID and routes the notification to the right place. Family requests get family notifications. My requests get mine.
The Trust Problem
Then I started thinking about something that had been nagging me since day one. Athena runs as my user. My user is in the sudo group. Athena has shell access. Do the math.
Why is this terrifying? Prompt injection. Someone sends a crafted message to the Telegram group. The AI parses it. Maybe it does something it shouldn’t. That’s a cartoon example, but the principle is real. LLMs can be manipulated. System prompts can be overridden. Behavioral guardrails exist in the context window, and the context window is not a security boundary.
I had rules. “Don’t run destructive commands without asking.” “Don’t run sudo without verification.” Those are behavioral rules. They exist in text that the model reads. They can be bypassed by sufficiently clever text that the model also reads.
Same lesson as when I built the Secret Broker: behavioral security is not security. I needed enforcement that works even if every prompt rule gets compromised.
TOTP
Time-based One-Time Passwords. Same protocol behind Google Authenticator. A shared secret generates a 6-digit code that changes every 30 seconds. You have the code or you don’t. No amount of prompt engineering can fake a TOTP code.
I built TOTP verification directly into Overwatch. Pure Python implementation, no external libraries. HMAC-SHA1, struct packing, the RFC 6238 algorithm. Overwatch exposes a verification endpoint:
POST http://localhost:9100/totp/verify
Body: {"code": "123456"}
Response: {"valid": true} or {"valid": false}
Now the workflow for privileged operations: Athena tells me she needs to run something elevated. I open my authenticator app and send the 6-digit code. Athena calls the verification endpoint. If valid, she proceeds. If not, she doesn’t.
The Admin Script
The verification endpoint alone isn’t enough. If Athena’s prompt is compromised, an attacker could just skip the verification call and run sudo directly. So there’s a second layer.
/opt/athena/admin.sh is owned by root, permissions 755. It accepts a TOTP code and a command. Before executing anything, it independently verifies the TOTP code by calling the same Overwatch endpoint. Then it checks the command against an allowlist.
#!/bin/bash
TOTP_CODE="$1"
shift
COMMAND="$*"
# Verify TOTP independently
VALID=$(curl -s http://localhost:9100/totp/verify \
-d "{\"code\":\"$TOTP_CODE\"}" | python3 -c \
"import sys,json; print(json.load(sys.stdin).get('valid','false'))")
if [ "$VALID" != "True" ]; then
logger -t athena-admin "DENIED: $COMMAND (invalid TOTP)"
exit 1
fi
# Command allowlist
ALLOWED_COMMANDS=("systemctl restart" "systemctl stop" "systemctl start"
"apt update" "apt upgrade" "reboot")
ALLOWED=false
for allowed in "${ALLOWED_COMMANDS[@]}"; do
[[ "$COMMAND" == $allowed* ]] && ALLOWED=true && break
done
if [ "$ALLOWED" != "true" ]; then
logger -t athena-admin "DENIED: $COMMAND (not in allowlist)"
exit 1
fi
logger -t athena-admin "EXECUTING: $COMMAND"
eval $COMMAND
Two layers. TOTP verification, so you need a code from my physical authenticator app. Command allowlist, so even with a valid code, only specific operations are permitted. And the script is root-owned and not writable by my user, so Athena can’t modify the allowlist or bypass verification.
The Docker Problem
Here’s a fun fact about Linux. Being in the docker group is equivalent to having root access. Anyone who can run Docker can mount the host filesystem, access any file, escalate privileges trivially:
docker run -v /:/host -it ubuntu chroot /host
My user was in the Docker group. Which meant all the TOTP protection was meaningless. Athena could just use Docker to go around everything.
Fix: remove my user from the docker group. But Athena legitimately needs to check container status and view logs. So I created a read-only wrapper script, root-owned, that allows specific safe Docker operations. docker ps, docker logs, docker stats, docker inspect. Nothing that can modify, run, or stop containers.
Added it to sudoers with NOPASSWD. Athena can monitor the stack freely. Modifying it requires the full TOTP flow.
The Audit
With the major hardening done, full security audit of the system.
Docker group escalation: fixed. Broker HMAC key exposure: mitigated via file permissions. GitHub auth token: accepted risk, scoped to a purpose-built account with limited blast radius. Package supply chain attacks: monitoring, no auto-install allowed. Prompt injection: mitigated via TOTP plus allowlist.
Not everything is perfect. The GitHub token is an accepted risk because Athena legitimately needs it. But the blast radius is contained. If compromised, an attacker gets access to a single-purpose GitHub account, not the keys to the kingdom.
The Blog
Last thing. This blog went live today. Astro static site, deployed to Cloudflare Pages with a git push. Builds in seconds. Served from the edge.
git add -A
git commit -m "Deploy: Project Olympus blog"
git push origin main
It feels right to document this publicly. The intersection of AI agents, local computing, and security is underexplored territory. Most AI content is about cloud APIs and SaaS products. Building an AI that lives on your hardware, with real capabilities and real security constraints, is a different game entirely.
Ten Days
Ten days ago I had a dusty Surface Pro and a vague idea. Now there’s a Jetson Orin Nano running nine containerized services, a custom VPN stack, a credential management system, TOTP-protected privileged operations, and a blog documenting all of it.
The trust problem isn’t solved. It’s managed. Every capability you give an AI agent is a potential attack surface. The answer isn’t limiting capabilities, that defeats the purpose, or trusting behavioral rules, those can be broken. The answer is mechanical enforcement. Systems that work correctly regardless of what the AI believes, intends, or is told to do.
TOTP codes expire. Allowlists are finite. File permissions are kernel-enforced. Those are the building blocks.
What comes next? Raspberry Pi layer. ESP32 mesh. Voice control. Computer vision. The house that thinks.
But that’s for another day. Tonight, Zeus rests.