diff --git a/README.md b/README.md index 049ceb3..3cc10e1 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,36 @@ So **TLS on a VPN-only hostname is impossible without the credential.** That is design, it is the shape of the problem. The alternative is not "the same thing without a key", it is a publicly exposed address with DNS you manage yourself. +## How the agent asks, and what it is never told + +The agent calls **this plugin**, and never Nginx Proxy Manager. Keeping up with NPM's API — its version, +its shape, the day it changes — is this plugin's job, and the whole point of having one. + +Most of what it exposes is a thin proxy over NPM's reads, and should stay thin. **The expose route is +the one place it deliberately is not.** The agent sends what was asked for — a hostname, what it should +point at, and which network may reach it — and the plugin turns that into the three separate things it +actually means: a DNS record, a certificate, a proxy host. + +That split is the whole safety argument. "Reachable only from the private network actually means these +three things" gets written down once, here, where it can be read and tested. The alternative is teaching +the agent NPM's own request format, which puts that knowledge in the agent's memory instead — and you +would discover it had forgotten only when a hostname is reachable that should not be. + +The agent is never given a credential. Not NPM's, not the DNS provider's. It says the sentence; the +plugin holds the keys and does the work. + +## "Private" is a network the user names, not a tailnet + +Officer can set up a Tailscale network for you, and it knows that network's address range — so it can +fill this in without asking. **That is a convenience, not the model.** Somewhere with a VPN already in +place has their own ranges and nothing to do with Tailscale, and they must be able to say so. + +So what gets stored is a **named network and the addresses it covers**, and the request is *"expose this +to "*. NPM's access lists take address ranges, so this costs nothing to support properly. + +The certificate half is unchanged by any of it. What forces the DNS proof is that the name cannot be +reached from the public internet — true of anybody's private network, whoever runs it. + ## Read-only is a state, not the design The first round creates nothing, edits nothing and deletes nothing, and the original README argued that @@ -152,10 +182,13 @@ Verified against the live instance: a wrong credential comes back `likelyAuth = renders it yet, so the connection has to be inserted by hand for now. - **`db:push` has not been run**, so `proxy_manager_connection` does not exist in Postgres yet. - **Redirections, streams, 404 hosts and access lists** have routes and no panels. -- **All of the above.** No domain registry, no DNS provider credentials, no agent, no write path. The - vision in "What this is for" is entirely unbuilt — the sentence a user should be able to say is the - thing to design next, and where domains and provider keys live is an open boundary question rather - than a settled one. +- **All of the above.** No domain registry, no DNS provider credentials, no named networks, no agent, + no expose route. Everything from "What this is for" through "Private is a network the user names" is + agreed and entirely unbuilt. +- **Two things to check before building the DNS half.** NPM can only run the DNS proof for providers it + ships support for — a fixed list, not "any provider" — and if yours is missing, the proof has to be + done elsewhere and a finished certificate handed over, which is a much larger job. Neither the list + nor whether a given GoDaddy account still has DNS API access has been verified here. - **Certificate expiry notifies nobody.** `daysUntilExpiry` is computed in `api/router.ts` and thrown away when the panel closes. NPM renews at 30 days; inside 14 means renewal has actually failed and the domain goes dark at zero. Nothing on the machine will say so.