Files
kosmo-connect/docs/architecture/messaging-gateway.md
Tomas Kracmar 0a4fb7b55e
Some checks failed
CI / lint-docs (push) Has been cancelled
CI / build-firmware (push) Has been cancelled
CI / test-backend (push) Has been cancelled
CI / test-web (push) Has been cancelled
feat: initial KosmoConnect platform v0.1
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
2026-04-12 17:30:15 +02:00

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