# Phase 4: NPM Client **Status:** ⬜ Not Started **Parent plan:** [PLAN.md](./PLAN.md) **Domain:** backend ## Objective Implement the Nginx Proxy Manager API client — JWT authentication, CRUD for proxy hosts, and host lookup. ## Tasks - [ ] Task 1: Create NPM client struct with base URL, cached JWT token, and auto-refresh - [ ] Task 2: Implement `Authenticate(ctx, email, password)` — POST /api/tokens, store JWT - [ ] Task 3: Implement `CreateProxyHost(ctx, config)` — POST /api/nginx/proxy-hosts - [ ] Task 4: Implement `UpdateProxyHost(ctx, id, config)` — PUT /api/nginx/proxy-hosts/{id} - [ ] Task 5: Implement `DeleteProxyHost(ctx, id)` — DELETE /api/nginx/proxy-hosts/{id} - [ ] Task 6: Implement `ListProxyHosts(ctx)` — GET /api/nginx/proxy-hosts - [ ] Task 7: Implement `FindProxyHostByDomain(ctx, domain)` — search existing hosts by domain name - [ ] Task 8: Define proxy host config struct (domain, forward host/port, SSL settings, etc.) - [ ] Task 9: Handle JWT token expiry — re-authenticate automatically on 401 ## Files to Modify/Create - `internal/npm/client.go` — NPM API client, auth, HTTP helpers - `internal/npm/types.go` — request/response types for proxy hosts ## Acceptance Criteria - Client authenticates and caches JWT - CRUD operations work for proxy hosts - Token refresh happens transparently on expiry - Proxy host config supports: domain, forward host, forward port, SSL (Let's Encrypt optional) - FindByDomain enables checking if a proxy already exists before creating ## Notes - NPM API base: typically `http://npm:81/api` - Forward host for containers: use container name on the shared Docker network - Forward port: the container's internal port (from EXPOSE) - SSL: for staging, can be disabled; production may want Let's Encrypt - NPM credentials come from settings (encrypted in SQLite, decrypted at runtime) ## Review Checklist - [ ] All tasks completed - [ ] JWT caching and refresh work correctly - [ ] HTTP errors are properly handled (not just status code, but response body) - [ ] No credentials logged or leaked in errors - [ ] Struct types match NPM API contract ## Handoff to Next Phase