Includes: - Backend services: ingestion (:8001), weather API (:8002), gateway (:8003), billing (:8004) with BTCPay integration - Shared asyncpg pool, TimescaleDB hypertable, Redis, Mosquitto MQTT - React frontend: Dashboard (MapLibre) and Messaging (chat UI) - Bridge daemon for Pi + Meshtastic (Serial/TCP T-Deck support) - Production Docker Compose, Nginx reverse proxy, ops scripts - DEPLOY.md with step-by-step deployment guide
189 lines
6.2 KiB
Markdown
189 lines
6.2 KiB
Markdown
# Messaging Gateway Architecture
|
|
|
|
The Messaging Gateway is the bridge between the internet (web users) and the Meshtastic mesh. It is the primary monetization surface for the project.
|
|
|
|
## Business Rules
|
|
|
|
1. **The mesh is open**: Anyone with a Meshtastic device can join the Kosmo mesh and send/receive messages locally for free.
|
|
2. **The gateway is gated**: Sending a message from the internet to the mesh requires an active subscription.
|
|
3. **Authorization granularity**:
|
|
- **Network-level**: Subscriber can send to any node reachable through the gateway.
|
|
- **Node-level**: Subscriber can send only to specific whitelisted nodes (e.g., family members).
|
|
- Future: **Group-level** access for organizations.
|
|
|
|
## Gateway Flow: Web → Mesh
|
|
|
|
```
|
|
User (Web Browser)
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ Web API │ <-- Validates JWT, checks subscription status
|
|
│ /messages │
|
|
└──────┬───────┘
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ Billing │ <-- Confirms subscriber has active plan & quota remaining
|
|
│ Service │
|
|
└──────┬───────┘
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ Message │ <-- Writes message to outbound queue (RabbitMQ / Redis)
|
|
│ Gateway │ Topic: `mesh.outbound.{node_id}`
|
|
└──────┬───────┘
|
|
│ MQTT / TLS
|
|
▼
|
|
┌──────────────┐
|
|
│ Infrastructure│ <-- Bridge daemon reads queue
|
|
│ Node │
|
|
└──────┬───────┘
|
|
│ Serial / protobuf API
|
|
▼
|
|
┌──────────────┐
|
|
│ Meshtastic │ <-- Broadcasts text message to target node ID
|
|
│ Radio │
|
|
└───────────────┘
|
|
```
|
|
|
|
## Gateway Flow: Mesh → Web
|
|
|
|
Replies and inbound messages from the mesh to a subscriber:
|
|
|
|
```
|
|
Meshtastic Radio (any node)
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ Infrastructure│ <-- Receives mesh message
|
|
│ Node │
|
|
└──────┬───────┘
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ Bridge │ <-- Publishes to `kosmo/mesh/inbound`
|
|
│ Daemon │
|
|
└──────┬───────┘
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ Message │ <-- Matches sender node ID to subscriber inboxes
|
|
│ Gateway │
|
|
└──────┬───────┘
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ Web API │ <-- Stores in user's inbox, sends push notification
|
|
│ Inbox │
|
|
└───────────────┘
|
|
```
|
|
|
|
## Subscription Models
|
|
|
|
### Plan Tiers (Example)
|
|
|
|
| Tier | Price | Messages/Month | Scope | Features |
|
|
|------|-------|----------------|-------|----------|
|
|
| Free | $0 | 5 (inbound only) | Inbox | Receive replies, view weather |
|
|
| Wanderer | $5/mo | 50 | Network | Send to any node |
|
|
| Guardian | $12/mo | 500 | Node-level | Manage up to 5 linked nodes |
|
|
| Sanctuary | $50/mo | Unlimited | Network + API | Bulk messaging, webhook access |
|
|
|
|
All paid plans are processed through the Church of Kosmo's self-hosted BTCPay Server at `pay.cqre.net`.
|
|
|
|
### Authorization Check
|
|
|
|
When a user attempts to send a message:
|
|
|
|
```python
|
|
def can_send(user: User, target_node_id: str) -> bool:
|
|
subscription = user.active_subscription()
|
|
if not subscription or subscription.is_expired():
|
|
return False
|
|
|
|
if subscription.plan == "network":
|
|
return True
|
|
|
|
if subscription.plan == "node_level":
|
|
return user.allowed_nodes.filter(mesh_node_id=target_node_id).exists()
|
|
|
|
return False
|
|
```
|
|
|
|
## Message Queue Schema
|
|
|
|
### Outbound (Cloud → Mesh)
|
|
```json
|
|
{
|
|
"message_id": "uuid-v4",
|
|
"sender_user_id": "uuid-v4",
|
|
"target_node_id": "!a1b2c3d4",
|
|
"text": "Hello from the web!",
|
|
"priority": "normal",
|
|
"max_hops": 7,
|
|
"want_ack": true,
|
|
"created_at": "2026-04-12T09:20:00Z",
|
|
"retry_count": 0
|
|
}
|
|
```
|
|
|
|
### Inbound (Mesh → Cloud)
|
|
```json
|
|
{
|
|
"message_id": "uuid-v4",
|
|
"source_node_id": "!a1b2c3d4",
|
|
"gateway_node_id": "!gateway01",
|
|
"text": "Reply from the woods",
|
|
"hop_count": 3,
|
|
"rssi": -90,
|
|
"snr": 8.5,
|
|
"received_at": "2026-04-12T09:25:00Z"
|
|
}
|
|
```
|
|
|
|
## Rate Limiting & Anti-Spam
|
|
|
|
- **Per-user**: Max 1 message per 10 seconds, burst of 5
|
|
- **Per-subscription tier**: Enforced monthly quotas
|
|
- **Per-target-node**: Max 10 web messages per hour (to prevent harassment)
|
|
- **Content filtering**: Basic profanity/spam filter on the gateway
|
|
- **Blocklist**: Users and nodes can block each other
|
|
|
|
## Delivery Tracking
|
|
|
|
The gateway tracks message state:
|
|
|
|
```
|
|
PENDING -> QUEUED -> TRANSMITTED -> DELIVERED (ACK received)
|
|
|
|
|
+-> FAILED (max retries exceeded)
|
|
```
|
|
|
|
Users see delivery status in the messaging UI:
|
|
- Single checkmark: Queued
|
|
- Double checkmark: Transmitted by gateway
|
|
- Blue double checkmark: Delivered (ACK from target node)
|
|
|
|
## Billing Integration
|
|
|
|
The gateway relies on the **Billing Service** to enforce subscriptions. The billing service:
|
|
- Creates invoices via BTCPay Server Greenfield API
|
|
- Listens to BTCPay webhooks for payment confirmation
|
|
- Manages subscription validity periods and quotas in PostgreSQL
|
|
- Deactivates old subscriptions and resets quotas on successful payment
|
|
|
|
## Security Considerations
|
|
|
|
1. **Authentication**: JWT-based auth for web users, API keys for bridge daemons
|
|
2. **Encryption**: Mesh messages are encrypted with the channel key. The bridge daemon does not decrypt content; it only forwards the encrypted payload.
|
|
3. **Privacy**: The gateway logs message metadata (sender, recipient, timestamp, size) but does not log message content.
|
|
4. **Node Impersonation**: Web messages are tagged with a special prefix or sender ID indicating they originated from the gateway, preventing spoofing of local mesh nodes.
|
|
|
|
## Fallback Behavior
|
|
|
|
If no infrastructure node is currently online:
|
|
- Outbound messages remain queued for up to 24 hours
|
|
- Users are notified that delivery is delayed
|
|
- If the queue expires, the message is marked as failed and the user's quota is refunded
|