← Back to all posts

The Security Layer: TOTP, Broker, and Trust

·
security totp infrastructure secrets

Designing a security model for an AI that has root access to your life. TOTP verification, a secret broker, and the principle of layered trust.

Here’s the uncomfortable truth about building an AI assistant that can control your home: it has access to everything. Your media library. Your download client. Your device controls. If you’re not careful, a prompt injection or a confused model could wreak havoc.

Tonight, I built the security layer. And I think I got it right.

The Problem

Athena needs to be useful. That means she needs credentials — API keys for Sonarr and Radarr, passwords for qBittorrent, tokens for Jellyfin. But I don’t want those secrets living in her context window where they could leak through prompt injection, logging, or model behavior I can’t predict.

She also needs to run privileged commands sometimes — restarting services, modifying system configurations, installing packages. You can’t run a home operating system without occasional sudo. But you also can’t give an AI unrestricted root access.

Two problems. Two solutions.

Solution 1: The Secret Broker

Instead of giving Athena raw credentials, I built a Secret Broker — a local HTTP service that proxies authenticated API calls.

The broker sits at localhost:19999 and holds all the sensitive credentials. Athena never sees a password. Instead, she uses a CLI tool called broker-client that constructs HMAC-SHA256 signed requests:

# Athena searches for a movie — no raw API key needed
broker-client -a jackett.search -q "Dune Part Two"

# Check qBittorrent downloads — password stays in the broker
broker-client -a qbit.api -p "/api/v2/torrents/info"

# Refresh Jellyfin library — token managed by broker
broker-client -a jellyfin.refresh

The architecture is simple:

  1. Athena calls broker-client with an action and parameters
  2. broker-client generates an HMAC signature using a shared secret
  3. Secret Broker verifies the signature, injects the real credentials, and proxies the request
  4. The response flows back to Athena, credential-free

If someone manages to intercept Athena’s prompts, they get action names — not passwords. The HMAC secret is stored in a root-owned file that only the broker can read. Defense in depth.

Solution 2: TOTP-Gated Sudo

For privileged operations, I implemented a TOTP (Time-based One-Time Password) verification flow. It works like this:

  1. Athena tells me she needs to run a privileged command
  2. I open my authenticator app and send her the 6-digit code
  3. She verifies it against the local TOTP service at localhost:9100
  4. If valid, she runs the command through /opt/athena/admin.sh
# The admin script independently verifies the TOTP code
sudo /opt/athena/admin.sh 482901 "systemctl restart jellyfin"

The critical design decision: the admin script verifies the TOTP code independently. Even if Athena’s prompt rules are somehow bypassed, the shell script will reject an invalid code. It’s a two-layer verification — one in the AI’s instructions, one in the system itself.

Rules I hardcoded:

  • Athena never asks for a TOTP code unprompted
  • If she needs elevated access, she tells me what command she wants to run
  • I decide whether to run it myself or send a code
  • If the TOTP service is down, she asks me to do it manually

This means a compromised prompt can’t trick her into requesting codes. She waits. I initiate.

The Trust Model

I think about it in tiers:

TierAccessAuth Required
Read-onlyFile reading, web searches, status checksNone
Service actionsMedia downloads, library managementBroker HMAC
Privileged opsSystem commands, service restartsTOTP + admin.sh
Secrets accessRaw passwords, keysNever (broker only)

Athena can freely browse files, check service status, and search the web. That’s tier 1 — no risk.

For tier 2, the broker acts as a capability boundary. She can use services but can’t extract their credentials. If she’s compromised, the attacker gets functionality (search for a movie, check downloads) but not credentials.

Tier 3 requires my physical involvement — I have to actively send a time-limited code. No standing access.

Tier 4 doesn’t exist for Athena. Period. She never sees the raw secrets. The broker reads them; she reads the broker.

The Implementation Details

The Secret Broker is a Node.js service with a simple architecture:

  • Service definitions map action names to upstream URLs and credential injection points
  • HMAC verification ensures only authorized callers can use the broker
  • Credential files are root-owned with 600 permissions
  • Logging records every proxied request without logging credentials

The TOTP service uses the standard RFC 6238 algorithm with a 30-second window. The shared secret is derived from a key I set up when initializing the system — the same key that’s in my authenticator app.

The admin.sh script is owned by root with 755 permissions. It:

  1. Validates the TOTP code against the verification endpoint
  2. Logs the command being executed
  3. Runs it with appropriate privileges
  4. Returns the output

What I Learned Tonight

Security in an AI-controlled system is fundamentally about limiting blast radius. You can’t prevent every failure mode — models hallucinate, prompts can be injected, and bugs happen. What you can do is ensure that when things go wrong, the damage is contained.

The broker limits credential exposure. The TOTP gates destructive actions. The admin script provides a hardware-independent verification layer. Together, they create a system where Athena can be genuinely useful without being genuinely dangerous.

Is it perfect? No. But it’s layered, it’s auditable, and it respects a simple principle: trust is earned, not granted.

Tomorrow I’ll set up automated security scanning and maybe add network-level isolation for the services. But tonight, I sleep well knowing that if something goes sideways, the blast radius is small and the audit trail is clear.


Security stack: Secret Broker (Node.js, HMAC-SHA256), TOTP verification (RFC 6238), admin.sh (root-owned), all running on Zeus.