The Two Topologies Side by Side
- Hermes user accessed via SSH
- SSH server listening on port 22
- Internet-accessible if port forwarded
- Key-based authentication
- User SSH in from personal Mac
- Non-admin Hermes account
- Headless, no GUI
- Hermes is a Telegram bot running locally
- NO SSH server anywhere
- Mac mini invisible to internet (NAT + firewall)
- Bot token authenticates to Telegram API
- User interacts via Telegram on phone
- Hermes sends outbound HTTPS to api.telegram.org
- Hermes can receive commands and send responses via Telegram
Scenario A is a server topology: Hermes is a service listening for connections. Scenario B is a client topology: Hermes initiates all connections and no service on the Mac mini is reachable from outside. This distinction changes everything about the threat model.
SSH Threat Vectors
| Threat | Severity | Notes |
|---|---|---|
| SSH-Specific Attack Surface | CRITICAL | SSH is the only gateway. Brute force, key compromise, MITM, agent forwarding abuse. |
| Credential Theft | HIGH | SSH private keys, API tokens in environment variables. |
| Persistence Mechanisms | HIGH | authorized_keys injection, LaunchAgents, cron, shell rc modification. |
| Supply Chain Compromise | HIGH | Homebrew/pip/npm packages installed as Hermes. |
| Privilege Escalation | MEDIUM | Difficult without sudo — Hermes has no admin access. |
| System Self-Destruction | MEDIUM | Damage limited to /Users/hermes/. Main user architecturally off-limits. |
| Data Exfiltration | MEDIUM | Limited to /Users/hermes/. Reduced blast radius due to non-admin. |
| Remote Code Execution | LOW | Reduced surface — no GUI apps, no browser. |
| macOS TCC/SIP Bypass | LOW | No GUI session — TCC prompts can't be answered. |
| Lateral Movement | LOW | POSIX + ACL barriers effective against non-admin Hermes. |
Defense-in-Depth Architecture (SSH Model)
The 5-layer blast radius containment that protects the main user's data:
- POSIX Permissions — Home dirs at
drwx------. Hermes cannot list or read main user's home. - ACL Explicit Deny Rules —
user:hermes deny list,search,readon critical paths. - SIP (System Integrity Protection) — Kernel-enforced protection on protected system paths.
- FileVault Full-Disk Encryption — AES-XTS encrypted. Separate key derivation per user.
- Secure Enclave Hardware Keys — Keychain master keys in coprocessor. Hardware-backed.
SSH Mitigation Framework
Create /etc/ssh/sshd_config.d/hermes-hardening.conf with:
# === Authentication ===
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
# === Disable risky features ===
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
# === Restrict to Hermes only ===
DenyUsers *
AllowUsers hermes
chmod 700 /Users/hermes/.ssh
chmod 600 /Users/hermes/.ssh/authorized_keys
sudo chflags immutable /Users/hermes/.ssh/authorized_keys
sudo chflags immutable /Users/hermes/.ssh
Don't create a keychain for the Hermes user. Use environment variables for API tokens only. This eliminates keychain exfiltration as a threat vector.
# Store token in .env with restricted permissions
touch /Users/hermes/.env && chmod 600 /Users/hermes/.env
# Add to /Users/hermes/.zshenv:
test -f /Users/hermes/.env && set -a && source /Users/hermes/.env && set +a
The Mac mini is no longer a server — it's a client. Hermes initiates outbound HTTPS connections to Telegram's API, receives messages, executes commands locally, and sends responses back. No inbound ports are open. The machine is invisible to internet scanners. This is a client topology, and the threat model reflects that.
Telegram + NAT Threat Vectors
| Threat | SSH Severity | New Severity | Reasoning |
|---|---|---|---|
| SSH Brute Force / Zero-Days | CRITICAL | ELIMINATED | No SSH server exists. Not listening on any port. Entire SSH CVE class gone. |
| Port Scanning / Reconnaissance | HIGH | ELIMINATED | Mac mini invisible from internet. NAT + firewall means Nmap returns nothing. |
| SSH Key Theft | HIGH | ELIMINATED | No SSH keys used for remote access. Personal Mac SSH is a separate trust domain. |
| Telegram Bot Token Exposure | Non-existent | CRITICAL | NEW #1 THREAT. Token is the authentication credential. If leaked, attacker can send commands directly to Hermes. No additional auth layer. |
| Phone/Device Compromise | Non-existent | HIGH | NEW HIGH. User's Telegram account is the new "root credential." Compromised phone = attacker can control Hermes. |
| Data Exfiltration via Telegram | MEDIUM | MEDIUM | PRIMARY residual threat. Hermes CAN send data back through Telegram. Bot can use sendMessage, sendDocument, sendPhoto API calls. This is the C2 channel built into the design. |
| Supply Chain Compromise | HIGH | MEDIUM | Still applies — Hermes installs packages. Reduced because no automated CI exposed to internet, but pip/npm still runs. |
| Local Privilege Escalation | MEDIUM | MEDIUM | Hermes executes local commands. If it runs as privileged user, local exploit can escalate. This is now the primary local vector since SSH is gone. |
| Command Injection via Telegram | HIGH | MEDIUM | Attacker must compromise token or user's Telegram. Much harder than finding an exposed SSH prompt. User can instantly block the bot. |
What the Skeptic Found
Internet-facing attacks go to near-zero. An attacker cannot scan, probe, or exploit any service on the Mac mini because no service is reachable. Drive-by downloads, SSH brute force, port scanning, web exploitation — all eliminated by NAT + no inbound ports. The attacker's problem becomes: "I need to either compromise the Telegram account, get the bot token, or trick the user into running something." Much harder than scanning for weak SSH passwords.
Data exfiltration via Telegram is real and cannot be blocked by NAT. Telegram is bidirectional by design. The bot receives commands but can also send messages, photos, and files. An attacker who compromises Hermes can use sendMessage/sendDocument to exfiltrate data directly through Telegram's API — which is outbound HTTPS from Hermes's perspective, traversing Telegram's servers. NAT doesn't block this. The firewall only blocks inbound; outbound HTTPS is normal.
The user's Telegram account is now the critical security control. If the phone is compromised (malicious app with notification access, SIM swap, phishing), an attacker has full Hermes control. This is the scenario that most threatens the Telegram-only topology's security story. 2FA on Telegram + session lockdown + device lock are not optional — they are the primary defense.
Telegram + NAT Mitigation Framework
If Hermes has no sendMessage, sendDocument, or sendPhoto calls in its code, a compromised token cannot be used to exfiltrate data. The bot becomes a one-way read pipeline.
# Principle: Hermes receives freely; outbound must be explicitly whitelisted
# Outbound config — set to false by default
OUTBOUND_ENABLED = false
# Only these commands can send outbound messages
OUTBOUND_COMMANDS = ["status", "health"]
# Rate limit ALL outbound: max 1 per 60 seconds
OUTBOUND_RATE_LIMIT = 1
OUTBOUND_RATE_WINDOW = 60
# Alert if outbound exceeds threshold
# If Hermes sends > 5 messages in 5 minutes → trigger alert
# NEVER hardcode the token. Use environment variable or file.
# Create ~/.hermes/ with restrictive permissions
mkdir -p ~/.hermes && chmod 700 ~/.hermes
# Option A: Environment variable (best)
# Add to ~/.hermes/.zshenv or systemd service file:
export HERMES_BOT_TOKEN="your_token_here"
# Option B: macOS Keychain (best for local dev)
security add-generic-password -a "hermes-bot" -s "Hermes Telegram Bot" -w "TOKEN"
# Retrieve at runtime:
security find-generic-password -a "hermes-bot" -w
# Validate: grep for token patterns — should return empty
grep -r "bot[0-9]" ~/.hermes/ 2>/dev/null
# Rotate if ever suspected: /revoke via @BotFather
# Enable 2FA on Telegram account
# Settings → Privacy & Security → Two-Step Verification → Set PIN
# This prevents SIM-swap account takeover
# Audit active Telegram sessions
# Settings → Devices → Review → Terminate unrecognized sessions
# Lock SIM with carrier PIN (prevents SMS-based takeover)
# Call carrier or use their app to set SIM PIN
# Auto-lock phone immediately
# Settings → Display & Brightness → Auto-Lock → 1 minute or immediately
# Optional: Dedicated Telegram account for Hermes control
# Separate number/account limits blast radius if personal account is phished
# Hermes is a bot, not a shell. Restrict the command surface.
# Command allowlist — only these commands execute
ALLOWED_COMMANDS = {
"status", "health", "uptime",
"list", "read", "search"
}
# Block dangerous commands by default
BLOCKED_COMMANDS = {
"exec", "shell", "run",
"send", "upload", "broadcast"
}
# Per-user rate limit: max 5 commands in 10 seconds → cooldown 60s
# Max command length: reject any message > 200 chars
# Unrecognized command → silent drop (no feedback to attacker)
# Log every incoming command with timestamp and message hash
echo "$(date): $USER_ID: $(echo $MESSAGE | sha256)" >> ~/logs/commands.log
# Pin dependencies to exact versions with hashes
pip freeze > requirements.lock
pip install -r requirements.lock
# Audit before install
pip audit
npm audit --audit-level=high
# Verify token not in built artifacts (pre-commit hook)
grep -r "bot[0-9]" build/ dist/ 2>/dev/null
# Must return empty before deploy
# Code review: any sendMessage/sendDocument call needs review
# No hidden exfil — all outbound must be intentional
# Enable FileVault
sudo fdesetup enable -user adminuser
# Auto-lock screen immediately
# System Settings → Privacy & Security → FileVault
# Set to require password immediately on sleep/screen lock
# Firmware password (prevents boot from external drive)
sudo firmwarepasswd -setpassword -setmode command
The Realist's Verdict
Yes, meaningfully so. The SSH setup was a server topology — exposed port 22 was reachable by the entire internet. The Telegram setup is a client topology — Hermes initiates outbound HTTPS to Telegram, and the Mac mini is invisible to internet scanners. An attacker must now compromise the Telegram account or find a vulnerability in a bot that receives commands and sends responses through Telegram's API.
The SSH model's critical path was network hardening (SSH config, key management). The Telegram model's critical path is now phone security + token management. The threat doesn't disappear — it relocates to a different layer. If your phone is compromised, all the NAT in the world doesn't protect you.
| Mitigation | Burden | Security Gain | Practical? |
|---|---|---|---|
| Make Hermes read-only | Low (code change) | Very High | Yes — highest value action |
| Token in env/Keychain, not code | Low (one-time) | High | Yes |
| Telegram 2FA enabled | Low (one-time) | High | Yes |
| Session audit + phone lock | Low (one-time) | High | Yes |
| FileVault + lock screen | Low (one-time) | High | Yes |
| Command allowlist + rate limit | Med | Medium | Yes |
| Supply chain pinning | Med | Medium | Yes |
Honest Recommendations
For the Telegram + NAT topology, the Realist cut through the theory. Here's what actually matters:
If Hermes never sends messages, a leaked token is nearly worthless. This single design decision eliminates the worst-case token exposure scenario. Do this first.
Token is not in the repo, not in any log, not in any backup. Use HERMES_BOT_TOKEN environment variable. Rotate once now as a best practice.
Your Telegram account is now the root credential. 2FA prevents SIM-swap account takeover. This is the second-biggest control.
Telegram Settings → Devices → Terminate all sessions you don't recognize. Enable passcode lock on the Telegram app itself. 10 minutes of setup, immediate security improvement.
System Settings → Privacy & Security → FileVault. Set lock screen to require password immediately. Protects against physical theft. Zero ongoing effort once enabled.
The Bottom Line
The Telegram + NAT topology is a genuine improvement over SSH. Internet-facing attacks go to near-zero. But the threat doesn't disappear — it concentrates in two places: the bot token (mitigated by read-only design + env storage) and the phone (mitigated by 2FA + session lockdown). Everything else is polish.
The best security posture is the one you'll actually maintain. The Telegram topology requires less ongoing effort than SSH hardening — but phone security is non-negotiable. If you skip it, the topology advantage is lost.