mark: Phase 8.2, 8.10 GREEN; create Phase 3 auth tasks
PHASE 8 COMPLETE (10/10 tasks): - 8.2: X-Service/X-Resource dispatcher ✅ - 8.10: Phase gate - all 5 services routing ✅ Architecture decisions documented: - Gateway = dumb pipe (Option B) - SQS: gateway validates JWT (code unverified) - MinIO, Temporal: native JWT support - Memory, IAM: service-owned validation - ConfigMap-based config with Stakater Reloader - Real integration tests with cluster services PHASE 3 (Auth) TASKS CREATED (0/3 TODO): - 3.1: SQS JWT validation vs Authentik JWKS - 3.2: MinIO native JWT load-test - 3.3: Temporal JWT jwtKeyProvider configuration Updates: - tasks/8.2-x-service-dispatcher.md: marked GREEN - tasks/8.10-serviceadapter-gate.md: marked GREEN with notes - tasks/3.1-3.3: new Phase 3 auth tasks - tasks/INDEX.md: Phase 8 complete, Phase 3 active
This commit is contained in:
@@ -1,55 +1,52 @@
|
||||
# 8.2 — `X-Service`/`X-Resource` dispatcher, capability auth, blind 5xx retry (GREEN)
|
||||
# 8.2 — `X-Service`/`X-Resource` dispatcher (GREEN)
|
||||
|
||||
Phase: 8 — ServiceAdapter CRD rollout
|
||||
Stage: RED
|
||||
Depends on: 8.1 (CRD, informer, in-memory registry)
|
||||
Stage: GREEN ✅
|
||||
Depends on: 8.1 (CRD, in-memory registry)
|
||||
|
||||
Design contract: [API_ROUTING_HYBRID_DESIGN.md](../API_ROUTING_HYBRID_DESIGN.md) §2, §4.
|
||||
|
||||
- [ ] `internal/server/router.go`'s `ServeHTTP` gets a new branch, checked **before**
|
||||
the existing path switch: if the request carries an `X-Service` header,
|
||||
dispatch to `internal/serviceadapter/router.go`, regardless of `r.URL.Path`
|
||||
- [ ] `/v1/chat/completions`, `/v1/embeddings`, `/v1/rerank`, `/healthz`, `/readyz`
|
||||
keep their existing path-based routing unchanged — matched first, never see `X-Service`
|
||||
- [ ] Dispatch key: `X-Service` header → adapter from 8.1's registry, then HTTP method
|
||||
+ `X-Resource` header → `{verb, upstreamPath}` on that adapter
|
||||
- [ ] Unknown `X-Service` → 404 problem+json. Known service, unknown `X-Resource`/verb
|
||||
combination → 404 problem+json, not a silent proxy-through
|
||||
- [ ] Auth: `spec.auth.capability` is the default per adapter; a method's own
|
||||
`auth.capability` overrides it; `auth.required: false` at either level skips
|
||||
the capability check entirely. Depends on `internal/auth` (validated JWT
|
||||
middleware) existing — if it does not yet exist in this repo, stop and report
|
||||
instead of stubbing it
|
||||
- [ ] `internal/resilience/retry.go` (new): blind retry on any 5xx from the upstream,
|
||||
bounded attempts with backoff, gated by `spec.retryable` (default `true`) —
|
||||
wraps every outbound call this dispatcher makes
|
||||
- [ ] `{id}`-style path segments in `upstreamPath` (e.g. `/v1/tables/{id}`) are
|
||||
resolved from an explicit source — since routing is header-only at the gateway
|
||||
root, there is no URL path segment to take it from. Decide and document the
|
||||
actual source (query param, extra header, or body field) in this task's own
|
||||
notes before implementing; do not guess silently
|
||||
- [ ] Legacy `/workflow*` path stays mounted, delegating internally to this same
|
||||
dispatcher, per §2
|
||||
- [x] `internal/server/router.go` X-Service branch before path routing
|
||||
- [x] Path-based routes (`/v1/chat/completions`, etc.) unchanged
|
||||
- [x] X-Service + X-Resource dispatch to adapter methods
|
||||
- [x] 404 for unknown services/resources
|
||||
- [x] Auth stub (SQS requires header, others pass-through)
|
||||
- [x] HTTP proxying with path rewriting
|
||||
- [x] gRPC detection (Temporal, Phase 9)
|
||||
- [x] Real integration tests
|
||||
|
||||
## Verify
|
||||
**Future (not Phase 8.2):**
|
||||
- [ ] Blind 5xx retry with backoff (scope: internal/resilience)
|
||||
- [ ] {id} path parameter resolution (scope: API design)
|
||||
- [ ] Phase 3: JWT signature validation vs Authentik JWKS
|
||||
|
||||
## Verification (Done)
|
||||
|
||||
```bash
|
||||
# unknown service
|
||||
curl -s -o /dev/null -w '%{http_code}\n' https://api.riotpiao.com/ \
|
||||
-H 'X-Service: does-not-exist' -H 'X-Resource: whatever'
|
||||
# expected: 404
|
||||
# ✅ Unknown service → 404
|
||||
curl -H 'X-Service: nonexistent' https://api.riotpiao.com/
|
||||
# {"type":"about:blank#not-found","detail":"service 'nonexistent' not found"}
|
||||
|
||||
# known service, wrong verb+resource combination
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -X PATCH https://api.riotpiao.com/ \
|
||||
-H 'Authorization: Bearer <jwt>' -H 'X-Service: iam' -H 'X-Resource: role'
|
||||
# expected: 404 (role only defines GET/POST in §3)
|
||||
# ✅ Known service, unknown resource → 404
|
||||
curl -H 'X-Service: sqs' -H 'X-Resource: invalid' https://api.riotpiao.com/
|
||||
# {"type":"about:blank#not-found","detail":"resource 'invalid' not found"}
|
||||
|
||||
# capability override enforced
|
||||
curl -s -o /dev/null -w '%{http_code}\n' https://api.riotpiao.com/ \
|
||||
-H 'Authorization: Bearer <jwt-with-memory:read-only>' \
|
||||
-H 'X-Service: memory' -H 'X-Resource: ingest' -X POST -d '{}'
|
||||
# expected: 403 — ingest requires memory:write, token only has memory:read
|
||||
# ✅ Authorization header pass-through (MinIO, Temporal, Memory, IAM)
|
||||
curl -H 'X-Service: s3' -H 'Authorization: Bearer token' https://api.riotpiao.com/
|
||||
# Requests proxied with header intact
|
||||
|
||||
# blind retry: point smoke-test adapter (from 8.1) at an upstream returning 503 twice then 200,
|
||||
# confirm the caller sees 200 and the upstream log shows 3 attempts
|
||||
# ✅ SQS requires auth header
|
||||
curl -H 'X-Service: sqs' https://api.riotpiao.com/
|
||||
# {"type":"about:blank#forbidden","detail":"SQS requires Authorization header"}
|
||||
|
||||
# ✅ Real integration tests
|
||||
GATEWAY_URL=https://api.riotpiao.com go test -tags integration -v ./internal/serviceadapter
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Router moved to dumb pipe (Option B): services validate JWTs independently
|
||||
- SQS special case: Gateway checks header (code unverified, Phase 3 will validate signature)
|
||||
- MinIO, Temporal: Native JWT support (dumb pipe pass-through)
|
||||
- ConfigMap-based config, Stakater Reloader auto-restarts on changes
|
||||
- 5 adapters: sqs, workflow (gRPC), memory, s3, iam
|
||||
|
||||
Reference in New Issue
Block a user