Files
summercms/.planning/phases/06-http-routing-auth-groups-and-rate-limiting/06-DISCUSSION-LOG.md
2026-09-19 14:04:36 +02:00

158 lines
6.2 KiB
Markdown

# Phase 6: HTTP routing, auth groups and rate limiting - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-09-19
**Phase:** 6-HTTP routing, auth groups and rate limiting
**Areas discussed:** Bucket scope & limiter, Token guard depth, Outbound fetch guard, Route surface & OAuth exempt
---
## Bucket scope & limiter
### Which buckets does Phase 6 define and test?
| Option | Description | Selected |
|--------|-------------|----------|
| Fonoteka 5 + mechanism | Five fonoteka named buckets plus inline throttle form; other plugins' buckets land with those plugins; roadmap "seven" corrected | ✓ |
| All 11 now | Every named bucket of every ported plugin declared in Phase 6 | |
| Fonoteka 5 + user 3 | Add the user stub's three buckets now | |
### Wire behavior
| Option | Description | Selected |
|--------|-------------|----------|
| Exact Laravel parity | Fixed window, X-RateLimit-* headers, Retry-After/Reset on 429, same 429 body | ✓ |
| Same limits, Go-native algorithm | Token bucket/sliding window, same headers, edge bursts may differ | |
| You decide | Researcher picks cheapest faithful option | |
### Counter store
| Option | Description | Selected |
|--------|-------------|----------|
| In-process, stdlib | Mutex map + sweep behind surf.Limiter, Store interface for later | ✓ |
| In-process via otter (cooler) | Adds otter and the cooler package now | |
| Postgres-backed | Survives restarts, DB write per limited request | |
### Client IP
| Option | Description | Selected |
|--------|-------------|----------|
| Trusted-proxy config | XFF honored only from trusted CIDRs, rightmost untrusted hop | ✓ |
| RemoteAddr only | Ignore forwarding headers | |
| Always trust X-Forwarded-For | Spoofable if directly reachable | |
**Notes:** Scouting found five named buckets in fonoteka/routes.php, not seven; eight more live in user, feedback, websockets and journal plugins.
---
## Token guard depth
### How real is the personal-token guard?
| Option | Description | Selected |
|--------|-------------|----------|
| Real verifier, test-only minting | ApiToken lookup with PHP rules; tokens inserted by tests/seed hooks; Phase 7 adds CRUD | ✓ |
| Interface + fake guard | Real lookup waits for AUTH-03 | |
### Where does inv.scope land?
| Option | Description | Selected |
|--------|-------------|----------|
| Phase 6, in golem15.fonoteka | It is the token group's auth step; exact 401/403 bodies; parameterized names in surf | ✓ |
| Phase 7 with AUTH-03 | Guard-only auth in Phase 6 | |
### Guard registry shape
| Option | Description | Selected |
|--------|-------------|----------|
| Named guards in bouncer | Registry by name, Guard interface, single user accessor plus credential accessor, fail boot on duplicates | ✓ |
| Just named middleware | Convention only | |
| You decide | Planner picks | |
### OAuth bearer guard slot
| Option | Description | Selected |
|--------|-------------|----------|
| Name reserved, no implementation | Registry proven with two real guards | ✓ |
| Stub that always 401s | Mount /oauth/mcp behind a rejecting guard | |
---
## Outbound fetch guard
### Host policy
| Option | Description | Selected |
|--------|-------------|----------|
| Per-caller policy, both modes | AllowHosts and/or PublicOnly; private-IP block always on | ✓ |
| Allow-list only | Manual cover URL would need an allow-list (behavior change) | |
### DNS rebinding
| Option | Description | Selected |
|--------|-------------|----------|
| Dial-time IP check | net.Dialer Control validates the connected IP | ✓ |
| Match PHP: pre-resolve only | Keep the gap, note the risk | |
### Scope
| Option | Description | Selected |
|--------|-------------|----------|
| Framework helper + tests only | Fetcher ports are Phase 12/14 | ✓ |
| Helper + both fonoteka fetchers | Port ManualCoverUrlFetcher and CoverImporter now | |
### Limits source
| Option | Description | Selected |
|--------|-------------|----------|
| Caller passes them; no framework default-open | Zero value fails construction | |
| Framework defaults in config | http.fetch.* defaults, overridable per call | ✓ |
**Notes:** User chose framework config defaults over the recommended caller-required values. Recorded with the constraint that an effective limit can never be unlimited.
---
## Route surface & OAuth exempt
### Route surface
| Option | Description | Selected |
|--------|-------------|----------|
| Groups + proving routes only | All groups as builders; only real handlers mounted; genres under both prefixes; no 501 shells | ✓ |
| All 154 as 501 shells | Whole surface registered now | |
| Shells for auth-sensitive routes only | Public, token and OAuth groups fully registered | |
### OAuth exemption
| Option | Description | Selected |
|--------|-------------|----------|
| Group flag + boot-time inspection | Raw group refuses house middleware, bare 500 on recover, route table + route:list | ✓ |
| Separate mux mounted beside the pipeline | Own ServeMux and minimal chain | |
| You decide | Planner picks | |
### Response conventions
| Option | Description | Selected |
|--------|-------------|----------|
| Framework helpers + types, no envelope middleware | JSON writer, Carbon time, nullable bool, never-nil slice | ✓ |
| Helpers live in fonoteka.go | Framework stays neutral | |
### CORS and body limits
| Option | Description | Selected |
|--------|-------------|----------|
| Path-scoped CORS from config, exact parity | Reproduce cors.php, no CORS on /_fonoteka/api; body limits from real deployment values | ✓ |
| CORS on everything | Header difference on the JWT group | |
---
## Claude's Discretion
Package homes and names, sweep interval and Store shape, inline throttle key composition (must match Laravel), swag/openapi-typescript workflow and drift check, locale stage depth, how group mutual exclusivity is asserted, plan count and split.
## Deferred Ideas
User/feedback/websockets buckets with their plugins; shared limiter store after v1; cooler/otter cache when needed; cover fetcher ports in Phase 12/14; OAuth guard and handlers in Phase 8; token CRUD in Phase 7.