Every app you deploy on DenkOps is closed by default. Every route requires the app's API key as a Bearer token, and the only built-in exception is /health. Nothing you ship is reachable by the public internet until you decide, path by path, that it should be.
What happens when you call a fresh deploy?
Deploying returns two things: a live HTTPS URL and an API key. Call the URL without the key and you get a 401 with a structured error body, including an ai_hint field that tells a coding agent exactly what was missing:
curl https://my-tools.denkops.host/todos
# 401 { "error": "...", "code": "...", "request_id": "...",
# "ai_hint": "This endpoint requires a Bearer token. Set the
# Authorization header, or mark the project public." }
curl -H "Authorization: Bearer $API_KEY" https://my-tools.denkops.host/todos
# 200The one route that always answers without a key is /health, so uptime checks and load balancer probes work out of the box. Everything else, every method, every path, is behind the key.
Why does default-closed matter for agent-built backends?
Because the agent that scaffolds your backend is fast, and fast scaffolding plus an open-by-default platform is how APIs full of customer data end up indexed by scanners. An agent asked to "add an endpoint that lists users" will add exactly that. It will not reliably add auth middleware, and you will not reliably notice the omission in a diff you skimmed at 11pm.
Default-closed flips the failure mode. If nobody thinks about auth, the endpoint is locked, not leaked. Forgetting now costs you a 401 during testing, which is loud and immediate, instead of an open database dump, which is silent until it is a headline. The security page calls this "nothing open by accident", and that is the whole design: the safe state is the one that requires no memory.
This is also why the error carries an ai_hint. When your agent hits the 401 while testing its own deploy, the hint tells it to send the Bearer token rather than guess. The lock does not fight the agent, it instructs it.
How do you open a path deliberately?
Some paths should be public. A webhook receiver has to accept calls from a third party that will never send your key. A status page is meant for everyone. You open these explicitly, per path: an exact path like /status, or a prefix pattern like /webhooks/* that covers everything under it. The match is deliberate and narrow, so /webhooks/* opens /webhooks/stripe but not /webhooksadmin.
The pattern that works well: keep the default, list the two or three paths that genuinely need to be public, and verify their handlers do their own checking, for example signature verification on a webhook. If the whole project is meant for everyone, you can mark it public as a whole, again as an explicit choice.
When is the API key the wrong tool?
The per-app key is machine-to-machine auth: your cron script, your other backend, your test suite. It is one shared secret for the whole app. When people connect through an AI assistant, you want per-user identity, consent, and per-person revocation instead. That is what connector OAuth provides for MCP servers: the key stays for machines, OAuth handles humans, and neither requires auth code in your app.