# Mesh Discovery Architecture

Styrene TUI now displays **live mesh devices** discovered via Reticulum announces instead of static inventory files.

## Conceptual Shift

**Old Way** (Static Inventory):
```
Devices defined in YAML → Dashboard shows static list
No connection to actual mesh activity
```

**New Way** (Live Discovery):
```
Devices announce on mesh → Styrene listens → Dashboard shows LIVE mesh
What you see = what's actually on the network RIGHT NOW
```

## Device Types

Styrene recognizes and categorizes different device types:

### 1. Styrene Nodes
**Announce Format**: `styrene:<hostname>:<version>:<capabilities>`

Example: `styrene:rpi-edge-01:0.1.0:ssh,deploy,logs`

**Display**: Green "STYRENE" badge in device table

**Capabilities**:
- SSH access via rnsh
- Remote deployment
- Log streaming
- Configuration management

### 2. RNodes
**Announce Format**: `rnode:<device_name>`

Example: `rnode:rnode-handheld-01`

**Display**: Cyan "RNODE" badge in device table

**What They Are**: RNode hardware devices (radio interfaces, handheld units, etc.)

**Capabilities**:
- Mesh routing
- Radio interface status
- Signal quality monitoring

### 3. Generic Announces
**Announce Format**: Any UTF-8 string without special prefix

Example: `nomadnet-node`, `my-custom-service`

**Display**: Dim "GENERIC" badge

**What They Are**: Other Reticulum services/endpoints on the mesh

### 4. Unknown
**Announce Format**: No app_data or binary data

**Display**: Dim "UNKNOWN" badge

## Dashboard Layout

```
┌─────────────────────────────────────────────────────────┐
│ MESH STATUS              │ RETICULUM                    │
│ ─────────────            │ ──────────                   │
│ STYRENE:  5              │ STATUS: online               │
│ RNODES:   2              │ TRANSPORT: disabled          │
│ ACTIVE:   6              │ INTERFACES: 2                │
│ STALE:    1              │                              │
├─────────────────────────────────────────────────────────┤
│ MESH DEVICES                                            │
│ ─────────────────────────────────────────────────────── │
│ NAME              TYPE      IDENTITY  STATUS   LAST     │
│ rpi-edge-01       STYRENE   a1b2c3d4  ACTIVE   2s ago   │
│ rpi-edge-02       STYRENE   e5f6a7b8  ACTIVE   5s ago   │
│ rnode-handheld    RNODE     1234abcd  ACTIVE   10s ago  │
│ nomadnet-node     GENERIC   9876fedc  STALE    8m ago   │
└─────────────────────────────────────────────────────────┘
```

## Node Status

Based on last announce time:

| Status | Time Since Last Announce | Color |
|--------|-------------------------|-------|
| **ACTIVE** | < 5 minutes | Green |
| **STALE** | 5-15 minutes | Yellow |
| **LOST** | > 15 minutes | Red |

## Mesh Status Panel

Shows live mesh statistics:

- **STYRENE**: Count of Styrene-managed nodes
- **RNODES**: Count of RNode devices
- **ACTIVE**: Nodes that announced recently
- **STALE**: Nodes that haven't announced in a while

## How It Works

### Initialization

When Styrene TUI launches:

1. Initialize RNS service
2. Start announce listener (`start_discovery()`)
3. Register callback to update dashboard on new announces

### Announce Handling

When a device announces on the mesh:

1. `StyreneAnnounceHandler.received_announce()` is called by RNS
2. Parse app_data to determine device type
3. Create/update `MeshDevice` object
4. Invoke callback to update dashboard
5. Dashboard refreshes device table

### Discovery Functions

```python
from styrene.services.reticulum import (
    discover_devices,      # Get all discovered devices
    get_styrene_devices,   # Get only Styrene nodes
    get_rnodes,            # Get only RNodes
)

# Get all mesh devices
devices = discover_devices()

for device in devices:
    print(f"{device.name} - {device.device_type.value}")
    print(f"  Status: {device.status.value}")
    print(f"  Last seen: {device.last_seen_display}")
    print(f"  Announces: {device.announce_count}")
```

## Announcing as a Styrene Node

To make a device appear as a Styrene node:

```python
import RNS

# Initialize RNS
reticulum = RNS.Reticulum()
identity = RNS.Identity()

# Create destination
dest = RNS.Destination(
    identity,
    RNS.Destination.IN,
    RNS.Destination.SINGLE,
    "styrene_endpoint",
    "node"
)

# Announce with Styrene format
hostname = "rpi-edge-01"
version = "0.1.0"
capabilities = "ssh,deploy,logs"
app_data = f"styrene:{hostname}:{version}:{capabilities}".encode("utf-8")

dest.announce(app_data=app_data)
```

## Announcing as an RNode

```python
# RNode format
device_name = "rnode-handheld-01"
app_data = f"rnode:{device_name}".encode("utf-8")

dest.announce(app_data=app_data)
```

## Testing

### Simulating Devices

```python
#!/usr/bin/env python3
"""Simulate mesh devices for testing."""

import RNS
import time

# Initialize
reticulum = RNS.Reticulum()

# Create multiple test identities
devices = [
    ("styrene:test-node-01:0.1.0:ssh", "styrene"),
    ("styrene:test-node-02:0.1.0:deploy", "styrene"),
    ("rnode:test-rnode-01", "rnode"),
    ("generic-service", "generic"),
]

destinations = []

for app_data_str, device_type in devices:
    identity = RNS.Identity()
    dest = RNS.Destination(
        identity,
        RNS.Destination.IN,
        RNS.Destination.SINGLE,
        "test",
        device_type
    )
    destinations.append((dest, app_data_str))

# Announce periodically
print("Simulating devices...")
while True:
    for dest, app_data_str in destinations:
        dest.announce(app_data=app_data_str.encode("utf-8"))
        print(f"Announced: {app_data_str}")

    time.sleep(60)  # Every minute
```

### CLI Testing

```bash
# View discovered devices
python -m styrene.cli.reticulum_cli discover

# Output:
# ✓ STYRENE | test-node-01         | a1b2c3d4... | announces: 5
# ✓ STYRENE | test-node-02         | e5f6a7b8... | announces: 5
# ✓ RNODE   | test-rnode-01        | 1234abcd... | announces: 5
#   OTHER   | generic-service      | 9876fedc... | announces: 5
```

## Future Enhancements

### Device Actions

Clicking on a device will show:

- **Styrene Nodes**: SSH button, deploy tools, logs viewer
- **RNodes**: Signal quality, routing stats
- **Generic**: View announce data, attempt connection

### Filtering

Add filters to device table:

- Show only Styrene nodes
- Show only active devices
- Hide generic announces

### Search

Search devices by name or identity.

### Device Details Screen

Full detail view for selected devices showing:

- Full identity hash
- All capabilities
- Announce history
- Connection options

## Migration Path

The old static fleet inventory (`~/.config/styrene/fleet.yaml`) is no longer the primary source of truth. Instead:

1. **Discovery-first**: Devices appear when they announce
2. **Metadata overlay**: Fleet file can still store notes, profiles, static IPs
3. **Hybrid mode**: Combine discovered devices with fleet metadata

Future implementation will allow:
- Saving discovered devices to fleet file
- Enriching mesh devices with fleet metadata
- Flagging "missing" devices (in fleet but not announcing)

## See Also

- [Standalone Usage](standalone-usage.md) - Using discovery without TUI
- [CLI Tools](../CLI-TOOLS.md) - Testing mesh discovery from command line
