Files
homelab-frontend/tasks/3.5-capability-authorization.md
T
Story Crater BotandClaude Opus 5 058f11cf2b
CI / Test (push) Canceled after 0s
CI / Vet (push) Canceled after 0s
CI / Build (push) Canceled after 0s
CI / Security (govulncheck) (push) Canceled after 0s
chore: initial commit of Go API gateway
Baseline for the Kong replacement on api.riotpiao.com. Brings the working
tree under version control for the first time: gateway source, the task
board that drives the agent runs, test fixtures, and K8s manifests.

Anchor the gateway ignore rule to the repo root. Unanchored, "gateway"
also matched the cmd/gateway/ source directory, so the program entrypoint
was excluded from every commit.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-19 20:54:34 -07:00

1.9 KiB

3.5 — Capability authorization (RED)

Phase: 3 — Authentication Stage: RED Depends on: 3.2, 3.4

  • Beyond proving who the caller is, the gateway checks the token is permitted to invoke the capability being called
  • Each capability prefix — /v1/* for the model surface, and the future /sqs/*, /workflow/*, /cluster/*, /db/* — maps to a required grant, and the mapping is configuration
  • A token carrying a queue grant but no model grant is rejected on POST /v1/chat/completions with 403, not 401 — it authenticated fine, it is not permitted
  • A token with no recognised grant at all is rejected on every protected route
  • Rejections are RFC 9457 application/problem+json and state which capability was denied, without echoing the token or its claims verbatim
  • Denials are logged with the caller identity and the capability, and counted as a distinct rejection reason
  • A route with no required grant configured while auth is on fails startup rather than defaulting to allow-all

Write the failing tests first: a queue token must not invoke a GPU. The default on an unconfigured route is deny, because a fail-open authorization layer is worse than none — it reads as protection while granting everything.

Verify

curl -s -i localhost:8080/v1/chat/completions -H "authorization: Bearer $QUEUE_ONLY_TOKEN" \
  -H 'content-type: application/json' -d '{"model":"reasoning","messages":[]}'
# expected: 403, application/problem+json naming the denied capability, no upstream stub hit

curl -s -o /dev/null -w '%{http_code}\n' localhost:8080/v1/chat/completions \
  -H "authorization: Bearer $MODEL_TOKEN" -H 'content-type: application/json' \
  -d '{"model":"reasoning","messages":[]}'
# expected: 200

./gateway --config testdata/auth-on-route-without-grant.yaml; echo "exit=$?"
# expected: non-zero exit, stderr names the route missing a required grant