Files
sovereign_browser/plans/fips-network-view.md
T

12 KiB

FIPS Network View — sovereign://fips/network

Goal

Extend the FIPS management page with a "Network" view that shows the entire mesh as the local node knows it — not just direct peers, but every node the daemon has discovered via bloom-filter propagation and Nostr discovery. This answers "what can you see on FIPS?" and "can you get a view of the entire network?"

What the FIPS daemon exposes

The FIPS control protocol (verified in ~/lt/fips/src/control/) has these read-only commands relevant to a network view:

Command What it returns Network-view use
show_status estimated_mesh_size (bloom-union cardinality), peer_count, tree_depth, is_root, root Header summary: "Mesh: ~N nodes, depth D"
show_peers Direct authenticated peers with npub, display_name, ipv6_addr, connectivity, is_parent, is_child, tree_depth, transport_addr, transport_type, MMP metrics The "direct peers" table (already shown)
show_tree Spanning-tree state: root, root_npub, my_node_addr, depth, parent, parent_display_name, peers[] (each with node_addr, display_name, depth, coords path, distance_to_us) Tree topology view — local node + 1-hop neighbors with their tree positions
show_identity_cache All known nodes in the mesh: entries[] with node_addr, npub, display_name, ipv6_addr, last_seen_ms, age_ms, plus count and max_entries The mesh node list — every node the local daemon has learned about, not just direct peers
show_bloom Per-peer bloom-filter state (filter sequence, estimated counts) Advanced/debug — shows how node-discovery knowledge propagates
show_routing Coord-cache entries, pending lookups, retries, forwarding counters Advanced/debug — routing health

Key insight: "the entire network" = identity cache + tree

The FIPS daemon does not have a "ask peer X what peers it has" command. The mesh is observed through two mechanisms:

  1. Bloom filters — each node broadcasts a bloom filter of node addresses it knows about. The local node unions these to estimate total mesh size (estimated_mesh_size) and caches individual node identities it has resolved (show_identity_cache).

  2. Spanning tree — each node declares a parent; the tree coordinates encode paths to the root. show_tree gives the local node's tree position and its neighbors' positions.

So "the entire network" as seen from this node is:

  • show_identity_cache → the set of known nodes (npub, ipv6, name, last-seen). This is the node list for the network view.
  • show_tree → the local tree topology (who is my parent, who are my children, what is the root, what is the depth).
  • show_status.estimated_mesh_size → a single estimated total.

We cannot build a full graph of who-connects-to-whom from the local node's control socket alone — that would require every node to expose its peer list, and FIPS doesn't do that (bloom filters propagate existence, not adjacency). What we can show is:

  • A node list (identity cache) — "these are all the nodes in the mesh that this node knows about."
  • A tree view — "this is my position in the spanning tree, my parent, my children, the root."
  • A mesh summary — "estimated N nodes total, tree depth D."

Design

Page layout

Add a "Network" tab/section to sovereign://fips (or a separate sovereign://fips/network page linked from the main FIPS page).

┌─────────────────────────────────────────────────────────────┐
│  FIPS Mesh Network                              [Refresh]   │
│  [Status] [Peers] [Network] [Tree]                          │
├─────────────────────────────────────────────────────────────┤
│  ┌─ Mesh Summary ────────────────────────────────────────┐  │
│  │  Estimated mesh size:  47 nodes                       │  │
│  │  Known nodes (cache):  23                              │  │
│  │  Tree depth:           4                               │  │
│  │  Root:                 npub1abc... (laantungir)        │  │
│  │  Our position:         depth 2, parent npub1def...     │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌─ Known Nodes (23) ────────────────────────────────────┐  │
│  │  npub1abc...  laantungir    fd00::1    2s ago    ●    │  │
│  │  npub1def...  fips.v0l.io   fd00::2    5s ago    ●    │  │
│  │  npub1ghi...  —             fd00::3    1m ago    ○    │  │
│  │  ...                                                   │  │
│  │  (● = direct peer, ○ = known via discovery only)      │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌─ Spanning Tree ───────────────────────────────────────┐  │
│  │  Root: npub1abc... (laantungir)                        │  │
│  │  Our parent: npub1def... (fips.v0l.io)                 │  │
│  │  Our children:                                         │  │
│  │    npub1jkl...  depth 3  distance 1                    │  │
│  │    npub1mno...  depth 3  distance 1                    │  │
│  │  Tree peers (1-hop):                                   │  │
│  │    npub1abc...  depth 0  root    distance 2            │  │
│  │    npub1def...  depth 1  parent  distance 1            │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

New JSON endpoints

URI FIPS command Returns
sovereign://fips/network show_status + show_identity_cache + show_tree Combined JSON for the network view
sovereign://fips/tree show_tree Spanning tree JSON (for a dedicated tree tab)

sovereign://fips/network

Calls three FIPS commands on a fresh control connection and combines the results:

{
  "summary": {
    "estimated_mesh_size": 47,
    "known_nodes": 23,
    "max_cache_entries": 256,
    "tree_depth": 4,
    "is_root": false,
    "root_npub": "npub1abc...",
    "root_display_name": "laantungir",
    "our_node_addr": "e3da8293e6ea58807bd02b4749824243",
    "our_parent": "npub1def...",
    "our_parent_display_name": "fips.v0l.io"
  },
  "nodes": [
    {
      "node_addr": "e3da8293...",
      "npub": "npub1abc...",
      "display_name": "laantungir",
      "ipv6_addr": "fde3:da82:...",
      "last_seen_ms": 1784309108403,
      "age_ms": 2000,
      "is_direct_peer": true
    }
  ],
  "tree": {
    "root": "1b4788b7...",
    "root_npub": "npub1abc...",
    "is_root": false,
    "depth": 2,
    "parent": "9d1192e8...",
    "parent_display_name": "fips.v0l.io",
    "peers": [
      {
        "node_addr": "1b4788b7...",
        "display_name": "laantungir",
        "depth": 0,
        "distance_to_us": 2
      }
    ]
  }
}

The is_direct_peer flag on each node is computed by cross-referencing the identity cache entries against the show_peers npub list (so the UI can mark direct peers vs. discovery-only nodes).

Implementation

1. Add fips_control_show_tree() and fips_control_show_identity_cache() to src/fips_control.c / .h

These follow the existing fips_control_show_peers() pattern — send the command, return the data field as a JSON string (caller frees):

char *fips_control_show_tree(int fd, char *error, size_t error_size);
char *fips_control_show_identity_cache(int fd, char *error, size_t error_size);

2. Add handle_fips_network_json() to src/nostr_bridge.c

Opens a control connection, calls show_status, show_peers, show_identity_cache, and show_tree, then combines them into the JSON above. The is_direct_peer flag is computed by building a set of direct-peer npubs from show_peers and checking each identity-cache entry against it.

3. Add routing for sovereign://fips/network and sovereign://fips/tree

if (strcmp(uri, "sovereign://fips/network") == 0 ||
    strncmp(uri, "sovereign://fips/network?", 26) == 0) {
    handle_fips_network_json(request);
    return;
}
if (strcmp(uri, "sovereign://fips/tree") == 0 ||
    strncmp(uri, "sovereign://fips/tree?", 22) == 0) {
    handle_fips_tree_json(request);
    return;
}

4. Add a "Network" tab to www/fips.html / www/fips.js

Add tab navigation (Status | Peers | Network | Tree) to the page. The Network tab fetches sovereign://fips/network and renders:

  • Mesh summary card (estimated size, known nodes, depth, root, our position).
  • Known nodes table (npub, name, ipv6, last-seen, direct-peer badge).
  • Spanning tree card (root, parent, children, 1-hop peers with distances).

The Tree tab fetches sovereign://fips/tree and renders a more detailed tree view (coords paths, declaration sequence).

5. Auto-refresh

The Network tab polls sovereign://fips/network every 5s (same as the status poll).

Limitations (what we cannot show)

  • Full adjacency graph — FIPS doesn't expose "peer X's peer list." Bloom filters propagate node existence, not edges. To build a full graph, every node would need to expose its peer list (a future FIPS protocol extension), or the browser would need to query each node's control socket individually (not practical across the mesh).
  • Real-time node join/leave — the identity cache has last_seen_ms and age_ms, so we can show staleness, but there's no event stream for join/leave (would need a FIPS control subscription protocol).

Files

src/
├── fips_control.c     # Add show_tree(), show_identity_cache()
├── fips_control.h     # Add declarations
├── nostr_bridge.c     # Add handle_fips_network_json(), handle_fips_tree_json(), routing
www/
├── fips.html          # Add Network + Tree tabs
├── fips.css           # Tab + network-table styles
├── fips.js            # Network + tree rendering, tab switching

Implementation phases

Phase 1: Network summary + known nodes

  1. Add fips_control_show_tree() and fips_control_show_identity_cache()
  2. Add handle_fips_network_json() combining status + peers + identity cache
  3. Add sovereign://fips/network route
  4. Add "Network" tab to the page with mesh summary + known-nodes table
  5. Mark direct peers with a badge

Phase 2: Spanning tree view

  1. Add handle_fips_tree_json() (pass-through of show_tree)
  2. Add sovereign://fips/tree route
  3. Add "Tree" tab with root, parent, children, 1-hop peers, coords paths
  4. Optional: simple ASCII/indented tree rendering

Phase 3: Polish

  1. Sort known nodes by last-seen (most recent first)
  2. Color-code staleness (green < 10s, yellow < 1m, gray > 5m)
  3. Click a node to see its detail (ipv6, age, whether direct peer)
  4. Copy-to-clipboard for npub / ipv6
  5. Optional: bloom-filter visualization (advanced/debug tab)