Approved Members Identity Publication Step 4 Implementation Summary

Stage 1 - Private-mode-safe identity existence checks

  • Changes:

    • Exposed the approved-members-only state to rendered pages through the existing layout data.

    • Prevented browser identity setup from calling the protected general profile lookup while private mode is active.

    • Kept the identity prepare/finalize contract as the authoritative duplicate check.

  • Verification:

    • php -l src/ForumRewrite/View/TemplateRenderer.php passed.

    • php -l src/ForumRewrite/Application.php passed.

    • node --check public/assets/browser_signing.js passed.

    • php tests/run.php FeatureFlagEvaluatorTest passed all tests.

    • Private dev-server smoke rendered data-approved-members-only="1"; unauthenticated own-profile request returned 404.

  • Notes:

    • This stage does not yet complete the prepared identity observed for fingerprint d0ee...; Stage 2 addresses automatic finalization and recovery.

Stage 2 - Automatic lobby identity publication

  • Changes:

    • Loaded the existing browser identity-publication service on the Lobby page.

    • Made Lobby automatically publish any saved browser keypair before attempting authentication.

    • Reused the existing prepare/finalize flow so new identities are created and existing identities are handled by the canonical duplicate response.

  • Verification:

    • php -l src/ForumRewrite/Application.php passed.

    • node --check public/assets/private_site_auth.js passed.

    • node --check public/assets/browser_signing.js passed.

    • php tests/run.php BrowserSigningNormalizationTest passed all tests.

    • Private dev-server smoke rendered both browser identity and private-auth scripts on Lobby; unauthenticated profile access remained 404.

  • Notes:

    • The user-facing flow no longer requires manually opening Account to publish a keypair already saved in browser storage.

Stage 3 - Approval inheritance and authenticated access

  • Changes:

    • Confirmed the existing approval-derived profile state remains the authority after identity publication.

    • Added a regression check that an approved authenticated session can open its own profile and the full board.

  • Verification:

    • php tests/run.php LocalAppSmokeTest::testApprovedPrivateSessionCanViewOwnProfileAndBoard passed.

    • The test confirmed the approved fixture identity sees This is your profile. and the Board page under private mode.

  • Notes:

    • Approval may be seeded before or after identity publication because it is resolved when the profile/read model is synchronized.

Stage 4 - Private lobby route boundary

  • Changes:

    • Fixed private-mode root handling so only a plain root/threads request redirects to Lobby.

    • Root RSS requests and other query variants now remain inaccessible and return 404.

    • Added a route-matrix regression check covering Lobby, Account, content, profile API, backup, and RSS access.

  • Verification:

    • php -l src/ForumRewrite/Application.php passed.

    • php tests/run.php LocalAppSmokeTest::testPrivateLobbyOnlyExposesLobbyAccountAndAuthenticationSurfaces passed.

  • Notes:

    • The private web-server rewrite still must route non-asset requests through the application when enabled in production.

Stage 5 - Regression coverage and operator documentation

  • Changes:

    • Documented one-shot Lobby publication and authentication behavior in the README and production runbook.

    • Documented that manual Account linking is a recovery path rather than the normal user flow.

  • Verification:

    • php tests/run.php FeatureFlagEvaluatorTest passed all tests.

    • php tests/run.php BrowserSigningNormalizationTest passed all tests.

    • php tests/run.php LocalAppSmokeTest::testApprovedPrivateSessionCanViewOwnProfileAndBoard passed.

    • php tests/run.php LocalAppSmokeTest::testPrivateLobbyOnlyExposesLobbyAccountAndAuthenticationSurfaces passed.

    • PHP and JavaScript syntax checks passed for the changed runtime files.

  • Notes:

    • The complete legacy suite remains outside this focused verification because it has pre-existing unrelated failures; no production cutover should occur until those failures are triaged separately.

Stage 6 - Browser authentication and own-profile recovery

  • Changes:

    • Fixed the verified existing-identity path so presentation-free identity checks no longer dereference a null Account-page root before authentication starts.

    • Imported the signature verifier into the application namespace; /api/authenticate_identity previously failed with a 503 before it could verify any signature.

    • Made Account key generation and private-key restoration authenticate immediately after publication instead of exposing an own-profile link backed only by local browser state.

    • Added a server-rendered authenticated identity marker so pending users reload once to receive session-aware Lobby/Account content without entering an authentication loop.

    • Exposed authentication failures on Lobby/Account and in the browser console, and regenerated the PHP session ID after successful signature verification.

  • Verification:

    • php tests/run.php PrivateSiteAuthTest BrowserSigningNormalizationTest LocalAppSmokeTest::testApprovedPrivateSessionCanViewOwnProfileAndBoard LocalAppSmokeTest::testPrivateAuthenticationEndpointReachesSignatureVerifier LocalAppSmokeTest::testPrivateLobbyOnlyExposesLobbyAccountAndAuthenticationSurfaces passed.

    • php tests/run.php FeatureFlagEvaluatorTest passed.

    • JavaScript and PHP syntax checks passed for all changed runtime and test files.

    • Isolated Chromium smoke: a newly generated unapproved identity published automatically, completed challenge authentication with one PHP session, re-rendered Account once, and opened its own profile with HTTP 200.

    • Isolated Chromium smoke: a newly generated identity with a pre-existing approval seed published automatically, authenticated with a real detached signature, entered the Board, and opened its own profile with This is your profile.

  • Notes:

    • The two reproduced root causes were independent: a null-root browser exception suppressed before challenge creation, followed by a missing PHP class import that returned 503 after the browser path was repaired.

    • All browser writes and approval seeds used an isolated temporary repository and database; the active local repository was not modified.

Stage 7 - Lobby-only navigation

  • Changes:

    • Made the shared navigation derive its private-site menu from the server-authenticated viewer profile.

    • Anonymous Lobby users now see only Lobby and Account; authenticated pending users additionally see only their own Profile link.

    • Approved users retain the normal Board, About, Users, Tools, and Account navigation.

    • Applied the restricted navigation to private-mode message/404 pages as well as Lobby and Account.

  • Verification:

    • php tests/run.php LocalAppSmokeTest::testPrivateLobbyOnlyExposesLobbyAccountAndAuthenticationSurfaces LocalAppSmokeTest::testPendingPrivateSessionNavigationOnlyShowsLobbyOwnProfileAndAccount LocalAppSmokeTest::testApprovedPrivateSessionCanViewOwnProfileAndBoard passed.

    • PHP syntax checks passed for the renderer, application, and focused smoke tests.

    • Live dev-server HTML for anonymous Lobby and Account contained only Lobby and Account navigation links.

  • Notes:

    • The own-profile navigation link is emitted only from the authenticated server profile, never directly from the browser identity hint or localStorage.

Stage 8 - Static content access boundary

  • Changes:

    • Restricted the development router and Apache rewrite bypass to existing /assets/* files and favicon.ico only.

    • Routed all content, API, download, lock-file, and generated-artifact requests through the PHP front controller regardless of whether the private flag comes from the deployment environment or the instance feature-flags record.

    • Preserved public-instance static HTML acceleration through the front controller's existing eligible-artifact path.

    • Updated deployment documentation to describe the secure routing model and removed the obsolete requirement that Apache independently observe the private feature flag.

  • Verification:

    • php tests/run.php WebServerRoutingTest LocalAppSmokeTest::testPrivateLobbyOnlyExposesLobbyAccountAndAuthenticationSurfaces passed.

    • php tests/run.php LocalAppSmokeTest::testFrontControllerServesStaticArtifactForAnonymousEligibleRoute LocalAppSmokeTest::testFrontControllerBypassesStaticArtifactWhenCookieIsPresent LocalAppSmokeTest::testFrontControllerBuildsMissingArtifactAfterEligibleAnonymousFallback passed.

    • Private live server returned 404 for /index.html, /posts/reply-001.html, a physical /.locks/* path, and canonical protected content; /assets/site.css and /favicon.ico remained 200.

    • Separate public-mode live server returned 200 for canonical / and /posts/reply-001, 404 for direct /index.html and /posts/reply-001.html, and 200 for /assets/site.css.

    • PHP syntax and scoped whitespace checks passed.

  • Notes:

    • Public static HTML hits now incur the front-controller PHP entry cost so a mutable site-level privacy flag remains authoritative; content rendering still uses the prebuilt artifact when eligible.

Stage 9 - Feature-flag test isolation

  • Changes:

    • Added FORUM_APPROVED_MEMBERS_ONLY to the feature-flag test environment cleanup list.

    • Prevented the environment-override test from leaving private mode enabled for every subsequent test in the shared PHP test process.

    • Restored the prior $_SESSION value after private-session smoke tests so later authorization tests cannot inherit an approved identity.

    • Updated the approved Account-page regression to require the direct own-profile link now shown outside the advanced area.

    • Repaired five older smoke-test setup/expectation defects exposed after the leaked private state was removed: temporary template roots, current public-key presentation, read-model initialization, SQLite viewer CSS loading, and isolated static-routing roots.

  • Verification:

    • php tests/run.php LocalAppSmokeTest passed all 77 local application smoke tests.

    • php tests/run.php passed the complete test suite.

    • PHP syntax and scoped whitespace checks passed for all changed test files.

  • Notes:

    • The leaked flag and authenticated session produced a misleading cascade of route, write API, static artifact, read-model, and authorization failures; those downstream failures were not independent private-site regressions.

Stage 10 - Site-scoped approval tooling and live session diagnostics

  • Changes:

    • Made approval CLI defaults honor FORUM_SITE_ID, matching the web server's site-scoped repository and read-model paths.

    • Centralized the default site-scoped database path alongside the existing repository-path helper.

    • Added CLI-server-only private-session diagnostics for each application request, including the request, session ID, authenticated identity, resolved profile, approval result, repository, and database without logging key material or challenges.

    • Seeded the reported D0EE identity in the actual Chouse repository and rebuilt state/cache/post_index_chouse.sqlite3.

  • Verification:

    • Focused site-profile, local-state-path, approval-command, and private-session tests passed.

    • A controlled authenticated request to the active Chouse server returned HTTP 200 for the Board and the D0EE profile rendered Approved: yes.

    • php tests/run.php passed the complete test suite.

    • PHP syntax and scoped whitespace checks passed for all changed runtime and test files.

  • Notes:

    • The original ./v3 approval seed ... command ran without FORUM_SITE_ID=chouse, so it inspected the default Zenmemes repository. The Chouse server independently used state/local_repository_chouse and state/cache/post_index_chouse.sqlite3, where the identity existed but had no approval seed.