Files
egress-proxies-operator/docs/plans/2026-08-11-2152-discovery-api-docs.md
2026-08-11 21:53:02 +02:00

5.8 KiB

Plan: Discovery API documentation (docs/api.md)

Created: 2026-08-11 21:52

Context

The operator serves an HTTP discovery/lease API on :8090 (internal/discovery/) that crawler clients use to list proxies, acquire TTL leases, release them, and report rate-limiting. There is no dedicated API reference today: README has four quickstart curls (no auth header, no schemas), and docs/architecture.md §7 has an ASCII route map. The user wants full documentation with curl examples for every feature.

Decisions made with user: doc lives in a new docs/api.md; commit straight to main (no MR).

Deliverable

1. New file docs/api.md — full API reference

Content (all facts verified against code during planning):

  • Overview & base URL — what the API is; in-cluster FQDN http://egress-proxies-operator-controller-manager-discovery-service.egress-proxies-operator-system.svc.cluster.local:8090 (Service: config/default/discovery_service.yaml); local access via kubectl port-forward svc/egress-proxies-operator-controller-manager-discovery-service 8090:8090.
  • Authentication — static bearer token from DISCOVERY_TOKEN env var (populated from the optional discovery-token Secret, key token; config/manager/manager.yaml). Empty token ⇒ auth disabled with startup warning. Curl: -H "Authorization: Bearer $TOKEN" on every example. /healthz always exempt.
  • Conventions — JSON everywhere; error envelope {"error":"<code>","message":"<text>"}; request bodies capped at 64 KiB; proxies with a deletion timestamp are excluded from all responses.
  • Configuration table--discovery-addr (default :8090), --max-lease-ttl (default 1h), --lease-cooldown (default 15m), DISCOVERY_TOKEN. Note the shipped Deployment passes none of these flags, so defaults apply.
  • Endpoints, each with request/response schema, status codes, and a copy-pasteable curl example:
    • GET /healthz — liveness, unauthenticated.
    • GET /v1/proxies — filters healthy=true|false (else 400 invalid_query) and repeatable attr.<key>=<value> (verbatim equality on spec.attributes, all pairs must match). Response {"proxies":[proxyView...],"count":N} sorted by id. Full proxyView field table: id (ns/name), ip, port, attributes, phase (Pending/Provisioning/Ready/Unhealthy/Deleting/Failed), healthy (condition Healthy == True), latencyMillis, activeLeases, maxLeases (default 5; explicit 0 = unleasable).
    • POST /v1/leases — body {selector, ttlSeconds, target} all optional; TTL defaults 5m, capped at max-lease-ttl (else 400 invalid_ttl). 201 {leaseID, proxy, expiresAt, ttlSeconds}; 409 no_match with considered/atCapacity/inCooldown/unhealthy counts (documented meanings).
    • DELETE /v1/leases/{id} — early release; always 204, idempotent.
    • POST /v1/leases/{id}/report — body {result: ok|rate_limited|banned, target}; 204, 404 unknown_lease, 400 invalid_result. ok is a pure ack; rate_limited and banned behave identically today (both start one cooldown window).
  • Selection & cooldown semantics (short section — this is the non-obvious part clients need):
    • Selection order: fewest active leases → lowest latency → lexicographic id; deterministic; only healthy, non-deleting proxies with free capacity are candidates.
    • Cooldown: 15m default (--lease-cooldown), keyed {proxy, target}. Report with a target blocks only leases requesting that target; report without a target (and lease without one) creates a global cooldown blocking all acquisitions of that proxy — call this footgun out explicitly.
    • Expired leases stay reportable for one cooldown window past TTL.
  • End-to-end workflow example — numbered curl walkthrough: acquire → use proxy.ip:port as HTTP proxy (curl -x) → report rate_limited on 429 → release. Using a jq-extracted leaseID.
  • Caveats — lease/cooldown state is in-memory and per-process: single replica only, operator restart drops all leases and cooldowns.

2. Pointers to the new doc (small edits)

  • README.md: one-line link near the quickstart curl section ("full reference: docs/api.md").
  • docs/architecture.md §7: one-line link to docs/api.md as the detailed reference.

3. Housekeeping per CLAUDE.md

  • First action post-approval: copy this plan to docs/plans/<timestamp>-discovery-api-docs.md (timestamp from date "+%Y-%m-%d-%H%M"), commit it alone.
  • Then write the docs, commit to main with Co-Authored-By: Claude <noreply@anthropic.com> trailer, push.
  • Append execution summary + status checklist to docs/plans-executions/<same-timestamp>-discovery-api-docs.md in the docs commit.
  • Add CHANGELOG.md entry (timestamp via date "+%Y-%m-%d %H:%M %Z") once the user confirms.

Key source files (facts source of truth)

Verification

  • Cross-check every documented status code / field name against internal/discovery/server_test.go expectations.
  • Sanity-run go build ./... && go test -short ./internal/discovery/ ./internal/lease/ (no code changes expected — confirms docs match current behavior, not a stale tree).
  • Optionally lint the curl JSON bodies by piping each through jq . locally.