what this is actually for
the brief that produced this plugin was "build me an NPM proxy", in full. no vision was given, so the agent inferred one — a careful read-only window onto NPM, with creating and editing deliberately excluded — and inferred it wrong. the point is the handover. two paths, and setup should ask out loud which one you are on: give Officer your NPM login and your DNS provider key and then ask for what you want in a sentence, or keep them and manage NPM yourself on a public address. strip the credentials out and what remains is a list NPM already shows plus a warning you could set up yourself — a notifier, not a plugin. it exists for the handover. which makes the clearest case worth stating on its own: TLS on a tailnet-only name is impossible without the DNS credential. let's encrypt cannot visit a private address, so the only check left is a DNS record, which needs the provider's API. that is not a limitation of the design, it is the shape of the problem. half the old principle survives. creating is additive and cannot break a host 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 stay in NPM's UI until there is a reason they should not. nothing here is built. said so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
# Proxy Manager
|
# Proxy Manager
|
||||||
|
|
||||||
Nginx Proxy Manager inside Officer — which hosts are running, whether they are healthy, and which
|
Hand your proxying over to Officer, and tell it what you want in a sentence.
|
||||||
certificates are about to expire.
|
|
||||||
|
|
||||||
**Status: first round. Reads work against a live NPM 2.15.1; the settings form is not built and the
|
**Status: first round, and built to the wrong thesis.** Reads work against a live NPM 2.15.1. The brief
|
||||||
plugin is not installed** — see "What is not built" below.
|
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
|
Not a second window onto Nginx Proxy Manager. NPM already has a perfectly good one.
|
||||||
one click away, and each is a form with real consequences — a wrong proxy host takes a domain off the
|
|
||||||
internet.
|
|
||||||
|
|
||||||
This surface answers *"what is running, and is it healthy"*, and the one write it allows is reversible in
|
The point is to let someone hand over the parts of this they would rather not think about, and to make
|
||||||
a click. Building the editor is a decision to make deliberately, not to arrive at by accretion.
|
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
|
## 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.
|
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.
|
- **`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.
|
- **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
|
- **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
|
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.
|
verdict or the version changes, so a poll is not an UPDATE.
|
||||||
|
|||||||
Reference in New Issue
Block a user