Design and Architecture
This page goes deeper into how Astra is structured internally. If you just want to use the library, Core Concepts is enough. This page is for contributors and people who want to understand the design decisions.
Layer diagram
+---------------------------------------------------+
| Your application (app.py / bot.py) |
+---------------------------------------------------+
| astra.Client |
| .chat .group .account (method mixins) |
+---------------------------------------------------+
| astra.protocol.ProtocolBridge (gateway.py) |
| - call() - ensure_bridge() - diagnostics |
+---------------------------------------------------+
| astra.core.bridge.* (domain bridges) |
| chat.py group.py media.py account.py ... |
+---------------------------------------------------+
| astra.protocol.js_engine (injected JS) |
+---------------------------------------------------+
| Playwright (page.evaluate) |
+---------------------------------------------------+
| Chromium browser --> WhatsApp Web servers |
+---------------------------------------------------+
Module breakdown
- astra.client
The public-facing
Clientclass and its supporting infrastructure:client.py– the main Client class withstart(),stop(),
run_forever(), and the startup banner.authenticator.py– QR and phone-pairing authentication flow.sync_engine.py– background heartbeat, stall detection, reconnection.lifecycle.py– state machine (OFFLINE, STARTING, READY, etc.).session_store.py– cookie and local-storage persistence.methods/– chat, group, account method mixins.
- astra.protocol
The transport layer between Python and JavaScript:
gateway.py–ProtocolBridge.call()with automatic retry,
error classification, and self-healing.
js_engine.py– the JavaScript source injected into WhatsApp Web.validators.py– JID and input validation.
- astra.core.bridge
Domain-specific bridges that translate high-level Python calls into specific JS method invocations:
chat.py,group.py,media.py,account.py,
contact.py,privacy.py,status.py,diagnostic.py- astra.errors
Structured error handling:
codes.py– 71 error codes in 8 categories.base.py–AstraErrorwith severity, retryable, cause, to_dict.exceptions.py– 40+ typed exception classes.
- astra.events
The event pipeline:
dispatcher.py– routes browser events to registered handlers.emitter.py– fire-and-forget event emission.filters.py– composable message matchers (command, regex, etc.).context.py–EventContextwrapping each incoming event.
- astra.models
Data classes representing WhatsApp entities:
message.py–Messagewith reply, react, delete helpers.chat.py–Chatwith title, participants, metadata.user.py–UserandJID.enums.py–ClientStatusand other enumerations.
- astra.connection
Browser lifecycle:
browser_manager.py– launch, lock, crash recovery, console relay.page_controller.py– navigation, cache clearing, page health.reconnect_policy.py– exponential backoff with jitter.
Error classification in the gateway
When a JS bridge call fails, the gateway doesn’t just re-raise the raw Playwright error. It classifies it:
JS exception text --> Python exception
-------------------------------------------------------
"method not found" --> BridgeMethodNotFoundError
"timeout" --> MessageTimeoutError
"rate" / "too many" --> RateLimitedError
"target closed" / "page" --> ConnectionLostError
anything else --> BridgeCallError
This means your code can catch specific errors and react appropriately.
Self-healing bridge
The bridge connection can break for several reasons: page reload, navigation, Chromium garbage collection. Astra handles this automatically:
A
call()fails with a recoverable error.The gateway calls
ensure_bridge()to check if the JS engine is alive.If not, it re-injects the engine and retries the call once.
If that also fails, it raises a typed exception.
The SyncEngine adds another layer: if no events arrive for 90 seconds, it assumes the bridge is stale and triggers a recovery cascade (re-bind listeners, re-init Store, full restart).
Design principles
- No protocol RE.
Astra never reverse-engineers the WhatsApp binary protocol. It drives the official web client, which means it stays compatible as WhatsApp updates.
- Fail loud, fail typed.
Every failure path has a named exception with a code, hint, and retry flag. No bare
except Exceptionblocks in the library.- Async all the way.
Every I/O operation is
async. No sync wrappers, no threading hacks.- Single source of truth.
Error codes live in
codes.py. Version lives inpyproject.tomland is synced to__init__.__version__.