Commit Graph
3 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 9be2ec7e6c hand over NPM's API instead of a curated subset of it
this plugin refused to create, edit or delete anything, and argued the refusal
was care: NPM's own UI does those well and each is a form with real
consequences. the owner's call on 2026-08-25 is that the argument ends up
somewhere else —

  "even though we are assuming users that don't know how to operate nginx, the
   last thing I want to do is treat users as stupid"

and a curated surface cannot keep up anyway. every NPM feature we did not wrap
is one the owner cannot reach through Officer at all, so the plugin becomes a
worse NPM rather than a way into the real one.

so /api/proxy-manager/npm/* forwards method, path, query and body, attaches a
token, and returns NPM's own status and body unchanged — errors included. a
proxy that improves the answers is not a proxy. authentication is the only
thing it does not pass through, which is the entire job.

the permission gate needed nothing: isRequestAllowedAtLevel already returns
true for safe methods and false otherwise, so read level IS a read-only view of
NPM through here, with no code in this file deciding it twice.

the curated routes stay, for the panels. a screen wants sorted hosts and a
computed daysUntilExpiry; an agent wants NPM.

what replaces the refusal is confirmation, and it lives in the skill because
that is what it is — an instruction, not a boundary. saying so is better than a
boundary that only looks like one. the skill now carries NPM's API, names what
must be confirmed before it happens (deleting, editing a live upstream,
touching a certificate in use), says creating needs no ceremony, and forbids
reaching NPM any way but this route.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
2026-08-25 18:04:38 +00:00
pastilhasandClaude Opus 5 3380a482ed the clever type cost twelve errors; the untested rule was the load-bearing one
four fixes from a review, and the first one was leaking out of the plugin.

TYPING. `respond` derived its context from `Parameters<typeof router.get>[1]`.
hono's .get is overloaded, `Parameters<>` resolves the FIRST overload — `[path:
string]` — so index 1 was `undefined`, ctx was `never`, and all nine handlers
errored. twelve errors from one line, in a tree where `bunx tsgo` is shared with
the platform and expected clean. imports hono's `Context` instead. tsgo now
exits 0 tree-wide, which it has not done since this plugin landed.

READONLYWRITES. `/_config/test` was declared as a read-only write so a member
would not meet a 403 on the test button. they always would: authorize.ts asks
isOwnerOnlyRoute before isRequestAllowedAtLevel, and ownerOnly is prefix
matched, so `/_config` already covered it and the entry was inert. removed, and
the reason it SHOULD be inert written down — that request carries the admin
secret in its body, so a member who could call it would hold an oracle pointed
at somebody else's password.

STATUS WROTE ON EVERY POLL. recordProbe ran unconditionally on a GET whose own
comment invites polling, so a read endpoint issued an UPDATE per poll per open
panel. the probe is cached 30s and the row is written only when the verdict or
the version changes — a flapping NPM still records every transition, which is
the case the column exists for. the cache is dropped when the credential does.

DEAD TERNARY. `likelyAuth ? 502 : 502`.

and the tests. 22 over a stubbed fetch, 99% of npm-client.ts. the two that
matter are the 404-means-credential rule and the mint-once-and-retry: both are
silent when broken, and a refactor of either turns a working panel into one
that says "not found" forever. every test uses its own credential because the
token cache is module state — sharing one would let an earlier test's token
satisfy the retry test, which would then pass for the wrong reason.

still not installed, still no settings form, still no db:push. README says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
2026-08-25 15:26:18 +00:00
pastilhasandClaude Fable 5 5914e96cf3 proxy-manager: nginx proxy manager in officer
reads hosts and certificates against a live NPM 2.15.1. one grantable
permission, with /_config owner-only — reading which hosts are healthy
is worth sharing, an NPM admin login is not, and NPM has no read-only
role.

the quirk that shapes the whole plugin: NPM answers an unauthenticated
read with 404, not 401, because its router only registers a resource
once a token is present. so 'not signed in' and 'no such endpoint' are
the same response, and a naive client tells you a feature is missing
when your password is wrong. the client mints a fresh token and retries
once, then reports a persistent 404 as likelyAuth.

certificates sort by expiry rather than name, which is the only ordering
that answers a question.

no create/edit/delete and no cert issuance: NPM's own UI does those and
a wrong proxy host takes a domain off the internet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 08:38:15 +00:00