--- phase: 09-backend-admin-authentication-and-schema-pipeline plan: 11 subsystem: admin tags: [permissions, navigation, settings, postgres, authorization, metadata] requires: - phase: 09-backend-admin-authentication-and-schema-pipeline provides: Backend guard, compiled controllers, forms, lists, CRUD, and relations provides: - Exact Fonoteka permission catalog with source role assignments - Stable permission-filtered navigation and settings discovery endpoints - Localized singleton settings schema plus transactional GET/PUT service affects: [09-backend-admin-authentication-and-schema-pipeline, phase-10-spa, admin-api] actuals: tokens: 30000 tasks: 3 commits: 3 tech-stack: added: [] patterns: - "Plugin permission contributions are compiled once and validate every controller, relation, navigation, and settings reference" - "Backend role defaults are merged at principal resolution without mutating role rows" - "Settings replay compares projected values and skips persistence when unchanged" key-files: created: - cabana/navigation.go - cabana/settings.go - cabana/metadata_settings_test.go - ../fonoteka.go/plugins/golem15/fonoteka/admin_permissions.go - ../fonoteka.go/plugins/golem15/fonoteka/admin_navigation.go - ../fonoteka.go/plugins/golem15/fonoteka/admin_settings.go - ../fonoteka.go/plugins/golem15/fonoteka/models/settings/fields.yaml - ../fonoteka.go/plugins/golem15/fonoteka/admin_metadata_test.go - ../fonoteka.go/plugins/golem15/fonoteka/admin_settings_test.go modified: - cabana/registry.go - cabana/http.go - cabana/auth.go - bouncer/context.go - pact/capabilities.go - ../fonoteka.go/plugins/golem15/fonoteka/admin.go key-decisions: - "Developer receives all seven source-declared Fonoteka permissions; publisher receives no implicit Fonoteka grant and superuser retains framework bypass" - "Backend principals carry an explicit domain marker, preventing a frontend principal with matching identifiers or grants from entering Cabana" - "Missing singleton GET returns schema defaults without creating a row; first valid PUT creates ID 1 and identical replay leaves updated_at unchanged" patterns-established: - "Pattern: filter metadata entries before constructing response values so denied labels and targets never serialize" - "Pattern: compile settings writable fields as the intersection of schema scalars, model columns, and Fillable" requirements-completed: [AUTH-08, ADMIN-05] coverage: - id: D1 description: "The exact permission catalog is unique, role-assigned, reference-valid, wildcard-aware, and restricted to backend principals." requirement: AUTH-08 verification: - kind: integration ref: plugins/golem15/fonoteka/admin_metadata_test.go#TestAdminMetadataPermissions status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_metadata_test.go#TestAdminMetadataFiltering status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_metadata_test.go#TestAdminMetadataRejectsFrontendPrincipal status: pass human_judgment: false - id: D2 description: "Navigation and settings discovery preserve source metadata, stable ordering, localization keys, non-null empty arrays, and whole-entry permission filtering." requirement: AUTH-08 verification: - kind: integration ref: plugins/golem15/fonoteka/admin_metadata_test.go#TestAdminMetadataNavigation status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_metadata_test.go#TestAdminMetadataSettingsList status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_metadata_test.go#TestAdminMetadataFiltering status: pass human_judgment: false - id: D3 description: "Fonoteka settings expose a localized required schema and side-effect-free missing read followed by singleton creation and idempotent replay." requirement: ADMIN-05 verification: - kind: integration ref: plugins/golem15/fonoteka/admin_settings_test.go#TestAdminSettingsSchema status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_settings_test.go#TestAdminSettingsMissingRead status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_settings_test.go#TestAdminSettingsIdempotentUpdate status: pass human_judgment: false - id: D4 description: "Settings PUT is permission-first, schema-projected, Fill/Validate-backed, transactional, and rolls back invalid required or typed values." requirement: ADMIN-05 verification: - kind: integration ref: plugins/golem15/fonoteka/admin_settings_test.go#TestAdminSettingsPermissionOrder status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_settings_test.go#TestAdminSettingsProjection status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_settings_test.go#TestAdminSettingsValidation status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_settings_test.go#TestAdminSettingsRollback status: pass human_judgment: false duration: 1h 15m completed: 2026-09-26 status: complete --- # Phase 9 Plan 11: Permissions, Navigation, and Singleton Settings Summary **Cabana now compiles a source-exact permission catalog, exposes only authorized discovery metadata, and serves validated replay-safe singleton settings.** ## Performance - **Duration:** 1h 15m - **Started:** 2026-09-26T21:35:00+02:00 - **Completed:** 2026-09-26T22:50:00+02:00 - **Tasks:** 3 - **Files modified:** 21 ## Accomplishments - Registered all seven Fonoteka permissions with developer-only source assignments and activation-time reference validation. - Added stable localized navigation/settings discovery that removes unauthorized entries before serialization. - Added permission-first settings schema/GET/PUT routes with default-only missing reads, projected Fill/Validate writes, rollback, and idempotent replay. - Added an explicit backend-principal domain marker so frontend identities cannot satisfy Cabana authorization. ## Task Commits 1. **Task 1: Register exact permissions and validate every operation reference** - `10ca7a0`, `fdf1d24` 2. **Task 2: Serve exact permission-filtered navigation and settings metadata** - `10ca7a0`, `c0a7043` 3. **Task 3: Serve permissioned singleton settings through Fill and Validate** - `10ca7a0`, `fdf1d24`, `c0a7043` ## Decisions Made - Role assignments are merged when a backend principal is resolved, avoiding boot-time database mutation while preserving exact plugin defaults. - The parent Fonoteka navigation entry is visible when the principal can use at least one child, even when grants are exact rather than wildcard. - Required boolean settings validate request presence independently from the model value, because `false` is a valid supplied value. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 2 - Missing Critical] Added explicit backend principal domain marker** - **Found during:** Task 1 identity isolation review. - **Issue:** A raw `bouncer.Principal` had no provenance marker, so a directly injected frontend principal with copied grants was indistinguishable after middleware. - **Fix:** Added `Principal.Backend`, set it only during backend-user resolution, and required it at Cabana authorization/metadata boundaries. - **Verification:** Frontend-audience token and unmarked-principal tests fail closed. - **Committed in:** `10ca7a0`. **2. [Rule 1 - Bug] Rejected nested values for known required settings fields** - **Found during:** Task 3 rollback verification. - **Issue:** A nested value for a known scalar was dropped by projection, allowing an unchanged 200 response instead of validation failure. - **Fix:** Required-input validation now rejects nested values before Fill and the transaction preserves prior data. - **Verification:** `TestAdminSettingsRollback` passes against PostgreSQL. - **Committed in:** `10ca7a0`. --- **Total deviations:** 2 auto-fixed (1 security boundary, 1 validation bug) **Impact on plan:** Both fixes enforce the plan's stated identity and rollback guarantees without expanding product scope. ## Issues Encountered - The fresh migration seeds settings row ID 1, while ADMIN-05 also requires missing-row behavior. Tests explicitly delete the singleton before proving side-effect-free GET and first PUT creation. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - All Phase 9 runtime capabilities are implemented. - Ready for 09-12 whole-phase security, PostgreSQL, OpenAPI, and evidence gates. ## Self-Check: PASSED - `go test ./cabana -count=1` - `go test ./plugins/golem15/fonoteka -run '^(TestAdminMetadata|TestAdminSettings)' -count=1` --- *Phase: 09-backend-admin-authentication-and-schema-pipeline* *Completed: 2026-09-26*