diff --git a/README.md b/README.md index 3cc10e1..96bb413 100644 --- a/README.md +++ b/README.md @@ -185,10 +185,14 @@ Verified against the live instance: a wrong credential comes back `likelyAuth = - **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. +- **One thing left to check before building the DNS half.** Whether a given GoDaddy account still has + DNS API access — they restricted it by account size at some point, and it is unverified here. + Cloudflare is the safer provider to build against first. + + The other half of this is now answered: **NPM tells you which providers it can drive**, over + `GET /nginx/certificates/dns-providers`, and it hands back each one's credential field names as a + template. 86 providers on the 2.15.1 image. So the picker asks the instance rather than trusting a + list of ours, and the form is generated from what NPM says. See `docs/npm-api.md`. - **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. diff --git a/docs/npm-api.md b/docs/npm-api.md index 970309a..3f29acf 100644 --- a/docs/npm-api.md +++ b/docs/npm-api.md @@ -125,6 +125,46 @@ a click. `expires_on` is null for some uploaded certificates. The router computes `daysUntilExpiry` server-side so the browser is not parsing dates and getting the timezone wrong. +### DNS providers — NPM tells you which it can drive + +``` +GET /api/nginx/certificates/dns-providers +``` + +Authenticated (`certificates.view`). Returns one entry per provider: + +```jsonc +{ + "id": "cloudflare", + "name": "Cloudflare", + "credentials": "dns_cloudflare_api_token = " // a template, with the field names in it +} +``` + +**86 providers on the 2.15.1 image here**, verified 2026-08-25 against the live instance and against +`/app/certbot/dns-plugins.json` inside the container, which is where the endpoint reads from. + +Two things follow, and they decide how the provider picker should be built: + +- **The list is per install, not universal.** It is whatever certbot DNS plugins that NPM image carries, + so an older container genuinely cannot drive a provider a newer one can. Curating this list ourselves + and shipping it as the truth would offer somebody a provider their NPM has never heard of, take their + API key, and fail at issue time with an error that does not say why. **Ask the instance.** +- **NPM also describes the credential SHAPE.** `credentials` is a template carrying that provider's own + field names — one token for Cloudflare, key and secret for others. So the form can be generated rather + than hand-written per provider, and it stays right when NPM adds providers we have never heard of. + +Which leaves a smaller job for any curated list of our own than it first appears: not which providers +exist or what fields they need — NPM answers both — but the human half. Which ones we recommend, and how +a person actually gets a key out of each one. + +### The 404 rule is not universal + +This endpoint answers **403 `Permission Denied`** unauthenticated, not the 404 documented above. So the +"NPM answers 404 when it means 401" quirk is per route rather than a property of the API, and +`npm-client.ts` treating 404 as probable-auth stays correct — it is a heuristic that fires on the routes +that need it, not a rule the whole API obeys. + --- ## Reaching NPM from Officer