Repository navigation
Conversation
NewRemoteSignalClient runs a linked device whose Signal private keys, sessions and message plaintext live outside the client, behind the RemoteSignal interface. The client keeps its socket, receipts, retry receipts, prekey uploads and QR pairing, but never decrypts or encrypts: - receive hands each <enc> payload to the remote store and gets back only delivery metadata (events.RemoteMessage), plus an app-state key share when an own device sent one; errors map to the same retry receipts and nacks as the local path; - prekeys come from a remote-backed PreKeyStore that marks whole upload batches; - pairing keeps the local HMAC and account-signature checks and takes the device signature from the store, refusing one over different details; the store learns its own JIDs before pairing is confirmed; - incoming retry receipts ask the store to resend from its own record; - SendPrebuiltStanza transmits stanzas the store built and waits for the ack. Local SendMessage and pair-code pairing are refused. The device store holds only public material and is marked RemoteKeys; the local Signal store panics instead of using its zero private keys.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
golang.org/x/tools v0.50.0 and later require Go 1.26, so installing goimports@latest fails the Go 1.25 build job on every new run. Pin v0.49.0, the newest release on Go 1.25.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d4c58f25a0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Remote retry path calls PreRetryCallback (message unavailable) before resending. - Retry-receipt prekeys from GenOnePreKey are marked published. - A store transport error is neither acked nor retried; the server redelivers. - Verify the remote device signature against the remote identity key. - Reuse unuploaded prekeys and mark only the batch that was uploaded. - SendFBMessage refuses local encryption on a remote client. - Identity changes forget the sender's sessions in the remote store.
- RemoteSignal gains MigratePNToLID. On a remote client, the PN-to-LID migration in decryptMessages goes to the remote store (once per user after success) instead of the unused local session store. - forgetRemoteIdentity sends hosted JIDs as their base user; the store clears hosted devices with it, matching the local cleanup. - Document that PreRetryCallback gets a nil message on remote clients.
Summary
Lets HyperMeow run a linked device whose Signal private keys, sessions and message plaintext live in a customer-run Signal service, so Polymorfa's runner never sees message content (the A2 boundary in polymorfa/polymorfa
internal/docs/superpowers/specs/2026-10-04-customer-owned-data-and-zero-knowledge-sessions.md).NewRemoteSignalClientkeeps the socket, receipts, retry receipts, prekey uploads and QR pairing. Each received<enc>goes toRemoteSignal.Receiveand only delivery metadata returns (events.RemoteMessage), plus an own-device app-state key share. Outgoing messages are stanzas the remote store built, sent withSendPrebuiltStanza. The Rust side is polymorfa/whatsapp-rust#5.Stacked on #43 because both change the same Signal call sites.
Impact
Impact table
RemoteKeysguard,events.RemoteMessageremote_signal.go,message.go,pair.go,retry.go,send.go,store/RemoteSignalover the runner–service contract; not startedErrRemoteSignalUnsupportedmeanwhilemsmsg) on remote keysNackMissingMessageSecretdecryptMessagesRemoteNo impact table
NewClient,NewShadowClient)isRemoteSignal(); the only shared refactor moves the incoming-retry limit check intoacceptIncomingRetryunchangedgo test ./...passesVerification
Tested revision
a4e906eonpolymorfa/remote-cipher(base49a8071).go test -count=1 ./...: all packages pass.remote_signal_test.go: public-only device and theRemoteKeyspanic guard; receive delivers metadata and a receipt; already-delivered only acks; decrypt failure sends a retry receipt andUndecryptableMessage; a store outage neither acks nor sends a retry receipt; undecodable is nacked with 491; unuploaded prekeys are reused and only the uploaded batch is marked, and retry-receipt prekeys are marked published; pairing rejects a signature over different details, one that does not verify, and one from a different identity;SendPrebuiltStanzawaits for the ack and rejects non-message stanzas; retry receipts go toResendand honourPreRetryCallback; identity changes reachForgetIdentity, with hosted senders sent as their base user; a phone-number sender's sessions move to its LID throughMigratePNToLIDonce per user after success; localSendMessage,SendFBMessageandPairPhoneare refused.86ec616; each thread has a reply naming the change and its test. A re-check found finding 7 missed hosted devices and that PN-to-LID migration still ran against the unused local store; both are fixed ina4e906ewith Customer-held Signal state: fenced sealed store and Signal operations whatsapp-rust#5.go vet ./...andgofmt -l .: clean.Not verified: after a store outage the stanza is left unacknowledged; recovery relies on the server redelivering it, which is expected on the next reconnect but has not been observed. Unuploaded prekeys are tracked in memory, so a restart leaves them unused in the remote store. no remote store or real WhatsApp session has been connected; the runner implementation and the runner–service contract do not exist yet.
Session provenance and UI evidence
internal/agent-sessions/with the monorepo backend PR.d4c58f2; no published checkpoint yet.Rollout and rollback
Merge after #43 (this PR targets its branch). Nothing uses
NewRemoteSignalClientuntil the backend PR pins a HyperMeow revision containing it, so merging changes no runtime behaviour. Rollback: revert; no consumer exists. A remote-keyed device persists zero private-key fields; the runner must setRemoteKeysagain on load, which the backend PR covers.