docs(06-06): complete personal-token rate-limit gap plan

This commit is contained in:
Jakub Zych
2026-09-20 13:31:41 +02:00
parent 3423da2222
commit 7f627961b1

View File

@@ -0,0 +1,114 @@
---
phase: 06-http-routing-auth-groups-and-rate-limiting
plan: 06
subsystem: api-security
tags: [rate-limiting, middleware-order, personal-token, security-review, httptest]
requires:
- phase: 06-http-routing-auth-groups-and-rate-limiting
provides: inv_token guard, InvScope, FixedWindowLimiter, fonoteka-api-token bucket, assembled route tests
provides:
- personal-token route order that rate-limits unauthenticated deny traffic before InvScope
- assembled-router proof of 401 through request 60 and exact 429 on request 61
- T-06-21 and T-06-22 security evidence with 21 threats closed and zero open
affects: [phase-07-user-plugin, phase-08-oauth, personal-token-routes, security-review]
tech-stack:
added: []
patterns:
- personal-token middleware order is inv_token -> throttle:<bucket> -> inv.scope:<scope>
- deny-path limiter regressions use one fresh surf.Assemble handler and stable RemoteAddr
key-files:
created: []
modified:
- plugins/golem15/fonoteka/routes.go
- plugins/golem15/fonoteka/routes_isolation_test.go
- .planning/phases/06-http-routing-auth-groups-and-rate-limiting/06-SECURITY-REVIEW.md
key-decisions:
- "Keep inv_token outermost so valid credentials populate bouncer.Credential before the limiter selects tok:<id>"
- "Place throttle before InvScope so missing and invalid credentials consume the per-IP deny-path budget"
patterns-established:
- "Any live route combining inv_token, a named throttle, and inv.scope declares them in that exact source order"
- "Rate-limit exhaustion tests exercise the real plugin set and production route rather than hand-composed middleware"
requirements-completed: [HTTP-04]
duration: 1h 29m
completed: 2026-09-20
---
# Phase 6 Plan 06: Personal-token deny-path rate-limit gap closure Summary
**Personal-token genres now preserves per-token keying while bounding unauthenticated 401 traffic at 60 requests per minute, with assembled-route and security-review evidence**
## Performance
- **Duration:** 1h 29m
- **Started:** 2026-09-20T10:01:25Z
- **Completed:** 2026-09-20T11:30:57Z
- **Tasks:** 2
- **Files modified:** 3
## Accomplishments
- Reordered the live personal-token genres middleware to `inv_token`, `throttle:fonoteka-api-token`, `inv.scope:read`, preserving valid-token `tok:<id>` keys while limiting unauthenticated fallback traffic by client IP.
- Added `TestPersonalTokenGenresUnauthenticatedRequestsAreRateLimited`, which drives 61 same-IP requests through one fresh handler returned by `surf.Assemble`: requests 1-60 retain the PHP-compatible 401 body and request 61 receives the exact 429 body and exhausted-limit headers.
- Extended `06-SECURITY-REVIEW.md` through Plan 06-06 with T-06-21/T-06-22, 21 closed threats, zero open, and no new accepted risk.
- Passed the targeted regression, `go vet ./...`, and repository-wide `go test ./... -short` in `fonoteka.go`.
## Task Commits
Each task was committed atomically:
1. **Task 1: Repair personal-token middleware order and prove deny-path throttling** - `33f721d` (fix, fonoteka.go)
2. **Task 2: Extend the Phase 6 security review through gap closure** - `3423da2` (docs, summercms.go)
**Plan metadata:** (this commit)
## Files Created/Modified
- `plugins/golem15/fonoteka/routes.go` - Declares the live personal-token middleware in credential, limiter, then scope order.
- `plugins/golem15/fonoteka/routes_isolation_test.go` - Proves the assembled production route returns 401 through request 60 and exact 429 on request 61.
- `.planning/phases/06-http-routing-auth-groups-and-rate-limiting/06-SECURITY-REVIEW.md` - Maps T-06-21/T-06-22 to the runtime-order invariant and regression evidence.
## Decisions Made
- `inv_token` remains before the limiter so valid credentials populate `bouncer.Credential` before the bucket closure resolves `tok:<id>`.
- `throttle:fonoteka-api-token` remains before `inv.scope:read` so missing and invalid credentials consume the per-IP fallback bucket before the scope gate returns 401.
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- The delegated executor stopped returning progress after partially editing the sibling `fonoteka.go` repository. After the configured stall threshold and user-approved recovery, execution switched inline; the partial diff was inspected, retained, validated, and committed without duplication.
- The sandboxed full test run could not access the Docker daemon used by the parity harness. The same required command passed after rerunning with approved Docker access.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- HTTP-04's verification blocker and code-review CR-01 are directly closed.
- Phase 6 is ready for re-verification; no Plan 06-06 implementation blockers remain.
## Self-Check: PASSED
- FOUND: `../fonoteka.go/plugins/golem15/fonoteka/routes.go`
- FOUND: `../fonoteka.go/plugins/golem15/fonoteka/routes_isolation_test.go`
- FOUND: `.planning/phases/06-http-routing-auth-groups-and-rate-limiting/06-SECURITY-REVIEW.md`
- FOUND: `33f721d` in `fonoteka.go`
- FOUND: `3423da2` in `summercms.go`
- PASS: targeted assembled-router regression
- PASS: `go vet ./...` in `fonoteka.go`
- PASS: `go test ./... -short` in `fonoteka.go`
- PASS: T-06-21/T-06-22 evidence and audit total 21 closed / 0 open
---
*Phase: 06-http-routing-auth-groups-and-rate-limiting*
*Completed: 2026-09-20*