ROADMAP mode is mvp but the goal is not a User Story (user-story.validate valid=false); this requested re-verification uses the technical roadmap contract.
previous_status
previous_score
gaps_closed
gaps_remaining
regressions
gaps_found
11/12
The live personal-token chain is now inv_token -> throttle:fonoteka-api-token -> inv.scope:read; requests 1-60 return 401 and request 61 returns 429.
Rate-limit admission is non-atomic and inline anonymous keys trust r.Host.
The SSRF guard misses private IPv4 embedded in NAT64/6to4 addresses.
Panic recovery cannot replace a partially committed response.
InvScope appends a newline to PHP-compatible 401/403 bodies.
truth
status
reason
artifacts
missing
All five named buckets and inline throttles enforce their limits and documented keys under concurrent traffic.
failed
06-06 fixes middleware order, but TooManyAttempts and Hit remain separately locked, so concurrent requests can all pass the threshold. Inline anonymous keys also include attacker-controlled Host.
path
issue
surf/limiter.go
Lines 95-108 are check-then-increment; lines 160-164 use r.Host in the key.
path
issue
surf/limiter_store.go
The Store has no atomic attempt/admission operation.
Atomic threshold check plus increment
Server-controlled inline key prefix instead of r.Host
Concurrent Max=1 and Host-rotation tests
truth
status
reason
artifacts
missing
The outbound fetch helper rejects private/reserved destinations in every supported address representation.
failed
Addr.Unmap handles mapped IPv4 only. NAT64 and 6to4 values embedding loopback, RFC1918, or metadata IPv4 miss both current tables.
path
issue
fetchguard/ip.go
No classification for 64:ff9b::/96, 64:ff9b:1::/48, or 2002::/16.
path
issue
fetchguard/fetch.go
Dial control only Unmaps before classification.
Decode/recheck embedded IPv4 or reject unsafe transition forms
Transition-address security tests
truth
status
reason
artifacts
missing
Panics yield only the promised bare raw 500 or opaque house 500, including after a partial write.
failed
Recovery writes after the wrapped handler. Once status/body is committed, the fallback 500 is ignored and partial data remains. Existing tests panic before writing.
path
issue
surf/router.go
recoverJSON/recoverBare write directly to the original ResponseWriter.
path
issue
surf/router_test.go
No partial-write-then-panic coverage.
Buffer/discard responses covered by the opaque recovery contract, or explicitly narrow raw semantics
Partial-write panic tests for both route kinds
truth
status
reason
artifacts
missing
Personal-token 401/403 bodies are byte-identical to PHP TokenScope.
failed
InvScope uses json.Encoder.Encode, which appends a newline; the tests hide it with strings.TrimSpace.
Use wire.WriteJSON (or equivalent no-newline writer)
Assert exact 401 and 403 bytes
truth
addressed_in
evidence
Public/onboarding groups have reachable unauthenticated handlers.
Phase 13
Phase 13 explicitly ports onboarding/public/invitation routes and public buckets.
truth
addressed_in
evidence
Unknown and malformed ids on ownership-scoped resources both return 404.
Phase 12
Phase 12 owns Collections and Albums; Phase 6 supplies the tested constraint primitive.
truth
addressed_in
evidence
Manual-cover and Discogs production callers use fetchguard.
Phase 12 / Phase 14
Those phases own cover handling and Discogs integration; 06-04 explicitly shipped helper-only.
Phase 6: HTTP routing, auth groups and rate limiting Verification Report
Phase Goal: The three mutually exclusive auth groups share handlers with correct subsets, rate-limit buckets are ported 1:1, OAuth/RFC routes are structurally raw, and the auth registry, limiter, and SSRF fetch helper form secure shared infrastructure.
Verified: 2026-09-20T11:53:11Z
Status: gaps_found
Re-verification: Yes — 06-06 closes the prior middleware-order gap, but current code-review findings expose goal-level defects.
ROADMAP marks this phase mode: mvp, but gsd-sdk query user-story.validate returns valid=false: the goal is not in As a …, I want …, so that …. form. User Flow Coverage cannot be generated honestly; this report retains the requested technical verification framing.
Goal Achievement
Observable Truths
#
Truth
Status
Evidence
1
JWT and personal-token groups share the genres handler; subsets are mutually exclusive
✓ VERIFIED (later groups deferred)
routes.go:10-16 binds one handler twice; full route-table isolation test passes.
2
Five named buckets and inline throttles enforce documented limits/keys, including stacking
✗ FAILED
06-06 order and request-61 test pass, but limiter.go:95-108 is non-atomic and inline keys trust r.Host (current REVIEW CR-01/CR-02).
3
Response conventions and raw/house panic behavior hold
✗ FAILED
Wire helpers and structural raw refusal pass. Partial-write panic breaks the promised 500 boundary, and InvScope adds \n while its test trims it (CR-04/WR-11).
4
Fetch helper is an SSRF boundary with allow-list, private-IP rejection, cap, timeout
✗ FAILED
Ordinary ranges, cap, timeout, and redirects are covered; NAT64/6to4 embedded private IPv4 bypasses classification (CR-03).
5
OpenAPI/type validation, path CORS, and production body limits exist
✓ VERIFIED (warnings)
OpenAPI 3 artifact, CORS wiring, and 134217728-byte config are present. Document omits the second live route/auth schemes (WR-07).
6
Guard registry unifies JWT/personal-token users and preserves exact error contracts
✗ FAILED
Registry/accessor are wired; exact personal-token bytes fail due Encoder newline.
7
name:param middleware resolves through factories
✓ VERIFIED
strings.Cut factory path is used by throttle/body.limit/inv.scope.
Source trace: separate check then hit; no concurrency test
multiple callers can pass
✗ FAIL
Partial-write panic
Source trace: recovery writes to committed writer
original response cannot be replaced
✗ FAIL
Probe Execution
No probe is declared and no scripts/*/tests/probe-*.sh exists.
Requirements Coverage
All seven PLAN IDs match the Phase 6 mappings; none is orphaned. REQUIREMENTS.md is internally inconsistent: HTTP-04 is checked complete at line 56 but its traceability row says In Progress.
Requirement
Status
Evidence
HTTP-03
✓ SATISFIED (public handlers deferred)
Shared handler and isolation exist.
HTTP-04
✗ BLOCKED
Route order fixed; atomic admission and stable inline keys remain broken.
HTTP-05
✓ SATISFIED
Registry/unified accessor exist.
HTTP-06
✗ BLOCKED
Recovery can retain/leak partial response rather than promised 500.
HTTP-07
✗ BLOCKED
Transition-address private targets evade the SSRF boundary.
No unreferenced TBD, FIXME, or XXX markers were found. Disconfirmation pass: HTTP-04 is only sequentially correct; panic tests cover panic-before-write only; fetch tests omit transition-address targets.
Human Verification Required
None. The production body-size checkpoint is already recorded. Current failures are programmatically observable and require code/test changes.
Gaps Summary
Plan 06-06 closes the original middleware-order defect. The phase still fails its security-load-bearing goal: rate limiting is bypassable under concurrency (and inline through Host rotation), fetchguard misses transition-address private targets, recovery cannot uphold the opaque/bare response promise after partial output, and the token scope response is not byte-compatible. These primitives belong to Phase 6 and later phases only consume them, so they are not deferred.
Verified: 2026-09-20T11:53:11ZVerifier: the agent (gsd-verifier)