Skip to content

Classroom Public Route Guard Proof

Status: accepted-bounded-next-slice-not-complete.

This proof closes the SVC-006-public-route-guard-proof slice. It proves the native classroom foundation keeps tenant scope and profile/organization references local while the public gateway routes remain legacy-proxied. It does not approve public classroom route promotion.

Evidence

RequirementEvidenceResult
Classroom route guard proofmake test-classroom-route-guard validates every gateway route table and keeps /api/classrooms* and /api/admin/classrooms* on legacy_proxy.Passed
Owner-local profile/org referencesTestClassroomPublicRouteGuardKeepsTenantAndOwnerReferencesLocal verifies actor organization scope cannot be widened by organizationId, cross-organization detail returns 404, and responses contain only public ids/counters without profile/org display fields.Passed
Gateway identity compatibilityTestClassroomServiceAcceptsGatewayUserHeaders proves X-User-Id/X-User-Role are accepted as gateway-injected fallbacks.Passed
Trusted actor boundaryConflicting caller-supplied X-Actor-* headers cannot override authenticated X-User-* identity, and the gateway strips actor headers before proxying.Passed
Missing tenant fail-closedNon-admin list/detail requests without organization context return 403 instead of accepting a query-supplied tenant.Passed
Admin browser candidateThe opt-in disposable Admin smoke restores session.user.defaultOrganizationId, observes exactly one tenant-scoped Gateway list request, validates credentialed CORS and native route headers, and renders only the authorized class.Passed locally, non-default
Rollback notesThe active default route table remains deploy/gateway/routes.json; no classroom native route was added.Passed

Ownership Boundary

  • classroom-service owns classroom-local rows and classroom member rows.
  • organizationId, unitId, subjectId, teacherId, and createdById are public-id references only.
  • Non-admin list/detail requests are forced to the actor organization scope; query/header values cannot widen that scope.
  • Gateway-authenticated identity in X-User-* takes precedence over any caller-supplied X-Actor-* compatibility headers; the gateway removes those actor headers before proxying.
  • Non-admin requests fail closed when organization context is absent.
  • Classroom-service does not hydrate teacher names, profile email, profile fields, organization display data, IAM roles, or organization membership.
  • Lessons, materials, tuition, exams, attempts, and progress source data remain outside this slice. Snapshot counters remain read-model metadata only.

Verification

bash
GOTOOLCHAIN=go1.25.11 go test ./services/classroom-service/... -count=1
make test-classroom-route-guard
CLASSROOM_READ_RUNTIME_SMOKE_CONFIRM=disposable-runtime \
CLASSROOM_READ_RUNTIME_SMOKE_ADMIN_BROWSER=1 \
make test-classroom-read-runtime

Both commands passed for this handoff. The focused HTTP test also covers gateway-style headers, cross-tenant detail denial, tenant query override rejection, PARENT denial, and profile/org PII exclusion.

Route State And Rollback

  • Default /api/classrooms*, /api/admin/classrooms*, and /api/classrooms/:id/notify remain legacy-proxied.
  • Native /v1/classrooms* routes remain internal service contracts only.
  • The opt-in Admin frontend query /admin/classes?adminClassesGateway=native is limited to a read-only, authenticated GET /api/classrooms?limit=100 rehearsal against the non-default native route example. Its disposable local browser smoke proves canonical nested session tenant propagation, one Gateway request, CORS/native route headers, and tenant-scoped rendering. The default Admin regression still proves no Gateway request, and neither proof alters this route state.
  • Rollback is to keep deploy/gateway/routes.json active and disable any caller using native classroom routes. No default route table change is part of this slice.
  • Public classroom promotion remains blocked until a deployed route target, response parity evidence against the legacy teacher surface, and rollback-after-live evidence exist. The browser evidence here is local and non-default only.

Remaining Risk

SVC-003 and SVC-004 IAM/Profile default cutover gates remain open. Classroom adjacent lessons/materials/tuition ownership and public adapter parity still need their owner slices.

Go-platform documentation is generated from repository Markdown.