A self-hosted LLM backend signals its exposure the moment its config binds to all interfaces, and you can read that indicator from the text on disk before the service answers one request. exposure_gate.py lints your .env, compose file, and LiteLLM config.yaml offline with five checks. In this post's fixtures, one field flips exit 0 to exit 1.

AI disclosure: I wrote exposure_gate.py with an AI assistant and ran it myself, offline, before publishing. Every number in the output blocks below is pasted from a real local run on Python 3.13.5, standard library only, no network. I checked the exit codes (0 / 1 / 2) and hashed the full STDOUT twice to confirm it is byte-for-byte deterministic. The external claims (the Zenity Labs honeypot writeup, the Dev.to post on who pays for AI access) are their research, not mine, and I link the primary sources. Their numbers stay in their own paragraphs; my numbers come only from the synthetic fixtures shown here.

In short:

The exposure surface of a self-hosted LLM backend shows up in its config text, readable before the first request: bind address, published port, a missing or placeholder master_key, and provider keys sitting behind an open door. Whether that surface is actually reachable still depends on a firewall or proxy the text cannot see.