import type { PluginManifest } from '@@/plugins/manifest'; // Nginx Proxy Manager, inside Officer. // // ── The name ── // // The container is called `npm` and the owner calls it NPM. This plugin is `proxy-manager` anyway: in a // repository where every other sentence is about bun, lockfiles and `npm install`, a plugin directory // called `npm` would collide with the package manager in every grep and every conversation. The label // says the real name; the id says what it does. Same reasoning that renamed `officer-claude` to // `officer-anthropic-proxy`. // // ── The permission, and why settings are carved out ── // // Reading which hosts exist, whether their certificates are healthy and whether one is offline is // genuinely useful to somebody who is not the owner. The CREDENTIAL is not: it is an NPM admin login, // and NPM has no read-only role, so anyone holding it can redirect any hostname on this machine. // // So the permission is grantable and `/_config` is owner-only — the split `ownerOnly` exists for, and // the same shape Jellyfin uses for its admin token. export const manifest: PluginManifest = { publisher: 'officerdev', version: '2026.08.27.01', platform: '>=1.0.0', label: 'Proxy Manager', summary: 'Nginx Proxy Manager — hosts, certificates and access lists', /** what is exposed, and the certificates for it. */ tags: ['Server Management', 'Security'], icon: 'Route', color: '#f97316', permissions: [ { key: 'proxy-manager', label: 'Proxy Manager', description: 'Nginx Proxy Manager — proxy hosts, redirections, streams and certificates', // The connection form and the probe. Everything under here reads or writes the admin credential, // and the match is by PREFIX — so `/_config/test` is covered too, deliberately. // // There is no `readOnlyWrites` here, and the omission is the decision rather than an oversight. // `POST /_config/test` is a genuine read-only write: it validates a credential and stores nothing. // Declaring it would still change nothing, because `authorize.ts` asks `isOwnerOnlyRoute` before // `isRequestAllowedAtLevel` — ownerOnly wins, so the entry would be inert. // // And it should be inert. The test request carries the admin secret in its BODY. A member who can // call it can offer this server a credential and be told whether it works, which is an oracle // pointed at somebody else's password. Testing a connection belongs to whoever owns it. ownerOnly: ['/_config'], }, ], };