zenmemes Platform Docs Download

Repository source: docs/plans/account_key_approval_safety/account_key_approval_safety_step4_implementation_summary.md

Feature plan: Step 1 · Step 2 · Step 3 · Step 4

Debug mode: enabled

Status: All six implementation stages complete. Local release verification and documentation are complete; production deployment remains separately authorized work.

Planning commit: febc1e03 on feature/account-key-approval-safety.

Stage 1 - Restrict account key approval paths

  • Changes: Added the default-enabled FORUM_ALLOW_NON_OPERATOR_CROSS_ACCOUNT_APPROVAL setting and shared approval policy. With the setting disabled, members retain new-account sponsorship and approval of their own additional keys; root-seeded operators retain cross-account authority. Direct/manual approvals and invitation redemption recheck the current policy under the write lock. Stale read models fail closed. Targets must match their canonical bootstrap and profile.

  • Changes: Generic posting rejects identity protocol tags, and generic finalization rejects prepared protocol records while retaining ordinary thread/reply support. Signed flag changes require actual root-seed authority rather than approval labels. Existing grants are not re-evaluated when the setting changes.

  • Changes: Fixed same-second invitation replay so issuance precedes redemption even when random record IDs sort in the opposite order. Full/incremental projections and the writer share the ordering rule; same-second revocation conservatively precedes redemption. This dependency bug was exposed by the signed acceptance/rebuild test.

  • Verification: Six policy tests cover the role/account matrix, live toggles, competing first-key claims, stale metadata/markers, canonical seed mismatches, protocol endpoint bypass attempts, invitation issuer/recipient restrictions, and same-second replay ordering. A signed invitation smoke test verifies acceptance-time denial, successful retry when permitted, and grant retention across a restricted rebuild.

  • Verification: The final targeted run of policy, feature flag, invitation UI, signed approval/invitation, prepared post, signed flag-change, and approval rebuild tests passed. git diff --check passed. The earlier broad run executed 153 tests: 147 passed and six failed. Four prepared-post regressions were corrected and passed on rerun. The vote-caption failure also reproduces against planning commit febc1e03; the task-queue failure has existing checkout history dating to October 9 and remains unresolved (it passes in the isolated baseline directory).

  • Notes: Production was not changed. Real browser review flows, durable acceptance receipts, expiry-independent invitation history, account/operator history, and deployment/rollback verification belong to stages 2–6. Do not treat this stage as completion of the approved vertical slice.

Resumption - October 15, 2026

  • User explicitly enabled Railspike debug mode. Resumed the existing feature branch and fast-forwarded it to current main before Stage 2 work.

Stage 2 - Review account access before signing

  • Changes: Reused ApprovalPolicy context for the three action-specific labels, approved-key count warning, and exact target-key confirmation. Profile controls show the consequence; pending controls open a review. Browser signing requires a complete review and cancellation performs no signing or finalization. Invitation issuance explains bearer-link scope and acceptance-time policy enforcement.

  • Changes: Prepared approvals include a signed context digest and retain the complete context in server-owned preparation metadata. Finalization compares live context under the existing write lock and rejects changed accounts, counts, policy, or authority, requiring refresh/review/retry. Existing canonical-byte and signature checks prevent target substitution; expired preparations remain rejected.

  • Verification: php tests/run.php ApprovalPolicyTest BrowserSigningNormalizationTest passed 87 tests. The final policy rerun passed six tests, including changed-count and changed-policy review invalidation, all three labels, target substitution, and expiry. Five focused WriteApiSmokeTest tests passed for profile controls, preparation, signed finalization, and invitation acceptance. node tests/browser/approval_review.mjs passed desktop/mobile confirmation, cancellation without signing, retry, and missing-review rejection. Browser review uses isolated transport/signing doubles; real signed acceptance is covered by the PHP smoke tests. git diff --check passed.

  • Notes: Debug mode was explicitly enabled on resumption. No production changes. Durable acceptance evidence and history remain stages 3–6; the signed review digest alone is not acceptance evidence.

Stage 3 - Durable acceptance evidence and stable invitation replay

  • Changes: Added repository-owned ApprovalAcceptance receipts bound to exact grant bytes, acceptance time, account/key, authorizer, classification, count, authority, and effective policy. Direct/manual and invitation grants commit the receipt with the grant/signature without changing signed bytes. Receipts validate identities, time, grant binding, and signed review context where present; absent legacy evidence remains unknown.

  • Changes: Failed acceptance writes unstage/remove incomplete grant files; a committed outcome is preserved if reporting fails. Exact signed direct retries recover committed success without duplicate grants. Existing projection-failure messages retain the committed-write distinction.

  • Changes: Full and incremental projections share InvitationApprovalReplay. Receipts supply acceptance time rather than preparation time; legacy creation dates are replay evidence only, not claimed acceptance dates. Expiry is compared with historical event time instead of today's clock; later revocation cannot remove a receipted grant. Both projections validate available direct receipts.

  • Changes: ArchiveRecordCatalog recognizes and validates receipts for trusted repository restores; remote content imports explicitly exclude this authority family.

  • Verification: Final targeted run passed 36 tests across ApprovalAcceptanceTest, ApprovalPolicyTest, InvitationIssuanceTest, RepositoryArchiveImportCommandTest, ContentImportPlannerTest, RepositoryArchiveValidationTest, and six exact WriteApiSmokeTest methods. Coverage includes delayed/pre-expiry versus post-expiry acceptance, unknown legacy receipts, tampered bytes/context, an injected Git commit failure with clean retry, duplicate signed retry, signed invitation restore from tar.gz followed by rebuild, full/incremental parity, and projection-failure handling. An initial parity failure exposed an invalid posts.source_path query; reusing the canonical path resolver fixed it and the final suite passed. git diff --check passed.

  • Notes: No database migration or production changes. Receipts are repository-owned evidence, not a new member-signed authority primitive; repository restore remains trusted administration. Account/operator presentation and release checks remain stages 4–6.

Stage 4 - Account key history

  • Changes: Added shared ApprovalHistory lookups and a common history partial on classic/Forte username and profile pages. Paginated account history shows approved keys, approver, known acceptance time, first-key/own-account/cross-account classification, operator overrides, count/policy snapshots, and grant/receipt links. Missing legacy acceptance facts stay unknown; root seeds are identified without inventing acceptance times. Receipt facts retain the original account/approver usernames when current names change.

  • Changes: Profile API and CLI audit report the same acceptance evidence; compact profile/user summaries link to full history. Existing audit current-state review hints remain separately labeled and do not become confirmed violations. Source-path validation permits receipt links under existing site-access rules.

  • Changes: Approval invalidation now includes the username, every same-account profile, and Forte variants; invitation acceptance uses the same invalidation path. History pages cap key pages at 100 (default 50) and candidate grant reads at 100 per key; ordinary bootstrap replies are filtered before the cap. No new public feed or device/IP data.

  • Verification: The broad history/audit/LocalAppSmokeTest run passed 151 tests, including static publication, private membership boundaries, profile and source routes. After the final history-field change, five focused tests passed again without warnings: pagination/snapshot preservation, approved private profile, restricted anonymous account/receipt routes, static artifact rendering, and classic/Forte history/API/evidence/invalidation. A separate signed invitation/rebuild test passed and checks its history classification/time. Profile presentation/theme checks passed in an earlier 15-test run. git diff --check passed.

  • Notes: No database migration or production changes. The history is the current account's approved keys with available grant evidence, not a revocation log. Operator correlation remains Stage 5. A template field was added while the broad process still held an older class definition, producing transient missing-field warnings; the final fresh-process static/history rerun passed without those warnings.

Stage 5 - Authenticated operator security review

  • Changes: Extended the shared ActivityService filter and classic/Forte activity controls with view=security, joining direct approvals, invitation redemptions, and recorded cross-account-policy changes. Grant details link the affected account and receipt and show immutable acceptance context. Legacy facts and deployment-only policy changes remain explicitly unknown. Settings link to review; banners explain that restriction does not revoke existing grants.

  • Changes: Both HTTP routing and the data service require an authenticated session whose identity is currently approved and canonically root-seeded. Identity hints cannot authorize review. The same route guard covers pages, pagination, item detail, and commit-detail requests with the security view. Successful and denied responses use no-store; no-store HTML bypasses conditional 304 responses. RSS is unavailable for this view. No restricted activity projection is added to public/offline tables; static activity lookup already excludes query routes and the worker excludes these responses.

  • Changes: Receipted grant activity uses actual acceptance time in full/incremental projections, preserving preparation dates in the signed source records. Existing signed flag event times and source commits remain the policy-change evidence; same-second ordering can require commit inspection.

  • Verification: An 18-test targeted run passed activity recovery/list/detail/pagination, manifest consistency, static ETag behavior, offline worker/public snapshot boundaries, authenticated operator correlation, and approval rebuild parity. Two final focused flag/operator tests passed. Extended node tests/browser/invite_zero_click.mjs passed public and members-only flows with actual signing/authentication, operator page/API no-store headers, member and forged-hint denial, RSS rejection, invitation acceptance/recovery, naming, rebuild, expiry, revocation, storage denial, and lost-response recovery. git diff --check passed.

  • Notes: No production changes. Existing profile/source/activity evidence retains its established audience; the new correlation view is restricted. Stage 6 will document local deployment, rebuild and rollback limits and record final checks.

Stage 6 - Release verification and operator documentation

  • Changes: Added the acceptance-receipt specification, deployment/recovery/rollback runbook, and README links and behavior summary. Retained the interrupted stage's acceptance-time invitation-expiry recheck and complete approval/cache invalidation on exact signed recovery; cache failure now explicitly reports an already committed grant.

  • Changes: Added a real signed browser approval suite and a signed API regression covering new-member sponsorship, own-account addition, cross-account restriction, committed refresh failure, exact retry after a policy toggle, stale artifact removal, and immutable history after rebuild. Extended the browser rehearsal through repeated rebuild, static publication, status, and approval audit.

  • Verification: php tests/run.php ApprovalPolicyTest ApprovalAcceptanceTest ApprovalHistoryTest ApprovalAuditCommandTest InvitationIssuanceTest RepositoryArchiveImportCommandTest RepositoryArchiveValidationTest ContentImportPlannerTest FeatureFlagsBehaviorTest PrivateSiteAuthTest WriteApiSmokeTest::testSignedAccountKeyFlowsPreserveReviewAndRecoverCommittedRefreshFailure passed 49/49. Five additional WriteApiSmokeTest methods passed: signed invitation acceptance/archive restore/rebuild, authenticated operator correlation, signature verification, incremental approval parity, and committed-refresh stale marking.

  • Verification: node tests/browser/approval_review.mjs, node tests/browser/account_key_approval.mjs, and node tests/browser/invite_zero_click.mjs passed. These cover desktop/mobile review, cancellation/retry, real signing/authentication, restricted operator override, history, public/member invitation recovery, expiry/revocation, restricted routes/no-store, repeated rebuild, static user listing, status, and audit in isolated temporary repositories. The deployment rehearsal used current code with isolated fixture content; no live instance was changed.

  • Verification: php tests/run.php LocalAppSmokeTest OfflineNavigationWorkerTest PublicOfflineSnapshotBuilderTest ran 150 tests: 148 passed and the two three-second startup shorthand tests timed out before their address output. Both passed in a clean worktree at baseline d4d54c6c; both also passed on the final isolated rerun in this workspace. No startup code changes were needed. Static release activation/recovery, private access, classic/Forte pages, source routes, offline boundaries, and snapshot checks passed in the broad run.

  • Verification: PHP syntax, Node syntax, local README/spec/runbook links, and git diff --check passed. Initial rehearsal assertions incorrectly expected a per-account static page; the release builder publishes shared user listings and serves account history dynamically. The final test checks the published listing and dynamic history after publication and passes.

  • Notes: Existing stage evidence is recorded in planning commit febc1e03, Stage 1 c7ba748b, Stage 2 08b155ac, Stage 3 c7ae2108, Stage 4 5c3c9c74, and Stage 5 d4d54c6c; Stage 6 is the commit containing this section. No database schema migration, production deployment, remote publication, or key revocation was performed. Production configuration, backups, writer maintenance, and rollout/rollback remain instance-specific checks described in the runbook. Earlier unrelated failures recorded under Stage 1 remain historical limitations of those runs; this is not a claim that the entire repository test suite passes.