A security checklist for self-hosted AI agents
Fourteen checks across network exposure, authentication, tool permissions, secrets and cost — with the command or setting for each, ordered by what actually reduces risk.
Run this against any agent you host yourself. It is ordered by impact, not by category — if you only do the first four, you will have removed most of the realistic risk.
Each item states what to check, how to check it, and what “good” looks like.
Network exposure
1. Know what your gateway binds to.
ss -tlnp | grep LISTEN
127.0.0.1:PORT is bound to localhost. 0.0.0.0:PORT or *:PORT is bound to every interface, which on a server with a public IP means the internet. Good: localhost, unless you have a specific reason otherwise.
2. Confirm reachability from outside.
Binding is not the whole story — a firewall may still be in front. From a machine on a different network:
curl -m 5 -sv http://YOUR_SERVER_IP:PORT/ 2>&1 | tail -20
Good: connection refused or timeout. An HTTP response means it is open.
3. Do not rely on an obscure port.
Moving a service to port 48291 does not hide it. Internet-wide scanners sweep the full port range continuously; a non-standard port buys you hours, not safety. Good: the control is authentication or a firewall, never obscurity.
4. Prefer a private network over a public port.
If you need remote access, WireGuard or Tailscale gives it to you without any publicly routable port. Good: no agent port is reachable from the public internet at all.
Authentication
5. Require authentication on every entry point.
Send an unauthenticated request to each interface your agent exposes — HTTP API, web UI, websocket, any channel adapter. Good: every one of them rejects you.
6. Check the interfaces you forgot about.
Agents often expose more than one thing: an API, a UI, a metrics endpoint, a debug port. Authentication on the main UI does not imply authentication on the API behind it. Good: you have enumerated every listening port and tested each one.
7. Change every default credential.
Default passwords and generated-on-first-run tokens that were never rotated are the same problem. Good: nothing in your deployment uses a credential that shipped with it.
8. Terminate TLS.
If anything crosses a network you do not control, it should be HTTPS. A reverse proxy — Caddy does it with near-zero configuration — is the normal answer. Good: no plaintext credentials or session tokens on the wire.
Tools and permissions
9. Enumerate what your agent can do.
List every tool the agent has: shell, file read, file write, HTTP, database access, credentials for third-party services. Assume for a moment that an untrusted person is sending it messages. Good: you have actually read this list recently, and nothing on it surprises you.
10. Scope the workspace.
An agent with file access should be confined to a directory, not pointed at a home directory or /. Good: the agent cannot read your SSH keys, your browser profile, or another project’s .env.
11. Remove tools you are not using.
Every tool is attack surface, and unused tools are pure downside. Good: the tool list matches what you actually do with the agent.
12. Run it as a non-root user.
If the agent can execute commands, the user it runs as is the ceiling on what those commands can do. Good: a dedicated unprivileged user, ideally in a container.
Secrets
13. Keep keys out of places that leak.
Check for API keys in committed config files, shell history, container images, prompt text, and log output. Logging is the one people miss — a debug log that records full request bodies will record your keys.
git log -p | grep -iE 'sk-|api[_-]?key|token' | head
Good: keys exist in exactly one place, that place is not in version control, and nothing prints them.
14. Use separate keys with limited scope.
A dedicated key per deployment means you can revoke one without breaking everything else, and you can tell from provider-side usage which deployment is doing what. Good: this agent’s key is not shared with your other projects.
Cost, which is a security property
An exposed agent’s most common outcome is someone spending your money, so the controls belong here.
Set a hard spending cap at your provider. This is the only limit that survives a mistake on your side. Two minutes to set, and it bounds the worst case.
Alert on unusual spend. A sharp increase is often the first observable symptom of unauthorised use, frequently before you notice anything else.
Doing it more than once
Every item above is a point-in-time check, and every one of them can be quietly undone: a config restore, a container rebuilt from an older image, a change made to get something working from a phone. The checks are not difficult. Remembering to repeat them is, and memory is a poor control.
That repetition is what the security audit in GambaOS automates — gateway binding, authentication, channel exposure policy and workspace scope, scored, with each finding linked to the page that fixes it. A default deployment usually scores in the fifties on the first run.
Automated or not, the list above is worth walking through by hand once. The first three items take about five minutes, and they are where the surprises usually are.