Add sbx-mesh-invite and sbx-mesh-join CLI tools to secubox-master-link: - sbx-mesh-invite: Generate invite tokens with URL output (for masters) - sbx-mesh-join: Join mesh with token (for peers), uses HTTPS Add .claude/prompts/secubox-deb-masterlink.md: - API specification for implementing master-link on secubox-deb (VM) - Endpoints: status, invite, join, peers, approve, cleanup - Data structures for tokens.json and peers.json - Integration notes for existing LuCI UI Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
241 lines
5.3 KiB
Markdown
241 lines
5.3 KiB
Markdown
# SecuBox-Deb Master-Link API Implementation
|
|
|
|
## Overview
|
|
|
|
Implement the Master-Link mesh enrollment API for SecuBox-Deb (Debian/Ubuntu VM version). This allows the VM to act as a **master node** that can onboard OpenWrt peer nodes into the mesh network.
|
|
|
|
The API should be added to the existing P2P FastAPI service running at `/run/secubox/p2p.sock` and exposed via nginx at `https://<host>/api/v1/p2p/master-link/*`.
|
|
|
|
## Current State
|
|
|
|
- P2P service: `/usr/bin/uvicorn api.main:app --uds /run/secubox/p2p.sock`
|
|
- Token storage: `/var/lib/secubox/p2p/master-link/tokens.json`
|
|
- Peer storage: `/var/lib/secubox/p2p/master-link/peers.json`
|
|
- The existing VM already has partial master-link support
|
|
|
|
## Required API Endpoints
|
|
|
|
### 1. Status Endpoint
|
|
```
|
|
GET /master-link/status
|
|
```
|
|
Returns mesh status:
|
|
```json
|
|
{
|
|
"enabled": true,
|
|
"role": "master",
|
|
"depth": 0,
|
|
"max_depth": 3,
|
|
"upstream": null,
|
|
"fingerprint": "sb-<unique-id>",
|
|
"hostname": "secubox-vm-x64",
|
|
"auto_approve": false,
|
|
"peers": {
|
|
"pending": 0,
|
|
"approved": 3,
|
|
"rejected": 0,
|
|
"total": 3
|
|
},
|
|
"active_tokens": 1
|
|
}
|
|
```
|
|
|
|
### 2. Generate Invite Token
|
|
```
|
|
POST /master-link/invite
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"auto_approve": true,
|
|
"ttl": 3600
|
|
}
|
|
```
|
|
Returns:
|
|
```json
|
|
{
|
|
"token": "abc123def456...",
|
|
"hash": "sha256-hash-of-token",
|
|
"expires": "2026-03-26T16:00:00Z",
|
|
"expires_ts": 1774540800,
|
|
"ttl": 3600,
|
|
"auto_approve": true,
|
|
"url": "https://192.168.255.200/master-link/?token=abc123def456..."
|
|
}
|
|
```
|
|
|
|
**Token Generation Logic:**
|
|
```python
|
|
import secrets
|
|
import hashlib
|
|
from datetime import datetime, timedelta
|
|
|
|
token = secrets.token_hex(16) # 32 char hex string
|
|
token_hash = hashlib.sha256(token.encode()).hexdigest()
|
|
expires = datetime.now() + timedelta(seconds=ttl)
|
|
```
|
|
|
|
### 3. Join Endpoint (for peers)
|
|
```
|
|
POST /master-link/join
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"token": "abc123def456...",
|
|
"fingerprint": "owrt-0050430d1918",
|
|
"hostname": "C3BOX",
|
|
"address": "192.168.255.1",
|
|
"model": "Globalscale MOCHAbin"
|
|
}
|
|
```
|
|
|
|
**Validation Flow:**
|
|
1. Hash incoming token: `sha256(token)`
|
|
2. Find matching token in `tokens.json` by hash
|
|
3. Check token status is "active" and not expired
|
|
4. If `auto_approve` is true, immediately approve
|
|
5. Otherwise, queue for manual approval
|
|
|
|
**Success Response (auto-approved):**
|
|
```json
|
|
{
|
|
"status": "approved",
|
|
"fingerprint": "owrt-0050430d1918",
|
|
"message": "Welcome to the mesh",
|
|
"master_fingerprint": "sb-test123456",
|
|
"depth": 1
|
|
}
|
|
```
|
|
|
|
**Success Response (pending):**
|
|
```json
|
|
{
|
|
"status": "pending",
|
|
"fingerprint": "owrt-0050430d1918",
|
|
"message": "Awaiting master approval"
|
|
}
|
|
```
|
|
|
|
**Error Responses:**
|
|
```json
|
|
{"status": "error", "message": "Invalid or expired token"}
|
|
{"status": "error", "message": "Token already used"}
|
|
```
|
|
|
|
### 4. List Peers
|
|
```
|
|
GET /master-link/peers
|
|
```
|
|
Returns:
|
|
```json
|
|
{
|
|
"peers": [
|
|
{
|
|
"fingerprint": "owrt-0050430d1918",
|
|
"hostname": "C3BOX",
|
|
"address": "192.168.255.1",
|
|
"model": "Globalscale MOCHAbin",
|
|
"status": "approved",
|
|
"joined_at": "2026-03-26T14:00:32.721532",
|
|
"depth": 1
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### 5. Approve/Reject Peer
|
|
```
|
|
POST /master-link/approve
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"fingerprint": "owrt-0050430d1918",
|
|
"action": "approve" // or "reject"
|
|
}
|
|
```
|
|
|
|
### 6. Cleanup Tokens
|
|
```
|
|
POST /master-link/cleanup
|
|
```
|
|
Removes expired and used tokens.
|
|
|
|
## Data Structures
|
|
|
|
### tokens.json
|
|
```json
|
|
[
|
|
{
|
|
"hash": "sha256-of-token",
|
|
"type": "join",
|
|
"created": "2026-03-26T11:44:54.033842",
|
|
"expires": "2026-03-26T12:44:54.033842",
|
|
"expires_ts": 1774529094,
|
|
"ttl": 3600,
|
|
"status": "active", // active, used, expired
|
|
"auto_approve": true,
|
|
"peer_fp": null, // filled when used
|
|
"used_by": null,
|
|
"used_at": null
|
|
}
|
|
]
|
|
```
|
|
|
|
### peers.json
|
|
```json
|
|
[
|
|
{
|
|
"fingerprint": "owrt-0050430d1918",
|
|
"hostname": "C3BOX",
|
|
"address": "192.168.255.1",
|
|
"model": "Globalscale MOCHAbin",
|
|
"status": "approved",
|
|
"token_hash": "abc123...",
|
|
"joined_at": "2026-03-26T14:00:32.721532",
|
|
"depth": 1,
|
|
"last_seen": "2026-03-26T15:30:00.000000"
|
|
}
|
|
]
|
|
```
|
|
|
|
## CLI Tools (for reference)
|
|
|
|
The OpenWrt side has these CLI tools that interact with this API:
|
|
|
|
### sbx-mesh-invite (on master)
|
|
Generates invite token and outputs join URL/command.
|
|
|
|
### sbx-mesh-join (on peer)
|
|
Joins a mesh by sending join request with token.
|
|
|
|
```bash
|
|
# On master (VM)
|
|
sbx-mesh-invite --ip 192.168.255.200
|
|
# Output: Token and join URL
|
|
|
|
# On peer (OpenWrt)
|
|
sbx-mesh-join 192.168.255.200 <token>
|
|
```
|
|
|
|
## Implementation Notes
|
|
|
|
1. **HTTPS Required**: The join endpoint uses HTTPS (port 443), not HTTP port 7331
|
|
2. **Self-signed Certs**: Peers use `--no-check-certificate` (wget) or `-k` (curl)
|
|
3. **Token Security**: Tokens are one-time use; mark as "used" immediately upon successful join
|
|
4. **Auto-approve**: When `auto_approve=true`, skip manual approval step
|
|
5. **Fingerprint**: Use unique device identifier (MAC-based for OpenWrt, random for VMs)
|
|
|
|
## Integration with Existing UI
|
|
|
|
The existing LuCI UI at `/admin/services/secubox-mesh` shows:
|
|
- Node status (Role, Fingerprint, Peers, Chain)
|
|
- ZKP Authentication section
|
|
- Generate Token / Cleanup Tokens buttons
|
|
|
|
These buttons should call the API endpoints above.
|
|
|
|
## File Locations (secubox-deb)
|
|
|
|
- API source: `/srv/secubox/api/routers/master_link.py` (to create)
|
|
- Data dir: `/var/lib/secubox/p2p/master-link/`
|
|
- Config: `/etc/secubox/master-link.yaml`
|