feat(mesh): NATS JetStream transport replacing MQTT

Replace the Mosquitto/paho mesh transport with NATS JetStream KV:
- mesh_wire.py: transport-agnostic registry (de)serialization (secret-stripped)
- nats_client.py: async CastleNATSClient — registry in the castle-registry KV
  bucket, heartbeat liveness, delete-on-stop offline, watch-driven mesh state
- rewire main/config/models/nodes; MeshStatus -> connected/nats_url
- frontend MeshStatus + MeshPanel to the new fields
- drop paho-mqtt/mqtt_client + _mqtt._tcp mDNS browse; add nats-py
- keep the secret-stripping invariant; mDNS peer discovery retained

Single-node verified; two-node parity pending the second node.
This commit is contained in:
2026-07-07 04:55:02 -07:00
parent f826d972b8
commit 6c00a9d33c
12 changed files with 436 additions and 76 deletions

View File

@@ -19,25 +19,23 @@ export function MeshPanel({ mesh }: MeshPanelProps) {
<span
className={cn(
"inline-flex items-center gap-1.5 text-xs px-2 py-0.5 rounded-full",
mesh.mqtt_connected
mesh.connected
? "bg-green-800/50 text-green-300"
: "bg-red-800/50 text-red-300",
)}
>
{mesh.mqtt_connected ? (
{mesh.connected ? (
<Wifi size={10} />
) : (
<WifiOff size={10} />
)}
{mesh.mqtt_connected ? "connected" : "disconnected"}
{mesh.connected ? "connected" : "disconnected"}
</span>
</div>
<div className="flex items-center gap-4 text-xs text-[var(--muted)]">
{mesh.mqtt_broker_host && (
<span className="font-mono">
mqtt://{mesh.mqtt_broker_host}:{mesh.mqtt_broker_port}
</span>
{mesh.nats_url && (
<span className="font-mono">{mesh.nats_url}</span>
)}
{mesh.mdns_enabled && (
<span>mDNS active</span>