🔒Securing access
A machine reachable from anywhere, running code generated by an AI: security isn't optional. SSH keys, firewall, secrets, agent permissions. How not to shoot yourself in the foot.
Let’s recap what you’ve built: a machine that runs 24/7, reachable from your phone, exposing services on the Internet, and running commands proposed by an AI. That’s awesome. It’s also an attack surface you need to take seriously, not out of paranoia, out of hygiene.
The good news: 90% of security comes down to a handful of simple gestures, done once. This guide is those gestures. None of them is hard. Skipping them, on the other hand, can turn your workshop into a spam relay or worse.
1. SSH: keys, never passwords
Remote access goes through SSH. An exposed SSH password is the number-one attack on the Internet, bots test millions of combinations per day. The defense is definitive: you disable password authentication and switch to keys.
An SSH key is a pair: a private part that stays on your laptop (never anywhere else), and a public part you drop on the machine. Without the private key, getting in is impossible, even knowing your username.
Generate a key on your computer (not on the mini-PC)
# On your laptop. Ed25519 = modern, short, solid.
ssh-keygen -t ed25519 -C "laptop-vers-minipc"
Leave the default path, set a passphrase (it’s the last line of defense if your laptop gets stolen).
Drop the public key on the machine
ssh-copy-id ulrich@adresse-de-la-machine
Test the connection: ssh ulrich@adresse-de-la-machine should log in without asking for a password.
Turn off passwords
On the mini-PC, edit /etc/ssh/sshd_config (your agent can do it, show it what you want):
PasswordAuthentication no
PermitRootLogin no
Then reload: sudo systemctl restart ssh. Keep a session open while you verify that a fresh key-based connection works, just in case.
2. The firewall: everything closed, except the essentials
By default, you block everything incoming, and open up sparingly. ufw makes this trivial.
sudo ufw default deny incoming # refuse everything by default
sudo ufw default allow outgoing # the machine can reach out
sudo ufw allow 22/tcp # SSH (or nothing if you go through Tailscale)
sudo ufw enable
sudo ufw status verbose # check
3. Secrets: never in plain text, never in the code
API keys, tokens, service passwords: these are secrets. The rule is absolute: a secret never lives in plain text in the code, nor in a file versioned by git.
Store secrets in a .env file outside git
# A .env file at the project root
echo "ANTHROPIC_API_KEY=sk-..." >> .env
# And ABOVE ALL, exclude it from git
echo ".env" >> .gitignore
The code reads the environment variable, it never contains the value.
Lock down the permissions of sensitive files
chmod 600 .env ~/.ssh/id_ed25519 # readable by you alone
Rotate anything that leaked
If a secret has been lying around somewhere (a commit, a copy-paste, a screenshot), consider it compromised and regenerate it. An API key is revoked and recreated in two clicks. Better a rotation for nothing than a leak you ignored.
Where to actually store your keys
The .env file is enough to get started, but as soon as you accumulate keys (AI APIs, GitHub tokens, service access), ask yourself the question of the right vault. There’s a little hierarchy, from simplest to most robust:
- The per-project
.envfile : the starting point. Outside git, withchmod 600. Perfect for a personal project. Its limit: the secret sits in plain text on disk, and it gets duplicated from project to project. - The keychain / password manager : for your personal stash of keys (the source of truth), a real manager (Bitwarden, 1Password, KeePassXC…) encrypted beats a thousand scattered text files. You pull from it to fill a
.envwhen you need to. - An encrypted-at-rest vault : to store secrets inside a repository without exposing them, tools like
sops+ageorpassencrypt the values: the file can be versioned, only whoever holds the decryption key reads it. The pro step when a project grows or gets shared. - A dedicated secrets manager : for serious multi-machine setups (Vault, Infisical, Doppler…), a service centralizes, audits, and rotates secrets. Probably beyond your needs at the start, but that’s where the path leads.
4. Keeping the agent in check: it executes, you decide
This is the part specific to our setup, and the most important. A code agent is powerful because it can run commands. That’s also what makes it dangerous if it runs off unsupervised. A few common-sense rules:
- Never run the agent as
root, never on automaticsudo. It runs under your normal user. When a command needssudo, you want to see it and approve it by hand. A destructive command run as root doesn’t forgive. - “Auto-approve” mode has to be earned. Agents often offer a mode where they execute without asking. Great for iterating on tests; to be banned for anything touching the network, secrets, file deletions (
rm), or prod. - Work in a project folder, not in
~or/. Limit the agent’s blast radius to the project folder. If it goes off the rails, the damage stays contained. - Generated code gets reviewed before it runs in prod. An agent can introduce a flaw without meaning to (a poorly escaped SQL query, an over-broad permission). A review, by you, or by a second agent in “review” mode, is not a luxury.
- git is your safety net. Frequent commits. You can always roll back if the agent did something dumb. A project with no git history is a trapeze artist with no net.
5. Keeping the machine up to date (without thinking about it)
A patched flaw is no longer a flaw, provided you install the patch. You automate security updates:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades # answer "Yes"
The machine now applies security patches on its own. For the rest (version upgrades), a sudo apt update && sudo apt upgrade now and then is enough, your agent can remind you.
6. The safety net: backups
Security isn’t just blocking intruders. It’s also surviving your own mistake, a disk that gives out, an over-eager agent. Back up what matters (your projects, your configs, your .env, encrypted) somewhere other than the machine: another disk, a NAS, remote storage. A backup you’ve never tested restoring is not a backup.