@johnlester-0369/fb-messenger-e2ee
v0.1.4
Published
E2EE-only TypeScript/Bun toolkit for Facebook Messenger Noise, Signal Protocol, WA-binary, media, and DGW flows
Readme
FME - FB Messenger E2EE
FME - FB Messenger E2EE is a TypeScript/Bun toolkit focused on Facebook Messenger E2EE flows built on Noise, WA-binary, protobuf, and the Signal Protocol. Plaintext/non-E2EE messaging is intentionally left to fca-unofficial directly.
Key Features
- Native E2EE path: Signal Protocol sessions, prekeys, sender keys, Noise socket frames, and WA-binary nodes for encrypted Messenger chats.
- Group sender-key support: group
skmsgdecrypt/encrypt plus participant fanout of sender-key distribution messages (skdm). - Device persistence: JSON-backed
DeviceStorekeeps Noise keys, Signal identity, sessions, prekeys, signed prekeys, and sender keys across restarts. - Automatic prekey maintenance: replenishes server-side one-time prekeys without deleting or re-registering the E2EE device.
- Typed E2EE events: catch-all and typed event subscriptions for E2EE messages, receipts, reactions, errors, and raw frames.
- DGW support: optional Direct Gateway / LightSpeed socket helpers.
Tech Stack
- Runtime: Bun / Node-compatible APIs
- Language: TypeScript
- Encryption: @signalapp/libsignal-client
- Protocol: ProtobufJS + manual WA-binary/protobuf encoders
- Auth bootstrap bridge: fca-unofficial is used internally only for appState login/CAT bootstrap; use it directly for non-E2EE messaging.
Getting Started
1. Installation
bun install2. Basic Usage
import { FBClient } from "fb-messenger-e2ee";
const client = new FBClient({
appStatePath: "./appstate.json",
sessionStorePath: "./session.json",
platform: "facebook",
});
const { userId } = await client.connect();
// Keep this file. It is the registered E2EE device identity and Signal state.
await client.connectE2EE("./device-store.json", userId);
client.onEvent((event) => {
if (event.type === "e2ee_connected") {
console.log("E2EE stream is ready");
}
if (event.type === "e2ee_message") {
console.log(`[E2EE] ${event.data.threadId} ${event.data.senderJid}: ${event.data.text}`);
}
if (event.type === "error") {
console.error("Client error:", event.data.message);
}
});
await client.sendMessage({
threadId: "1234567890", // user ID, *.@msgr JID, or group JID
text: "Hello from the secure side!",
});Device Store & Key Maintenance
connectE2EE(deviceStorePath, userId) loads or creates a persistent device store. Do not delete it as a normal recovery step:
- Deleting it generates a new Noise key, Signal identity, registration ID,
facebook_uuid, and device registration. - Keeping it lets the same registered E2EE device continue using existing sessions and sender keys.
- The client now refreshes one-time prekeys automatically and does not need a new device registration for normal prekey exhaustion.
Configurable environment variables:
| Env | Default | Purpose |
|---|---:|---|
| FB_E2EE_PREKEY_SYNC_INTERVAL_MS | 1800000 | Periodic prekey check interval. Set 0 to disable. |
| FB_E2EE_PREKEY_MIN_COUNT | 5 | Upload more prekeys when server count falls below this. |
| FB_E2EE_PREKEY_UPLOAD_COUNT | 50 | Number of fresh one-time prekeys to upload per refill. |
Group decrypt note: if local sender_keys for a group/sender are truly missing, the client cannot derive that key locally. On retryable decrypt failures it sends an E2EE retry receipt to request a resend/SKDM from the sender/server. The send path also keeps a short in-memory retry cache so incoming receipt type="retry" requests for recently-sent messages can be re-encrypted directly to the requesting device. This is different from prekey exhaustion.
E2EE event identity model:
threadId: stable conversation ID. For DMs this is the bare Facebook user ID; for groups it is the group JID.chatJid: canonical E2EE chat JID. For DMs this isuser.0@msgr; for groups it is[email protected].senderJid: device-specific sender JID such asuser.160@msgr.senderDeviceId: numeric Messenger E2EE device ID when available.kind: normalized content kind (text,image,reaction,edit, etc.). Empty optional fields are omitted from the event payload.
Project Structure
src/
├── controllers/ # Thin orchestration + receive handlers
├── core/ # Public FBClient facade
├── e2ee/
│ ├── application/ # E2EEClient, retry/prekey/cache/fanout runtime helpers
│ ├── store/ # DeviceStore + JSON migration/repository
│ ├── transport/ # Noise, WA-binary, DGW sockets
│ ├── signal/ # Signal sessions, prekeys, sender keys
│ ├── message/ # Protobuf writer/builders/codecs/schemas
│ ├── media/ # Media crypto/upload
│ └── facebook/ # FB-specific SKMSG/SKDM/ICDC helpers
├── models/ # TypeScript interfaces & domain models
├── services/ # Auth/CAT bridge, ICDC, E2EE facade, media helpers
├── repositories/ # Session persistence
└── utils/ # Logger and conversion helpersTesting
npm run typecheck
npm test -- --runInBandManual echo script:
bun run tests/script/echo-e2ee.tsDocumentation
For the API reference and operational notes, see DOCS.md.
Acknowledgements
Special thanks to the fca-unofficial team for the foundational bridge work.
License
MIT © VangBanLaNhat
