diff --git a/README.md b/README.md index 3108ea0..049ceb3 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # Proxy Manager -Nginx Proxy Manager inside Officer — which hosts are running, whether they are healthy, and which -certificates are about to expire. +Hand your proxying over to Officer, and tell it what you want in a sentence. -**Status: first round. Reads work against a live NPM 2.15.1; the settings form is not built and the -plugin is not installed** — see "What is not built" below. +**Status: first round, and built to the wrong thesis.** Reads work against a live NPM 2.15.1. The brief +that produced this code was, in full, "build me an NPM proxy" — no vision was given, so the agent that +wrote it inferred a sensible one and inferred it wrong. What is below under "What this is for" is the +actual intent, written down after the fact. Read that before extending anything. --- @@ -34,16 +35,53 @@ Redirection hosts, streams, 404 hosts and access lists are exposed by the API an --- -## What it deliberately does not do +## What this is for -**No creating, editing or deleting hosts. No certificate issuance.** NPM's own UI does those well and is -one click away, and each is a form with real consequences — a wrong proxy host takes a domain off the -internet. +Not a second window onto Nginx Proxy Manager. NPM already has a perfectly good one. -This surface answers *"what is running, and is it healthy"*, and the one write it allows is reversible in -a click. Building the editor is a decision to make deliberately, not to arrive at by accretion. +The point is to let someone hand over the parts of this they would rather not think about, and to make +handing over safe enough that it is a reasonable thing to do. There are two paths and the setup should +ask, out loud, which one you are on. ---- +**You hand over the credentials.** Your NPM login, and your DNS provider's API key — GoDaddy, +Cloudflare, whoever you registered with. Both go into the secret store, each encrypted under its own +purpose key, and neither is ever returned across the API to anybody, including you. From then on you ask +for what you want in a sentence — *"expose sub.mydomain.com on the tailnet, over HTTPS"* — and an agent +does the three things that sentence actually means: the DNS record, the certificate, the proxy host. + +**You keep them.** Then you run NPM yourself on a public address and handle DNS at your provider. That +is a completely reasonable way to run a server, and if it is you, this plugin has little to offer: a +list you already have, plus a certificate warning you could set up yourself. + +Which is the honest test of whether this should exist at all. **It exists for the handover.** Everything +here is worth building in proportion to how much it helps somebody who does not want to learn what a +proxy host is — and it should still be legible to somebody who has been doing this by hand for years, +because that person is who decides whether the handover looks safe. + +## Why a private name needs your DNS key + +The clearest case, and the one that shows the credential is a requirement rather than a nice-to-have. + +HTTPS needs a certificate. Let's Encrypt issues them free, but only after checking you control the name, +and normally it checks by visiting it over the internet. A name that resolves to a tailnet address +cannot be visited. There is exactly one other check it accepts: prove control by placing a specific +record in that domain's DNS — which needs the provider's API. + +So **TLS on a VPN-only hostname is impossible without the credential.** That is not a limitation of this +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. + +## Read-only is a state, not the design + +The first round creates nothing, edits nothing and deletes nothing, and the original README argued that +as a principle: NPM's own UI does those well, and each is a form with real consequences. + +Half of that survives. **Creating is additive** — a new proxy host cannot break one that already works — +so it is the write path the agent should get, and it is the product rather than a later phase. **Editing +and deleting are not**, and those can stay in NPM's UI until there is a reason they should not. + +Read-only is what the plugin shows before anyone has handed anything over. It is the fallback, not the +thesis. ## Permissions @@ -114,6 +152,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. +- **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. - **Nothing is polled.** Both panels load once and have a refresh button. `GET /status` is built to be polled when something does: the probe is cached for 30 seconds and the row is written only when the verdict or the version changes, so a poll is not an UPDATE.