If we rebuilt OpenMessage from scratch: the pairing-fragility problem
A design note prompted by the Nth time Google Messages died silently. The
recurring "SMS stopped syncing N days ago" failure is not a bug — it is the
architecture working as designed. This note asks what architecture would not
have that failure mode, and what to actually do about it.
The root cause, stated plainly
OpenMessage aggregates four networks into one local inbox:
| Network |
How we reach it |
Stability in practice |
| Signal |
signal-cli linked device |
rock-solid |
| WhatsApp |
whatsmeow companion device |
solid (occasional logout) |
| iMessage |
read chat.db on the Mac |
solid (local, read-only) |
| SMS/RCS |
libgm → messages.google.com web session |
fragile — dies every ~1–2 weeks |
Three of four are fine. The one that keeps failing is SMS/RCS, and the reason is
specific: it is the only network where we impersonate a web session whose two
endpoints are both controlled by Google. libgm reverse-engineers the "Messages
for web" client. Authentication is a bag of Google account cookies that rotate
about every 30 minutes plus a linked-device registration the phone can revoke
at any time, silently, with no callback. When the laptop is asleep or travelling
during a rotation, the stored cookies expire, every token refresh returns
HTTP 401, and the session is dead until someone does cookie surgery or a full
re-pair. Google can also just change the web client and break libgm wholesale.
Contrast Signal and WhatsApp: those companion-device protocols are designed for
a persistent second endpoint, the libraries (signal-cli, whatsmeow) are
mature and self-heal their own sessions, and — critically — we control one end
(the linked device) and the protocol expects it to persist. Google Messages
web expects a browser tab that reauthenticates constantly, and treats a
long-lived headless client as an anomaly to be evicted.
So "fix Android pairing" is really: how do you get SMS/RCS without impersonating
a browser session you don't own either end of?
The hard constraint: RCS is a Google monopoly on Android
Any redesign has to swallow this fact (confirmed, 2026):
- RCS on Android is exclusively Google Messages. The RCS APIs are hidden
behind an allowlist; only Google Messages (and a Samsung-only carve-out for
wearables) can register with Google's Jibe backend. There is no third-party
RCS API, rooted or not.
- SMS/MMS is open. Any app that is the default SMS app on Android has full
SMS/MMS access through the standard Telephony APIs.
Consequence: you cannot get RCS off an Android phone except through Google
Messages. You can only choose which end of Google Messages you attach to:
- Its web session (today's libgm) — impersonate the browser. Fragile for the
reasons above.
- The Google Messages app on the phone itself — read RCS from its
notifications / RCS content provider, send via notification RemoteInput or an
accessibility action. Ugly, but the phone's own RCS registration is
legitimate and does not silently unlink, because you are not competing for
a "linked device" slot — you are a local reader of an app that is already
signed in.
That reframing is the whole design: move the SMS/RCS attachment point from
Google's web endpoint (which Google controls and evicts) to the phone endpoint
(which the user controls and Google blesses).
Four architectures
A. Status quo, done right — local reverse-engineered bridges
Keep libgm/whatsmeow/signal-cli running locally. Accept that sessions die; make
death cheap and self-healing.
- Pros: zero extra devices, no phone app, everything on the Mac, works today.
- Cons: SMS/RCS fragility is intrinsic and unremovable — you are always one
Google web-client change away from a total break. You are renting a session
Google can cancel.
- What "done right" means (and what this session shipped): treat expired
cookies as an expected event and auto-refresh them from the user's real Chrome
profile in-process; retry token refresh on transient network errors (the
upstream libgm fix we had been missing because our fork was frozen at March);
and when self-heal genuinely can't recover, alert loudly instead of going
silent for 15 days.
B. Android companion app — control the phone end (the KDE Connect / Phone Link model)
A small app on the user's own phone talks to the Mac over a LAN/mesh link
(Tailscale, or a direct socket). This is exactly how KDE Connect and
Microsoft Phone Link mirror SMS today — proven at scale.
- SMS/MMS: the companion is (or cooperates with) the default SMS app and has
full, official Telephony access. Rock-solid; no reverse engineering.
- RCS: the companion reads inbound RCS from Google Messages' notifications /
RCS provider and sends via RemoteInput. Lossier than a real API (notification
text can be truncated; outbound is reply-shaped), but the registration is the
phone's own and does not evict.
- Transport: the phone is always on and with the user; a mesh link (Tailscale)
makes it reachable off-LAN without exposing anything publicly.
- Pros: kills the cookie/unlink fragility for SMS entirely; no Google web
session anywhere; the failure mode becomes "phone offline," which is visible
and self-correcting. This is the durable answer to the actual complaint.
- Cons: you must build and keep alive an Android app; RCS via notifications is
a compromise, not a clean API; iOS users are out of luck (but this is a
Mac+Android tool).
- Prior art that proves it: KDE Connect (open source, does SMS over LAN;
RCS is a long-standing open request precisely because of the Jibe lock),
Microsoft Phone Link (companion app mirrors SMS/notifications). The novel part
here is only "unify with WhatsApp/Signal/iMessage locally and expose it over
MCP" — which is OpenMessage's actual differentiator.
C. Hosted-Android device — a Pixel in a drawer
Run architecture B's companion on a dedicated always-on Android (a cheap Pixel on
a charger, or a cloud-hosted Android instance) that exposes an API the Mac
consumes.
- Pros: most robust RCS endpoint possible — a real, always-online,
Google-blessed Messages install; survives the user's own phone being off.
- Cons: a device to own and keep charged/updated; cloud Android is costly and
ToS-gray; more moving parts than most users want. This is the "power user /
server" tier, not the default.
D. Matrix bridges — the industrial version of A
Run the full mautrix-gmessages / mautrix-whatsapp / mautrix-signal bridges
against a headless Matrix homeserver and consume via the Matrix client-server API.
- Pros: mature bridge lifecycle management; one clean unified protocol
(Matrix) instead of per-platform Go glue; you inherit upstream fixes directly.
- Cons: heavy (homeserver + N bridges + a Matrix client); and it does not
solve the fragility — the Google bridge is the same libgm underneath, so the
same cookie/unlink death happens, just inside a bigger stack. Matrix is a better
integration substrate, not a fix for the pairing problem.
Recommendation
Two tiers, matching effort to how much the fragility actually costs.
-
Keep OpenMessage's shape — local-first, one inbox, MCP-native. That part
is right and is the product's whole reason to exist. None of the failures are
caused by the local-first or MCP choices. Do not rebuild toward a Matrix
stack (Option D) chasing robustness it won't deliver.
-
For SMS/RCS specifically, migrate the attachment point from Google's web
session to the phone (Option B). The insight is that
Google-Messages-for-web is a bridge where Google owns both ends and can evict
you on their schedule — which is exactly what keeps happening. An Android
companion app moves one end onto hardware the user controls and a registration
Google blesses. SMS becomes as solid as Signal is today; RCS becomes
"as good as the phone's notifications," which is strictly more durable than a
rotating-cookie web session even if occasionally lossier.
Concretely: a thin Android app (default SMS app for SMS/MMS; NotificationListener
- RemoteInput bridging Google Messages for RCS) that speaks a small local
protocol to the existing Go backend over Tailscale. The backend keeps
whatsmeow/signal-cli/iMessage exactly as they are and swaps the libgm route for
the companion route. libgm can stay as an optional no-extra-device fallback.
-
Until B exists, Option A "done right" is the stopgap — and most of it now
exists (in-process cookie self-heal, upstream libgm retry fix, loud
health alerts). That converts the failure from "silently dead for two weeks,
needs an expert to revive" into "auto-recovers, and if it can't, tells you in
one notification." That is enough to live on while B is built.
One-line version
The SMS pain is intrinsic to renting a Google web session. Three of the four
networks already work because we hold a legitimate endpoint; the fix for the
fourth is to hold a legitimate endpoint there too — an app on the phone — not to
keep impersonating a browser Google keeps logging out.
Sources for the RCS/companion claims:
If we rebuilt OpenMessage from scratch: the pairing-fragility problem
A design note prompted by the Nth time Google Messages died silently. The
recurring "SMS stopped syncing N days ago" failure is not a bug — it is the
architecture working as designed. This note asks what architecture would not
have that failure mode, and what to actually do about it.
The root cause, stated plainly
OpenMessage aggregates four networks into one local inbox:
signal-clilinked devicewhatsmeowcompanion devicechat.dbon the Maclibgm→ messages.google.com web sessionThree of four are fine. The one that keeps failing is SMS/RCS, and the reason is
specific: it is the only network where we impersonate a web session whose two
endpoints are both controlled by Google. libgm reverse-engineers the "Messages
for web" client. Authentication is a bag of Google account cookies that rotate
about every 30 minutes plus a linked-device registration the phone can revoke
at any time, silently, with no callback. When the laptop is asleep or travelling
during a rotation, the stored cookies expire, every token refresh returns
HTTP 401, and the session is dead until someone does cookie surgery or a fullre-pair. Google can also just change the web client and break libgm wholesale.
Contrast Signal and WhatsApp: those companion-device protocols are designed for
a persistent second endpoint, the libraries (
signal-cli,whatsmeow) aremature and self-heal their own sessions, and — critically — we control one end
(the linked device) and the protocol expects it to persist. Google Messages
web expects a browser tab that reauthenticates constantly, and treats a
long-lived headless client as an anomaly to be evicted.
So "fix Android pairing" is really: how do you get SMS/RCS without impersonating
a browser session you don't own either end of?
The hard constraint: RCS is a Google monopoly on Android
Any redesign has to swallow this fact (confirmed, 2026):
behind an allowlist; only Google Messages (and a Samsung-only carve-out for
wearables) can register with Google's Jibe backend. There is no third-party
RCS API, rooted or not.
SMS/MMS access through the standard Telephony APIs.
Consequence: you cannot get RCS off an Android phone except through Google
Messages. You can only choose which end of Google Messages you attach to:
reasons above.
notifications / RCS content provider, send via notification RemoteInput or an
accessibility action. Ugly, but the phone's own RCS registration is
legitimate and does not silently unlink, because you are not competing for
a "linked device" slot — you are a local reader of an app that is already
signed in.
That reframing is the whole design: move the SMS/RCS attachment point from
Google's web endpoint (which Google controls and evicts) to the phone endpoint
(which the user controls and Google blesses).
Four architectures
A. Status quo, done right — local reverse-engineered bridges
Keep libgm/whatsmeow/signal-cli running locally. Accept that sessions die; make
death cheap and self-healing.
Google web-client change away from a total break. You are renting a session
Google can cancel.
cookies as an expected event and auto-refresh them from the user's real Chrome
profile in-process; retry token refresh on transient network errors (the
upstream libgm fix we had been missing because our fork was frozen at March);
and when self-heal genuinely can't recover, alert loudly instead of going
silent for 15 days.
B. Android companion app — control the phone end (the KDE Connect / Phone Link model)
A small app on the user's own phone talks to the Mac over a LAN/mesh link
(Tailscale, or a direct socket). This is exactly how KDE Connect and
Microsoft Phone Link mirror SMS today — proven at scale.
full, official Telephony access. Rock-solid; no reverse engineering.
RCS provider and sends via RemoteInput. Lossier than a real API (notification
text can be truncated; outbound is reply-shaped), but the registration is the
phone's own and does not evict.
makes it reachable off-LAN without exposing anything publicly.
session anywhere; the failure mode becomes "phone offline," which is visible
and self-correcting. This is the durable answer to the actual complaint.
a compromise, not a clean API; iOS users are out of luck (but this is a
Mac+Android tool).
RCS is a long-standing open request precisely because of the Jibe lock),
Microsoft Phone Link (companion app mirrors SMS/notifications). The novel part
here is only "unify with WhatsApp/Signal/iMessage locally and expose it over
MCP" — which is OpenMessage's actual differentiator.
C. Hosted-Android device — a Pixel in a drawer
Run architecture B's companion on a dedicated always-on Android (a cheap Pixel on
a charger, or a cloud-hosted Android instance) that exposes an API the Mac
consumes.
Google-blessed Messages install; survives the user's own phone being off.
ToS-gray; more moving parts than most users want. This is the "power user /
server" tier, not the default.
D. Matrix bridges — the industrial version of A
Run the full
mautrix-gmessages/mautrix-whatsapp/mautrix-signalbridgesagainst a headless Matrix homeserver and consume via the Matrix client-server API.
(Matrix) instead of per-platform Go glue; you inherit upstream fixes directly.
solve the fragility — the Google bridge is the same libgm underneath, so the
same cookie/unlink death happens, just inside a bigger stack. Matrix is a better
integration substrate, not a fix for the pairing problem.
Recommendation
Two tiers, matching effort to how much the fragility actually costs.
Keep OpenMessage's shape — local-first, one inbox, MCP-native. That part
is right and is the product's whole reason to exist. None of the failures are
caused by the local-first or MCP choices. Do not rebuild toward a Matrix
stack (Option D) chasing robustness it won't deliver.
For SMS/RCS specifically, migrate the attachment point from Google's web
session to the phone (Option B). The insight is that
Google-Messages-for-web is a bridge where Google owns both ends and can evict
you on their schedule — which is exactly what keeps happening. An Android
companion app moves one end onto hardware the user controls and a registration
Google blesses. SMS becomes as solid as Signal is today; RCS becomes
"as good as the phone's notifications," which is strictly more durable than a
rotating-cookie web session even if occasionally lossier.
Concretely: a thin Android app (default SMS app for SMS/MMS; NotificationListener
protocol to the existing Go backend over Tailscale. The backend keeps
whatsmeow/signal-cli/iMessage exactly as they are and swaps the libgm route for
the companion route. libgm can stay as an optional no-extra-device fallback.
Until B exists, Option A "done right" is the stopgap — and most of it now
exists (in-process cookie self-heal, upstream libgm retry fix, loud
health alerts). That converts the failure from "silently dead for two weeks,
needs an expert to revive" into "auto-recovers, and if it can't, tells you in
one notification." That is enough to live on while B is built.
One-line version
The SMS pain is intrinsic to renting a Google web session. Three of the four
networks already work because we hold a legitimate endpoint; the fix for the
fourth is to hold a legitimate endpoint there too — an app on the phone — not to
keep impersonating a browser Google keeps logging out.
Sources for the RCS/companion claims: