158 lines
6.2 KiB
Markdown
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.
|