Skip to content
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
2ceca7e
feat(client): add canonical low-level builder
jlucaso1 Jul 21, 2026
a569b50
refactor(bot): build clients through canonical pipeline
jlucaso1 Jul 21, 2026
de5d5bc
feat(client): add generation-scoped extension lifecycle
jlucaso1 Jul 21, 2026
1f28c48
fix(client): retain superseded scopes until cleanup
jlucaso1 Jul 21, 2026
a0da3e2
fix(client): harden extension lifecycle callbacks
jlucaso1 Jul 21, 2026
b42a2df
feat(events): add removable filtered subscriptions
jlucaso1 Jul 21, 2026
c2ead6c
docs(client): clarify lifecycle termination semantics
jlucaso1 Jul 21, 2026
bc16830
fix(client): reference-count raw node forwarding
jlucaso1 Jul 21, 2026
3bd4bd5
fix(client): make lifecycle teardown cancellation-safe
jlucaso1 Jul 21, 2026
66f464a
docs(client): clarify lifecycle test hook constraints
jlucaso1 Jul 21, 2026
835d61d
feat(plugins): add transactional native plugin host
jlucaso1 Jul 21, 2026
7c2e584
perf(plugins): keep native host opt-in
jlucaso1 Jul 21, 2026
e41901a
fix(plugins): harden host lifecycle ownership
jlucaso1 Jul 21, 2026
510c55e
Merge updated main into plugin architecture
jlucaso1 Jul 21, 2026
9e7aba1
fix(plugins): make terminal cleanup cancellation-safe
jlucaso1 Jul 21, 2026
776f907
fix(core): harden reentrant teardown races
jlucaso1 Jul 21, 2026
35d3315
fix(plugins): avoid reentrant teardown deadlock
jlucaso1 Jul 21, 2026
2137fcb
fix(plugins): close late lifecycle ownership races
jlucaso1 Jul 21, 2026
9cef55d
feat(plugins): add bounded custom event routing
jlucaso1 Jul 21, 2026
57ec05d
feat(plugins): prove native metrics vertical slice
jlucaso1 Jul 21, 2026
06050d0
fix(lifecycle): guarantee terminal cleanup signaling
jlucaso1 Jul 21, 2026
5b4a3b4
fix(plugins): drain scoped tasks before teardown
jlucaso1 Jul 21, 2026
47dcf10
fix(lifecycle): bound and compact callback backlog
jlucaso1 Jul 21, 2026
1ee952b
fix(lifecycle): retire scopes when reconnect starts
jlucaso1 Jul 21, 2026
c86f6d5
fix(plugins): seal construction and rollback races
jlucaso1 Jul 21, 2026
63bb4d9
perf(plugins): make lifecycle integration opt in
jlucaso1 Jul 21, 2026
54cbf95
fix(plugins): make terminal rollback unwind safe
jlucaso1 Jul 21, 2026
271d842
fix(plugins): isolate resource teardown panics
jlucaso1 Jul 21, 2026
e2f6bc4
fix(plugins): harden lifecycle cleanup under faults
jlucaso1 Jul 21, 2026
e72f272
fix(lifecycle): preserve all scope closure callbacks
jlucaso1 Jul 21, 2026
667689a
feat(plugins): add native observability
jlucaso1 Jul 21, 2026
3244e32
docs(lifecycle): clarify scope closure lock contract
jlucaso1 Jul 21, 2026
dc55c1e
docs(plugins): define host and adapter contracts
jlucaso1 Jul 21, 2026
4788a1d
Merge remote-tracking branch 'origin/main' into agent/plugin-architec…
jlucaso1 Jul 21, 2026
69aaca2
fix(lifecycle): bind ready publication to its generation
jlucaso1 Jul 21, 2026
fb263ec
fix(plugins): attribute spawned task panics
jlucaso1 Jul 21, 2026
a09f007
docs(plugins): clarify feature and task contracts
jlucaso1 Jul 21, 2026
7f025d3
fix(lifecycle): gate direct connect during construction
jlucaso1 Jul 22, 2026
b4b9c63
fix(plugins): attribute evictions to event owners
jlucaso1 Jul 22, 2026
f1a8138
fix(plugins): publish APIs at construction commit
jlucaso1 Jul 22, 2026
719011e
fix(plugins): close lifecycle review races
jlucaso1 Jul 22, 2026
38d20ab
Merge remote-tracking branch 'origin/main' into agent/plugin-architec…
jlucaso1 Jul 22, 2026
d4abef1
Merge remote-tracking branch 'origin/main' into agent/plugin-architec…
jlucaso1 Jul 22, 2026
29481d9
feat(plugins): add manifest-keyed untyped adapters
jlucaso1 Jul 22, 2026
03c4b8b
feat(plugins): own core event subscriptions
jlucaso1 Jul 22, 2026
1444153
feat(plugins): support cooperative task draining
jlucaso1 Jul 22, 2026
60fda0b
fix(plugins): bound partial installation cleanup
jlucaso1 Jul 22, 2026
bfdb269
fix(plugins): release closed subscription registry entries
jlucaso1 Jul 22, 2026
21b430c
docs(plugins): define subscription ownership
jlucaso1 Jul 22, 2026
30d06ff
refactor(plugins): leave capability expansion room
jlucaso1 Jul 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Read these when working on the relevant area:
- `agent_docs/debugging.md` — evcxr REPL, binary protocol debugging
- `agent_docs/binary_size_ci.md` — size-tracking CI: metrics, budgets, baseline semantics
- `agent_docs/observability.md` — per-session stats (I/O, memory report, TaskInstrument/CPU), design rules
- `agent_docs/plugin_architecture.md` — native plugin host, lifecycle/capability invariants, future foreign adapter seam
- `agent_docs/signal_durability.md` — Signal counter leases, pre-wire gates, crash recovery, review checklist

When adding comments to the code, dont be so verbose, also only explain why, not what
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ repository = "https://github.com/jlucaso1/whatsapp-rust"
readme = "README.md"
description = "Rust client for WhatsApp Web"

[package.metadata.docs.rs]
features = ["plugins"]
rustdoc-args = ["--cfg", "docsrs"]

[workspace]
members = [
".",
"examples/voip-cli",
"http_clients/ureq-client",
"plugins/metrics",
"storages/chat-store",
"storages/sqlite-storage",
"tests/bench-integration",
Expand Down Expand Up @@ -49,6 +54,9 @@ disallowed_methods = "deny"
# portable_atomic. Host-only test/bench counters carry an inline allow.
disallowed_types = "deny"

[workspace.lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(docsrs)"] }

[workspace.dependencies]
# Shared dependencies
aes = "0.9.1"
Expand Down Expand Up @@ -112,6 +120,12 @@ zlib-rs = { version = "0.6.5", default-features = false, features = ["std", "rus

[features]
debug-snapshots = ["wacore/debug-snapshots"]
# Generation-scoped extension lifecycle. Kept opt-in so ordinary clients do not
# retain lifecycle state or branches when no extension host is present.
client-lifecycle = []
# Build-time native plugin host. Kept opt-in so clients that do not use plugins
# retain the pre-host binary footprint.
plugins = ["client-lifecycle", "dep:bon"]
# Optional observability. Off by default: no `tracing` dep, zero overhead.
# Emits tracing spans/events only; the application installs the subscriber
# (and any OpenTelemetry bridge). See examples/observability.rs.
Expand Down Expand Up @@ -170,6 +184,7 @@ async-trait = { workspace = true }
base64 = { workspace = true }
buffa = { workspace = true }
bytes = { workspace = true }
bon = { workspace = true, optional = true }
chrono = { workspace = true, features = ["clock"] }
event-listener = { workspace = true }
futures = { workspace = true, features = ["std"] }
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ A high-performance, async Rust library for the WhatsApp Web API. Inspired by [wh
- **Profile** — Set push name, status text, profile picture
- **Privacy** — Fetch/set privacy settings, disappearing messages
- **Modular** — Pluggable storage, transport, HTTP client, and async runtime; SQLite, Tokio WebSocket, and ureq ship as the defaults, swap any of them with `default-features = false`
- **Native plugins** — Build-time, type-safe extensions with scoped capabilities and lifecycle ownership behind the `plugins` feature
- **Runtime agnostic** — Bring your own async runtime via the `Runtime` trait (Tokio included by default)

For the full API reference and guides, see the **[documentation](https://whatsapp-rust.jlucaso.com)**.
Expand Down Expand Up @@ -60,6 +61,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

The default cargo features wire up the Tokio WebSocket transport, the ureq HTTP client, the SQLite store, and the Tokio runtime; only the storage backend has to be chosen explicitly. Every piece is replaceable through the builder (`with_transport_factory`, `with_http_client`, `with_runtime`) for custom environments such as wasm or embedded targets.

Native plugin APIs are opt-in: use `features = ["plugins"]` when implementing a
plugin in the application. Published plugin crates can enable that feature in
their own `whatsapp-rust` dependency, and Cargo feature unification activates it
for the consumer. See [`agent_docs/plugin_architecture.md`](agent_docs/plugin_architecture.md)
for the host contract and type-safe API example.

### One dependency is enough

`whatsapp-rust` re-exports the whole stack, so you never need to declare the sibling crates (`wacore`, `wacore-binary`, `waproto`, `whatsapp-rust-tokio-transport`, `whatsapp-rust-ureq-http-client`, `whatsapp-rust-sqlite-storage`) yourself, including when pinning a git revision:
Expand Down
28 changes: 24 additions & 4 deletions agent_docs/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,30 @@ figures come from the `wacore::stats::HeapSize` trait:
Semantics: honest estimates for attribution and leak detection, not
byte-exact accounting. The e2e `memory_soak.rs` logs the byte totals next to
RSS; its growth-bound assertions are on entry counts.
When a new cache is added to `Client`, add it to `memory_report()` (the
`MemoryReport::collections()` list keeps the total and `Display` in sync) and
— if it can dominate memory — implement `HeapSize` for its value type next to
that type's definition.
When a new cache is added to `Client`, add it to `memory_report()` (the common
`MemoryReport::collections()` list or its feature-gated report section) and —
if it can dominate memory — implement `HeapSize` for its value type next to that
type's definition.

With the opt-in `plugins` feature, the report also includes installed plugins,
active install/connection tasks, retained connection generations, core-event
subscriptions, custom-event endpoints, and unique queued payload bytes. Fanout
shares one envelope, so queued payload memory is counted once even when several
endpoints retain it.

### Plugin host snapshots (opt-in)

`Client::plugin_stats()` is computed only when called and returns lifecycle,
health, task, subscription, and custom-event counters keyed by public manifest
ID. `PluginEventRouter::stats()` provides endpoint capacity, current unique
queue retention, and cumulative delivery/backpressure totals; publishers can
read their own totals through `PluginEvents::stats()`.

Health is sticky for the lifetime of the host: lifecycle errors/panics,
timeouts, spawned-task panics, task-drain timeouts, isolated core-event panics,
resource teardown panics, publication failures, and queue drops mark only the
responsible plugin as degraded. Concurrent snapshots are intentionally
approximate, and carry no message content, JIDs, or phone numbers.

### 3. `BotBuilder::with_task_instrument` — CPU / custom attribution (opt-in)

Expand Down
Loading
Loading