Skip to content

fix: chat keys not updating when refreshing#203

Merged
highesttt merged 2 commits into
mainfrom
highest/plat-37742
Jul 1, 2026
Merged

fix: chat keys not updating when refreshing#203
highesttt merged 2 commits into
mainfrom
highest/plat-37742

Conversation

@highesttt

Copy link
Copy Markdown
Collaborator

No description provided.

@linear-code

linear-code Bot commented Jul 1, 2026

Copy link
Copy Markdown

PLAT-37742

@indent-zero

indent-zero Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor
PR Summary

Fixes a bug where LineClient.tryLogin (the stored-credential re-login path used during token recovery) silently discarded the fresh E2EE keychain LINE returned, leaving ExportedKeyMap and the on-disk secure blob stale. The initial-login keychain-export logic is refactored into shared helpers so both call sites persist the new keychain, refresh the secure file, and reload the live e2ee.Manager; a new "reconnect required" Matrix notice is surfaced when a group send trips over a missing own private key. Also fixes a pre-existing filename mismatch (initial login was writing the secure blob keyed by MXID while Connect loaded it by LINE MID).

  • pkg/connector/client.go: added refreshLoginE2EEKeys invoked from tryLogin, which re-derives the keychain, updates metadata, saves the secure blob under the MID, and pushes fresh keys into lc.E2EE if it exists.
  • pkg/connector/connector.go: factored fetchLoginKeys into shared helpers (exportLoginE2EEKeys, saveLoginE2EEKeyMetadata, loginSecureDataID) and switched the secure-blob filename to prefer LINE MID over MXID.
  • pkg/e2ee/manager.go: promoted two sentinel errors ErrMissingOwnPrivateKey (from UnwrapGroupSharedKey) and ErrGroupKeyNotLoaded (from EncryptGroupMessageRaw) so callers can branch on them via errors.Is.
  • pkg/connector/send_message.go: intercepts ErrMissingOwnPrivateKey around both the pre-encrypt fetch and the retry, returning a bridgev2.MessageStatus failure with a Matrix notice telling the user to reconnect LINE.
  • pkg/connector/{login_keys,send_message}_test.go and pkg/e2ee/manager_test.go: unit coverage for the new helpers, the MID-vs-MXID fallback, sentinel errors, and the reconnect status wrapping.

Issues

1 potential issue found:

  • The new "Reconnect LINE in Beeper, then try sending again" notice fires on any e2ee.ErrMissingOwnPrivateKey, but reconnecting only helps when the server-side group key targets a receiver key ID that a fresh keychain unwrap will actually produce. If LINE's E2EEGroupSharedKey.ReceiverKeyID points at a key that isn't in the current keychain (stale group key wrapped for a rotated device key), the user reconnects, refreshLoginE2EEKeys populates ExportedKeyMap, and the next send hits the same notice again. Consider gating the notice on evidence that our current MyKeyIDs() differs from ReceiverKeyID, or falling back to the existing plaintext path after one failed reconnect attempt, so users don't get stuck in a loop. → Autofix
1 issue already resolved
  • saveLoginE2EEKeyMetadata writes EncryptedKeyChain/E2EEPublicKey/E2EEVersion/E2EEKeyID onto meta before exportLoginE2EEKeys succeeds, so a failure in newE2EEManager/GetEncryptedIdentityV3/InitStorage/InitFromLoginKeyChain leaves the persisted keychain metadata pointing at the new login while ExportedKeyMap still holds the previous export. No consumer trusts those fields together today, but any future caller that uses EncryptedKeyChain/E2EEKeyID to decide whether the exported map is fresh will get burned; consider swapping the order so metadata is only written after exportLoginE2EEKeys returns nil. (fixed by commit 5a16797)

CI Checks

Waiting for CI checks...


⚡ Autofix All Issues

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c7b605ed-d352-42f1-96a9-30eaf8a202a4

📥 Commits

Reviewing files that changed from the base of the PR and between 868fa9c and 5a16797.

📒 Files selected for processing (3)
  • pkg/connector/client.go
  • pkg/connector/connector.go
  • pkg/connector/login_keys_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/connector/connector.go
  • pkg/connector/client.go
📜 Recent review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Lint with 1.25
  • GitHub Check: build-docker
  • GitHub Check: build-docker
  • GitHub Check: Lint with 1.25
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use go fmt for code formatting across all Go files
Use goimports with -local "github.com/highesttt/matrix-line-messenger" flag to group project-local imports correctly
Use zerolog for logging throughout the codebase
Do not use Msgf in logging; use Msg with structured fields instead
Use Stringer interface where applicable in Go code

Files:

  • pkg/connector/login_keys_test.go
**/!(ltsm)/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/!(ltsm)/**/*.go: Run staticcheck on all Go files excluding pkg/ltsm package (transpiled WASM code)
Run go vet on all Go files excluding pkg/ltsm package (transpiled WASM code)

Files:

  • pkg/connector/login_keys_test.go
🔇 Additional comments (2)
pkg/connector/login_keys_test.go (2)

1-40: LGTM!


42-77: LGTM!


📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved LINE re-login recovery by refreshing E2EE key material after sign-in and restoring encrypted messaging more reliably.
    • Enhanced LINE group encryption handling: when the user’s private key is missing, the app now shows a clear “reconnect” failure notice instead of silently retrying.
    • Made login secure-data updates more robust by persisting exported key data with proper error handling and preserving existing metadata if refresh/export fails.
  • Tests
    • Added/extended unit tests covering login E2EE key metadata, secure-data ID selection, and the new reconnect/error behaviors.

Walkthrough

Refactors LINE login E2EE key export and refresh handling, adds sentinel E2EE manager errors, and changes group message send flow to return a reconnect status when the own private key is missing.

Changes

LINE E2EE key management and reconnect handling

Layer / File(s) Summary
E2EE key export and metadata helpers
pkg/connector/connector.go, pkg/connector/login_keys_test.go
Adds exportLoginE2EEKeys, saveLoginE2EEKeyMetadata, and loginSecureDataID helpers; reworks fetchLoginKeys to use them and checks secure-data save errors instead of ignoring them; adds tests for metadata copying and ID fallback logic.
Re-login E2EE key refresh flow
pkg/connector/client.go, pkg/connector/login_keys_test.go
Adds newE2EEManager and LineClient.refreshLoginE2EEKeys, called during tryLogin after re-login to persist refreshed key metadata/export data and reload keys into the active E2EE manager; adds a test covering export failure preservation.
E2EE manager sentinel errors
pkg/e2ee/manager.go, pkg/e2ee/manager_test.go
Adds ErrMissingOwnPrivateKey and ErrGroupKeyNotLoaded sentinel errors, wraps them into UnwrapGroupSharedKey and EncryptGroupMessageRaw failures, and adds tests asserting these errors via errors.Is.
Group message reconnect error handling
pkg/connector/send_message.go, pkg/connector/send_message_test.go
Adds lineGroupE2EEReconnectRequiredError to convert missing-private-key errors into a bridgev2 status error with a reconnect notice, applied in HandleMatrixMessage's group key fetch and retry paths; adds corresponding tests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TryLogin
  participant LineClient
  participant E2EEManager
  participant SecureStorage
  TryLogin->>LineClient: refreshLoginE2EEKeys(loginResult)
  LineClient->>LineClient: saveLoginE2EEKeyMetadata(meta)
  LineClient->>E2EEManager: export encrypted keys
  LineClient->>SecureStorage: SaveSecureDataToFile(exportedKeyMap)
  LineClient->>E2EEManager: LoadExportedKeys
  E2EEManager-->>TryLogin: refresh result / error
Loading
sequenceDiagram
  participant HandleMatrixMessage
  participant E2EEManager
  participant User
  HandleMatrixMessage->>E2EEManager: fetchAndUnwrapGroupKey
  E2EEManager-->>HandleMatrixMessage: ErrMissingOwnPrivateKey
  HandleMatrixMessage->>HandleMatrixMessage: lineGroupE2EEReconnectRequiredError
  HandleMatrixMessage-->>User: reconnect status error with notice
Loading

Related Issues: Not specified in the provided information.

Related PRs: Not specified in the provided information.

Suggested labels: e2ee, bug-fix

Suggested reviewers: Not specified in the provided information.

🐰
((
( -.-)
o_(")(")
A rabbit refreshes keys with care,
Reconnect notices sent through the air,
Sentinel errors now clearly named,
Group secrets safely reclaimed.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided, so there isn't enough content to assess beyond the title. Add a brief description of the behavior change and the main areas touched by the PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: refreshing login/chat E2EE keys to fix stale key updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch highest/plat-37742

Comment @coderabbitai help to get the list of available commands.

Comment thread pkg/connector/connector.go Outdated
WithIsCertain(true).
WithMessage(lineGroupE2EEReconnectNotice).
WithSendNotice(true).
WithErrorReason(event.MessageStatusGenericError)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit — reconnect notice can loop. lineGroupE2EEReconnectRequiredError triggers on any ErrMissingOwnPrivateKey, but the sentinel bubbles up from Manager.UnwrapGroupSharedKey whenever sharedKey.ReceiverKeyID isn't in keyByRawID/myRawKeyID. If LINE hands us a group key wrapped for a receiver key that the freshly-refreshed keychain also doesn't contain (e.g. an old shared key still targeting a rotated device key), the user follows the instructions, refreshLoginE2EEKeys succeeds, and the next send fails with the same notice — an infinite reconnect prompt from the user's perspective. Consider only surfacing this status when the loaded MyKeyIDs() differs from ReceiverKeyID, or falling back to the existing markGroupNoE2EE + plaintext path once a reconnect has already been suggested.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
pkg/connector/send_message_test.go (1)

15-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Cover the HandleMatrixMessage reconnect branches too.

These tests only prove the helper works in isolation. The user-visible behavior changed in pkg/connector/send_message.go Lines 573-592, where HandleMatrixMessage should return the reconnect status instead of falling through to the plaintext fallback. Adding one focused test for each branch would catch regressions if those call sites stop invoking the helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/connector/send_message_test.go` around lines 15 - 44, The current tests
only cover lineGroupE2EEReconnectRequiredError in isolation, so add focused
coverage for the reconnect paths in HandleMatrixMessage where it should return
the reconnect MessageStatus instead of falling back to plaintext handling.
Update pkg/connector/send_message_test.go with one test per branch that
exercises the relevant HandleMatrixMessage path and verifies the returned
status/error behavior. Use the existing helper
lineGroupE2EEReconnectRequiredError, lineGroupE2EEReconnectNotice, and the
matrix send flow symbols in send_message.go to locate the exact branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/connector/client.go`:
- Around line 565-570: The E2EE metadata is being updated in tryLogin before
exportLoginE2EEKeys can fail, which can leave meta partially refreshed when the
export errors. In the login flow around saveLoginE2EEKeyMetadata,
exportLoginE2EEKeys, and meta.ExportedKeyMap, delay mutating meta until after
exportLoginE2EEKeys succeeds, or build the refreshed metadata from the exported
result and only then persist it. Make sure tryLogin only saves the refreshed
metadata once both the export and the ExportedKeyMap update have completed
successfully.
- Around line 574-578: The exportLoginE2EEKeys flow is dropping the freshly
initialized E2EE manager when lc.E2EE is nil, so the client keeps no in-memory
keys after login refresh. Update the lc.E2EE handling in exportLoginE2EEKeys to
install the returned manager into lc.E2EE when no active manager exists, and
only call LoadMyKeyFromExportedMap on an existing manager. Use the lc.E2EE and
exportLoginE2EEKeys symbols to locate the branch.

---

Nitpick comments:
In `@pkg/connector/send_message_test.go`:
- Around line 15-44: The current tests only cover
lineGroupE2EEReconnectRequiredError in isolation, so add focused coverage for
the reconnect paths in HandleMatrixMessage where it should return the reconnect
MessageStatus instead of falling back to plaintext handling. Update
pkg/connector/send_message_test.go with one test per branch that exercises the
relevant HandleMatrixMessage path and verifies the returned status/error
behavior. Use the existing helper lineGroupE2EEReconnectRequiredError,
lineGroupE2EEReconnectNotice, and the matrix send flow symbols in
send_message.go to locate the exact branches.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 89ea6579-f7e4-4713-afeb-9dff34e271ba

📥 Commits

Reviewing files that changed from the base of the PR and between ae8aae0 and 868fa9c.

📒 Files selected for processing (7)
  • pkg/connector/client.go
  • pkg/connector/connector.go
  • pkg/connector/login_keys_test.go
  • pkg/connector/send_message.go
  • pkg/connector/send_message_test.go
  • pkg/e2ee/manager.go
  • pkg/e2ee/manager_test.go
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Lint with 1.25
  • GitHub Check: build-docker
  • GitHub Check: Lint with 1.25
  • GitHub Check: build-docker
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use go fmt for code formatting across all Go files
Use goimports with -local "github.com/highesttt/matrix-line-messenger" flag to group project-local imports correctly
Use zerolog for logging throughout the codebase
Do not use Msgf in logging; use Msg with structured fields instead
Use Stringer interface where applicable in Go code

Files:

  • pkg/connector/login_keys_test.go
  • pkg/connector/send_message_test.go
  • pkg/e2ee/manager.go
  • pkg/connector/connector.go
  • pkg/connector/client.go
  • pkg/e2ee/manager_test.go
  • pkg/connector/send_message.go
**/!(ltsm)/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/!(ltsm)/**/*.go: Run staticcheck on all Go files excluding pkg/ltsm package (transpiled WASM code)
Run go vet on all Go files excluding pkg/ltsm package (transpiled WASM code)

Files:

  • pkg/connector/login_keys_test.go
  • pkg/connector/send_message_test.go
  • pkg/e2ee/manager.go
  • pkg/connector/connector.go
  • pkg/connector/client.go
  • pkg/e2ee/manager_test.go
  • pkg/connector/send_message.go
pkg/connector/connector.go

📄 CodeRabbit inference engine (AGENTS.md)

Implement bridgev2.NetworkConnector and bridgev2.NetworkAPI interfaces in the connector package for bridge logic

Files:

  • pkg/connector/connector.go
🔇 Additional comments (6)
pkg/e2ee/manager.go (1)

7-7: LGTM!

Also applies to: 20-24, 466-466, 633-633

pkg/e2ee/manager_test.go (1)

1-36: LGTM!

pkg/connector/send_message.go (1)

8-8: LGTM!

Also applies to: 22-22, 35-48, 575-577, 591-592

pkg/connector/connector.go (1)

473-522: LGTM!

pkg/connector/login_keys_test.go (1)

1-38: LGTM!

pkg/connector/client.go (1)

24-24: LGTM!

Also applies to: 549-551

Comment thread pkg/connector/client.go Outdated
Comment thread pkg/connector/client.go
Comment on lines +574 to +578
if lc.E2EE != nil {
if err := lc.E2EE.LoadMyKeyFromExportedMap(exported); err != nil {
return fmt.Errorf("load exported keys into active E2EE manager: %w", err)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Install the freshly initialized manager when lc.E2EE is nil.

exportLoginE2EEKeys returns an initialized manager, but this branch drops it when no active manager exists. That leaves the re-logged-in client without in-memory E2EE keys even after a successful refresh.

Proposed fix
-	if lc.E2EE != nil {
-		if err := lc.E2EE.LoadMyKeyFromExportedMap(exported); err != nil {
-			return fmt.Errorf("load exported keys into active E2EE manager: %w", err)
-		}
+	if lc.E2EE == nil {
+		lc.E2EE = mgr
+	} else if err := lc.E2EE.LoadMyKeyFromExportedMap(exported); err != nil {
+		return fmt.Errorf("load exported keys into active E2EE manager: %w", err)
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if lc.E2EE != nil {
if err := lc.E2EE.LoadMyKeyFromExportedMap(exported); err != nil {
return fmt.Errorf("load exported keys into active E2EE manager: %w", err)
}
}
if lc.E2EE == nil {
lc.E2EE = mgr
} else if err := lc.E2EE.LoadMyKeyFromExportedMap(exported); err != nil {
return fmt.Errorf("load exported keys into active E2EE manager: %w", err)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/connector/client.go` around lines 574 - 578, The exportLoginE2EEKeys flow
is dropping the freshly initialized E2EE manager when lc.E2EE is nil, so the
client keeps no in-memory keys after login refresh. Update the lc.E2EE handling
in exportLoginE2EEKeys to install the returned manager into lc.E2EE when no
active manager exists, and only call LoadMyKeyFromExportedMap on an existing
manager. Use the lc.E2EE and exportLoginE2EEKeys symbols to locate the branch.

@highesttt highesttt merged commit 0a867ea into main Jul 1, 2026
10 checks passed
@highesttt highesttt deleted the highest/plat-37742 branch July 1, 2026 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant