Identity Bootstrap Signature Recovery: Step 4 Implementation Summary

Stage 1 - Bounded bootstrap recovery

  • Changes:

    • Preserved identity-bootstrap API failures as safe technical details.

    • Added one fresh retry for signature_verification_failed, alongside the existing transient key-inspection retry.

    • Kept the existing Account recovery message after a second failure.

  • Verification:

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

    • php tests/run.php BrowserSigningNormalizationTest::testReadyIdentityRetriesBootstrapSignatureVerificationFailure BrowserSigningNormalizationTest::testReadyIdentityStopsAfterSecondBootstrapSignatureVerificationFailure — passed.

  • Notes:

    • The retry occurs before any identity publication marker is stored.

Stage 2 - Shared protected-action readiness

  • Changes:

    • Added the canonical ensureActionIdentity readiness contract over the existing browser identity flow.

    • Routed compose identity preparation through that contract and exposed it to other protected-action surfaces.

  • Verification:

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

    • php tests/run.php BrowserSigningNormalizationTest::testActionIdentityReadinessUsesTheSharedRecoveryPath BrowserSigningNormalizationTest::testReadyIdentityRetriesBootstrapSignatureVerificationFailure BrowserSigningNormalizationTest::testReadyIdentityStopsAfterSecondBootstrapSignatureVerificationFailure — passed.

  • Notes:

    • The default preserves the established lightweight action-readiness behavior; callers can explicitly request full publication verification when needed.

Stage 3 - Protected-action continuation

  • Changes:

    • Routed approvals, reactions, invitation issuance, and invitation redemption through the shared readiness gate.

    • Kept compose on that gate and retained each action's existing once-only submission controls.

  • Verification:

    • node --check public/assets/browser_signing.js public/assets/thread_reactions.js public/assets/invite_issuance.js public/assets/invite_redemption.js — passed.

    • php tests/run.php BrowserSigningNormalizationTest::testApprovalSigningPreparesSignsAndFinalizesCanonicalApproval BrowserSigningNormalizationTest::testSignedThreadSubmitPreparesSignsAndFinalizes BrowserSigningNormalizationTest::testThreadReactionBootstrapsIdentityBeforeApplyingLike BrowserSigningNormalizationTest::testPostReactionLikeUsesLikeFeedbackCopy — passed.

  • Notes:

    • Direct signing actions now wait for readiness before preparing their action, so recovery completes before any protected write is attempted.

Stage 4 - Safe verification diagnostics

  • Changes:

    • Added an operator log entry when identity-bootstrap signature verification fails.

    • The diagnostic records only the failure status, expected and reported public fingerprints, and parsed GnuPG status-code names.

    • It deliberately excludes armored keys, detached signatures, user IDs, and raw GnuPG output.

  • Verification:

    • php tests/run.php IdentityBootstrapDiagnosticsTest::testDiagnosticKeepsOnlySafeVerificationMetadata — passed.

  • Notes:

    • Client-visible API errors remain unchanged; the diagnostic is emitted only through the server error log.

Stage 5 - Regression verification

  • Changes:

    • No production code or schema changes were needed in this stage; the focused recovery, protected-action, verifier, and diagnostic coverage from Stages 1–4 provides the regression suite.

  • Verification:

    • node --check public/assets/browser_signing.js public/assets/thread_reactions.js public/assets/invite_issuance.js public/assets/invite_redemption.js — passed.

    • php tests/run.php BrowserSigningNormalizationTest PrivateSiteAuthTest OpenPgpKeyInspectorTest IdentityBootstrapDiagnosticsTest — passed.

    • php tests/run.php WriteApiSmokeTest::testLinkIdentityUsesPublicKeyUserIdForUsernameAndInvalidatesProfileArtifact WriteApiSmokeTest::testLinkIdentityAutoCreatesHiddenBootstrapPostWhenNoBootstrapPostIdIsProvided WriteApiSmokeTest::testCreatePreparedPostVerifiesDetachedSignatureAndCommitsSignatureFile WriteApiSmokeTest::testCreatePreparedPostRejectsInvalidDetachedSignatureWithoutWritingFiles WriteApiSmokeTest::testLinkIdentityUsesIncrementalReadModelUpdateWhenDatabaseIsWarm WriteApiSmokeTest::testLinkIdentityWithNewBootstrapUsesIncrementalReadModelUpdateWhenDatabaseIsWarm WriteApiSmokeTest::testApprovedLikeTagAddsScoreAndDoesNotDoubleCountForSameIdentity WriteApiSmokeTest::testFinalizePreparedApprovalVerifiesSignatureBeforeCreatingApproval — passed.

    • A full php tests/run.php attempt identified existing failures outside this feature in LocalAppSmokeTest activity rendering and LazyComposeSigningTest; the relevant recovery tests above remained green.

  • Notes:

    • A browser session with a newly created OpenPGP key and a protected write still needs production-like manual confirmation before deployment; this workspace has no authenticated browser session to perform it.