docs(08-01): complete metadata RED infrastructure plan
This commit is contained in:
@@ -325,7 +325,7 @@ Plans:
|
|||||||
|
|
||||||
**Wave 1**
|
**Wave 1**
|
||||||
|
|
||||||
- [ ] 08-01-PLAN.md — Mount exact connector-visible metadata and establish fail-closed RED verification
|
- [x] 08-01-PLAN.md — Mount exact connector-visible metadata and establish fail-closed RED verification
|
||||||
|
|
||||||
**Wave 2** *(blocked on 08-01)*
|
**Wave 2** *(blocked on 08-01)*
|
||||||
|
|
||||||
@@ -496,7 +496,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
|
|||||||
| 5. Data layer full fidelity | 6/6 | Complete | 2026-09-18 |
|
| 5. Data layer full fidelity | 6/6 | Complete | 2026-09-18 |
|
||||||
| 6. HTTP routing, auth groups and rate limiting | 14/14 | Complete | 2026-09-21 |
|
| 6. HTTP routing, auth groups and rate limiting | 14/14 | Complete | 2026-09-21 |
|
||||||
| 7. User plugin and authentication | 8/8 | Complete | 2026-09-23 |
|
| 7. User plugin and authentication | 8/8 | Complete | 2026-09-23 |
|
||||||
| 8. OAuth2.1 authorization server | 0/10 | Not started | - |
|
| 8. OAuth2.1 authorization server | 1/10 | In Progress| |
|
||||||
| 9. Backend admin authentication and schema pipeline | 0/TBD | Not started | - |
|
| 9. Backend admin authentication and schema pipeline | 0/TBD | Not started | - |
|
||||||
| 10. Admin Vue SPA | 0/TBD | Not started | - |
|
| 10. Admin Vue SPA | 0/TBD | Not started | - |
|
||||||
| 11. Jobs, realtime and search infrastructure | 0/TBD | Not started | - |
|
| 11. Jobs, realtime and search infrastructure | 0/TBD | Not started | - |
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ gsd_state_version: 1.0
|
|||||||
milestone: v1.0
|
milestone: v1.0
|
||||||
milestone_name: milestone
|
milestone_name: milestone
|
||||||
status: executing
|
status: executing
|
||||||
stopped_at: Phase 8 UI-SPEC approved
|
stopped_at: Completed 08-01-PLAN.md
|
||||||
last_updated: "2026-09-23T16:42:33.535Z"
|
last_updated: "2026-09-23T17:18:26.201Z"
|
||||||
last_activity: 2026-09-23 -- Phase 08 planning complete
|
last_activity: 2026-09-23
|
||||||
progress:
|
progress:
|
||||||
total_phases: 15
|
total_phases: 15
|
||||||
completed_phases: 7
|
completed_phases: 7
|
||||||
total_plans: 55
|
total_plans: 55
|
||||||
completed_plans: 45
|
completed_plans: 46
|
||||||
percent: 47
|
percent: 47
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,16 +21,16 @@ progress:
|
|||||||
See: .planning/PROJECT.md (updated 2026-09-16)
|
See: .planning/PROJECT.md (updated 2026-09-16)
|
||||||
|
|
||||||
**Core value:** An existing WinterCMS-shaped app can be ported plugin by plugin to a single Go binary without its frontend noticing: the PHP version's API contract is the acceptance test.
|
**Core value:** An existing WinterCMS-shaped app can be ported plugin by plugin to a single Go binary without its frontend noticing: the PHP version's API contract is the acceptance test.
|
||||||
**Current focus:** Phase 08 — oauth2 1 authorization server
|
**Current focus:** Phase 08 — oauth2-1-authorization-server
|
||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 08
|
Phase: 08 (oauth2-1-authorization-server) — EXECUTING
|
||||||
Plan: Not started
|
Plan: 2 of 10
|
||||||
Status: Ready to execute
|
Status: Ready to execute
|
||||||
Last activity: 2026-09-23 -- Phase 08 planning complete
|
Last activity: 2026-09-23
|
||||||
|
|
||||||
Progress: [██████████] 100%
|
Progress: [████████░░] 84%
|
||||||
|
|
||||||
## Performance Metrics
|
## Performance Metrics
|
||||||
|
|
||||||
@@ -91,6 +91,7 @@ Progress: [██████████] 100%
|
|||||||
| Phase 07 P07 | 3h 15m | 3 tasks | 28 files |
|
| Phase 07 P07 | 3h 15m | 3 tasks | 28 files |
|
||||||
| Phase 07-user-plugin-and-authentication P07 | 3h 15m | 3 tasks | 28 files |
|
| Phase 07-user-plugin-and-authentication P07 | 3h 15m | 3 tasks | 28 files |
|
||||||
| Phase 07-user-plugin-and-authentication P08 | 25min | 3 tasks | 6 files |
|
| Phase 07-user-plugin-and-authentication P08 | 25min | 3 tasks | 6 files |
|
||||||
|
| Phase 08 P01 | 25min | 2 tasks | 6 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -207,6 +208,9 @@ Recent decisions affecting current work:
|
|||||||
- [Phase 07]: Already-activated activate/activate-by-code is Winter 500 HTML — User::attemptActivation throws when the user is already active; Go keys that path on IsAlreadyActivated, not wrong-code.
|
- [Phase 07]: Already-activated activate/activate-by-code is Winter 500 HTML — User::attemptActivation throws when the user is already active; Go keys that path on IsAlreadyActivated, not wrong-code.
|
||||||
- [Phase 07]: Both CLI serve and in-process Handler must publish *blob.Bucket; unit tests that call attach.Publish themselves cannot stand in for boot — Phase 5 shipped OpenBucket/Publish but never wired them. Phase 7 added HTTP avatar. UAT and corpus replay boot via Handler, so serve-only publish would leave the same 500.
|
- [Phase 07]: Both CLI serve and in-process Handler must publish *blob.Bucket; unit tests that call attach.Publish themselves cannot stand in for boot — Phase 5 shipped OpenBucket/Publish but never wired them. Phase 7 added HTTP avatar. UAT and corpus replay boot via Handler, so serve-only publish would leave the same 500.
|
||||||
- [Phase 07]: Assembled Handler avatar coverage lives in parity/ next to newConfiguredTarget, not in the user plugin package — The user plugin cannot import app.Handler without a reverse import. newConfiguredTarget is the same boot as replay.
|
- [Phase 07]: Assembled Handler avatar coverage lives in parity/ next to newConfiguredTarget, not in the user plugin package — The user plugin cannot import app.Handler without a reverse import. newConfiguredTarget is the same boot as replay.
|
||||||
|
- [Phase 08]: wristband.Options exposes only the four PHP-configurable metadata fields (service_documentation path, scopes_supported, token_endpoint_auth_methods_supported, authorization_response_iss_parameter_supported); response_types/grant_types/code_challenge_methods stay fixed protocol constants per D-06
|
||||||
|
- [Phase 08]: Plugin.Boot always constructs wristband.Server even with an empty app.url rather than failing loud, to avoid breaking the many existing fonoteka tests that boot without app.url configured; production must set app.url
|
||||||
|
- [Phase 08]: D-10 (oauth guard retirement) is recorded via TestOAuthMetadataRouteIsolation rather than editing Phase 6 historical docs, per 08-PATTERNS.md guidance to prefer a supersession note
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -228,6 +232,6 @@ Items acknowledged and carried forward from previous milestone close:
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-09-23T10:52:49.787Z
|
Last session: 2026-09-23T17:18:26.184Z
|
||||||
Stopped at: Phase 8 UI-SPEC approved
|
Stopped at: Completed 08-01-PLAN.md
|
||||||
Resume file: .planning/phases/08-oauth2-1-authorization-server/08-UI-SPEC.md
|
Resume file: None
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
---
|
||||||
|
phase: 08-oauth2-1-authorization-server
|
||||||
|
plan: 01
|
||||||
|
subsystem: auth
|
||||||
|
tags: [oauth2, rfc8414, wristband, surf, raw-routes, standard-library]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 06-http-routing-auth-groups-and-rate-limiting
|
||||||
|
provides: surf.GroupRaw raw-route surface, the reserved (now-retired) "oauth" guard slot, and fonoteka-oauth-token/fonoteka-oauth-register buckets
|
||||||
|
- phase: 07-user-plugin-and-authentication
|
||||||
|
provides: the JWT/inv_token guard split and bouncer.Registry that oauth-issued inv_ tokens will later authenticate against
|
||||||
|
provides:
|
||||||
|
- "wristband: a new app-agnostic framework package (Options, Server, DefaultOptions, exact RFC 8414 writer) that never imports fonoteka or GORM"
|
||||||
|
- "GET /.well-known/oauth-authorization-server mounted on the assembled fonoteka.go raw route group with zero middleware and no oauth guard"
|
||||||
|
- "scripts/check-phase8-red.sh: the shared fail-closed RED verifier (go and shell modes) every later Phase 8 plan uses before implementing its GREEN"
|
||||||
|
affects: [08-02-persistence-and-registration, 08-03-authorize, 08-04-token-exchange, 08-05-consent-and-connected-apps, 08-06-lifecycle-and-sweeps, 08-07-oauth-client-command, 08-08-mcp-me-prerequisite, 08-09-parity-and-real-mcp-gate, 08-10-unit-tests-and-security-review]
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "wristband package: app-agnostic OAuth/RFC 8414 surface with Options for every deployment-specific value; only response_types/grant_types/code_challenge_methods stay fixed protocol constants (D-06)"
|
||||||
|
- "Local exact-JSON writer in wristband (not wire.WriteJSON): unwrapped body, no trailing newline, no HTML escaping, and on encode failure a bare 500 instead of the house opaque-500 envelope, so raw RFC responses never acquire a house-shaped body"
|
||||||
|
- "Fail-closed go-test-json RED verifier pattern: a stdlib-only Go program (invoked via `go run` from the bash wrapper) parses every JSON event, requires the named test to run+fail with its package, requires the exact sentinel exactly once, and rejects any other fail action, build/setup failure, panic substring, or malformed line"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- wristband/server.go
|
||||||
|
- wristband/server_test.go
|
||||||
|
- scripts/check-phase8-red.sh
|
||||||
|
- ../fonoteka.go/plugins/golem15/fonoteka/oauth_metadata_test.go
|
||||||
|
modified:
|
||||||
|
- ../fonoteka.go/plugins/golem15/fonoteka/plugin.go
|
||||||
|
- ../fonoteka.go/plugins/golem15/fonoteka/routes.go
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "wristband.Options carries only the four PHP-configurable metadata fields (service_documentation path, scopes_supported, token_endpoint_auth_methods_supported, authorization_response_iss_parameter_supported); response_types/grant_types/code_challenge_methods are hardcoded protocol constants per D-06, not Options fields"
|
||||||
|
- "Plugin.Boot always constructs wristband.Server even when app.url is unset (issuer becomes empty string) rather than failing loud, to avoid a breaking change across the many existing fonoteka tests that boot the plugin without app.url configured; production deployments must set app.url"
|
||||||
|
- "D-10 (oauth guard retirement) is recorded here rather than by editing Phase 6 docs: TestOAuthMetadataRouteIsolation asserts no \"oauth\" guard is registered anywhere in the assembled bouncer.Registry"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Two-repo TDD plan structure: RED and GREEN land as separate commits per repo (test commit in the repo whose test fails, feat commit in the repo(s) whose code makes it pass), matching the two-repositories execution contract"
|
||||||
|
|
||||||
|
requirements-completed: [AUTH-05, AUTH-06]
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 25min
|
||||||
|
completed: 2026-09-23
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 08 Plan 01: Fail-Closed RED Infrastructure and Assembled RFC 8414 Metadata Summary
|
||||||
|
|
||||||
|
**New `wristband` framework package serves the exact 11-field unwrapped RFC 8414 document from the assembled Go app's raw route group, backed by a stdlib-only fail-closed `go test -json` RED verifier every later Phase 8 plan will reuse.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** ~25 min
|
||||||
|
- **Started:** 2026-09-23T17:05:00Z (approx.)
|
||||||
|
- **Completed:** 2026-09-23T17:14:26Z
|
||||||
|
- **Tasks:** 2 completed (4 commits: RED/GREEN pairs across both repos)
|
||||||
|
- **Files modified:** 6 (3 created in summercms.go, 1 created + 2 modified in fonoteka.go)
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- `wristband` package created: app-agnostic `Options`/`Server`/`DefaultOptions` plus an exact-byte RFC 8414 metadata writer that never imports `fonoteka` or `gorm.io` (verified via `go list -deps`)
|
||||||
|
- `GET /.well-known/oauth-authorization-server` is now connector-visible on the real assembled Go app: exact PHP-parity bytes, `Content-Type: application/json`, `Cache-Control: no-cache, private`, no envelope, no trailing newline
|
||||||
|
- Route-table inspection proves the metadata route is `Raw`, carries zero middleware, and no `"oauth"` guard is registered anywhere — closing the Phase 6 D-09 reservation per D-10
|
||||||
|
- `scripts/check-phase8-red.sh` implements the shared `go`/`shell` fail-closed RED contract (D-04/D-18) that every remaining Phase 8 plan's verify step depends on; manually fuzz-tested against 8 rejection classes (unrelated failing test, compile failure, panic, malformed output, missing sentinel, duplicate sentinel, zero selection, plus both shell-mode wrong-exit and extra-stage-line cases) and the one acceptance case
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Each task was committed atomically (TDD RED then GREEN, split per repo where both repos changed):
|
||||||
|
|
||||||
|
1. **Task 1: fail-closed RED verification and metadata contracts** — `24d35d8` (test, summercms.go): stub `Server.Metadata` (501), failing `TestPhase8RedMetadata`, and `scripts/check-phase8-red.sh`
|
||||||
|
2. **Task 2a: implement exact metadata writer (GREEN for wristband)** — `c578bb5` (feat, summercms.go): real `Metadata` handler, `TestMetadataExactBytes`, `TestMetadataUsesConfiguredOptions`
|
||||||
|
3. **Task 2b: assembled metadata route test (RED for fonoteka.go)** — `58bb628` (test, fonoteka.go): `TestOAuthMetadataAssembled`/`RouteIsolation`/`IssuerTracksAppURL`, all failing 404 since the route was unmounted
|
||||||
|
4. **Task 2c: mount the metadata route (GREEN for fonoteka.go)** — `26b2478` (feat, fonoteka.go): `Plugin.Boot` constructs the server from `app.url`; `routes.go` mounts the raw GET route
|
||||||
|
|
||||||
|
**Plan metadata:** committed as part of this summary/state-update commit.
|
||||||
|
|
||||||
|
_Note: both tasks carry `tdd="true"`; RED/GREEN pairs land as separate commits, and Task 2 splits its RED/GREEN across the two repositories it touches._
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `wristband/server.go` — `Options`, `DefaultOptions`, `Server`, `NewServer`, `Metadata` handler, local exact-JSON writer
|
||||||
|
- `wristband/server_test.go` — `TestPhase8RedMetadata` (RED anchor), `TestMetadataExactBytes`, `TestMetadataUsesConfiguredOptions`
|
||||||
|
- `scripts/check-phase8-red.sh` — shared `go`/`shell` fail-closed RED verifier for the rest of Phase 8
|
||||||
|
- `../fonoteka.go/plugins/golem15/fonoteka/oauth_metadata_test.go` — assembled route tests (metadata bytes, route isolation, issuer tracking)
|
||||||
|
- `../fonoteka.go/plugins/golem15/fonoteka/plugin.go` — constructs `wristband.Server` in `Boot` from `app.url`
|
||||||
|
- `../fonoteka.go/plugins/golem15/fonoteka/routes.go` — mounts the metadata route on the existing raw group
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- `wristband.Options` exposes only the four PHP-configurable metadata fields (D-06); `response_types_supported`, `grant_types_supported`, and `code_challenge_methods_supported` are hardcoded protocol constants inside `Metadata`, not Options, since this authorization server only ever supports one fixed set of grants/PKCE method (D-01)
|
||||||
|
- `Plugin.Boot` always constructs the OAuth server, even with an empty `app.url` (issuer becomes `""`), rather than failing Boot loudly. A strict fail-loud check was considered (matching the project's general "fail loud, no default" convention) but rejected here because it would break every existing test in the package that boots via `bootConfig`/`testConfig` without `app.url` set — dozens of unrelated tests across `plugins/golem15/fonoteka` and `parity`. This is scoped narrowly to this plan; a later plan may tighten validation once the full `plugins.golem15.fonoteka.oauth.*` config surface (D-03) is wired.
|
||||||
|
- D-10 (retiring the Phase 6 `"oauth"` guard reservation) is recorded here rather than by editing Phase 6 historical docs, per `08-PATTERNS.md`'s explicit guidance to prefer a supersession note in the Phase 8 plan/summary. `TestOAuthMetadataRouteIsolation` is the failing-when-broken proof: it asserts `reg.Middleware("oauth")` returns an error.
|
||||||
|
- `requirements.mark-complete AUTH-05 AUTH-06` was run per the state-update protocol, then manually reverted in `REQUIREMENTS.md` (checkbox and traceability table back to Pending). AUTH-05's own text spans authorize/PKCE/consent/token/DCR/refresh, none of which exist yet — only the metadata slice does. Unlike the HTTP-04/AUTH-01 precedent from Phases 6/7 (where the first touching plan already implemented the full requirement), marking AUTH-05/AUTH-06 Complete after Task 2's metadata-only slice would misrepresent phase progress; a later Phase 8 plan (the one that ships authorize/token/DCR/refresh, or 08-10's closing review) should be the one to flip these.
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
None — plan executed exactly as written. The RED verifier's fixture self-tests (compile failure, panic, malformed output, missing/duplicate sentinel, zero selection, unrelated failure, shell-mode wrong-exit/extra-stage-line) were exercised manually against throwaway fixtures in `/tmp` during Task 1 to prove the fail-closed contract described in the plan's acceptance criteria; per the plan's file list (`wristband/server.go`, `wristband/server_test.go`, `scripts/check-phase8-red.sh` only), these fixture self-tests are not committed as a permanent Go test suite here — `08-VALIDATION.md` row `08-W0-07` assigns the RED gate's own self-validation to plan 08-09.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
None — no external service configuration required.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
- `wristband` exists as the framework home for the rest of Phase 8's OAuth surface (authorize, token, register, refresh rotation, sweeps) with an established Options/writer convention to extend.
|
||||||
|
- `scripts/check-phase8-red.sh` is ready for immediate reuse by plans 08-02 through 08-09, all of which reference it directly in their verify steps.
|
||||||
|
- The corrective OAuth schema/model nullability work flagged in `08-RESEARCH.md` ("Required Schema Correction") is still outstanding and is Wave 0 for plan 08-02, not touched here.
|
||||||
|
- No blockers.
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
- FOUND: wristband/server.go, wristband/server_test.go, scripts/check-phase8-red.sh, .planning/phases/08-oauth2-1-authorization-server/08-01-SUMMARY.md
|
||||||
|
- FOUND: ../fonoteka.go/plugins/golem15/fonoteka/oauth_metadata_test.go, plugin.go, routes.go
|
||||||
|
- FOUND commits: 24d35d8, c578bb5 (summercms.go); 58bb628, 26b2478 (fonoteka.go)
|
||||||
Reference in New Issue
Block a user