Files
resolutionflow/docs/superpowers/specs/2026-04-04-network-diagrams-design.md
chihlasm be34a20441 docs: add network diagrams design spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 07:25:51 +00:00

21 KiB

Network Diagrams — Design Spec

Date: 2026-04-04 Status: Approved Scope: Standalone network diagram builder (Phase 1 — no FlowPilot integration)


Overview

A fully functional Network Diagram builder for MSP engineers. Engineers can manually build network topology diagrams with drag-and-drop device nodes and connections, or use AI to generate diagrams from plain English descriptions. Diagrams are team-scoped and filterable by client.

User-facing label: "Network Maps" in the sidebar.


Key Design Decisions

Decision Choice Rationale
Device types Database-driven with system defaults MSPs serve different verticals with different gear. Extensibility from day one avoids a refactor.
Device type rendering DB stores identity + metadata; frontend maps icons/colors by slug Clean separation. Custom types get category-based default icons. Icon picker deferred to future.
Connection types Fixed defaults + free-text "Custom..." option Connection types are more standardized than devices. 6 defaults cover 95% of cases.
AI generation Replace + Merge (Add to Diagram) modes Iterative building is the natural workflow: "generate base" then "add VPN to branch office."
Export formats PNG + PDF + JSON PNG for sharing, PDF for formal client docs, JSON for backup/import/portability.
Client filter Combobox/autocomplete Type to filter, click to select from distinct client_name values.
State management Local React state (not Zustand) Matches AssistantChatPage pattern. Editor page owns nodes/edges/meta.
Scope Standalone tool only Future integration with FlowPilot sessions documented but not built.

Data Model

device_types table

Stores system-default and team-custom device types.

CREATE TABLE device_types (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    slug VARCHAR(50) NOT NULL,
    label VARCHAR(100) NOT NULL,
    category VARCHAR(50) NOT NULL,  -- network, compute, storage, cloud, endpoint, infrastructure, security
    is_system BOOLEAN NOT NULL DEFAULT FALSE,
    team_id UUID REFERENCES teams(id) ON DELETE CASCADE,
    sort_order INTEGER NOT NULL DEFAULT 0,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    CONSTRAINT uq_device_types_slug_team UNIQUE NULLS NOT DISTINCT (slug, team_id)
);

CREATE INDEX idx_device_types_team ON device_types(team_id);
  • is_system = true, team_id = NULL for built-in types
  • is_system = false, team_id = <uuid> for team-custom types
  • slug is the key used in diagram node data and the frontend rendering registry

System device type seed data

Slug Label Category
router Router network
switch Switch network
firewall Firewall network
access-point Access Point network
load-balancer Load Balancer network
server Server compute
workstation Workstation compute
vm Virtual Machine compute
container Container compute
nas NAS storage
san SAN storage
cloud-storage Cloud Storage storage
cloud Cloud cloud
aws AWS cloud
azure Azure cloud
gcp Google Cloud cloud
printer Printer endpoint
phone Phone endpoint
iot IoT Device endpoint
camera Camera endpoint
tablet Tablet endpoint
laptop Laptop endpoint
ups UPS infrastructure
pdu PDU infrastructure
rack Rack infrastructure
patch-panel Patch Panel infrastructure
nvr NVR security
badge-reader Badge Reader security

network_diagrams table

CREATE TABLE network_diagrams (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL,
    client_name VARCHAR(255),
    asset_name VARCHAR(255),
    description TEXT,
    nodes JSONB NOT NULL DEFAULT '[]',
    edges JSONB NOT NULL DEFAULT '[]',
    thumbnail_url TEXT,
    is_archived BOOLEAN DEFAULT FALSE,
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_network_diagrams_team ON network_diagrams(team_id);
CREATE INDEX idx_network_diagrams_client ON network_diagrams(team_id, client_name);

JSONB Node Structure

{
  "id": "unique-string",
  "type": "router",
  "label": "Core Router",
  "position": { "x": 400, "y": 200 },
  "properties": {
    "hostname": "core-rtr-01",
    "ip": "10.0.0.1",
    "subnet": "10.0.0.0/24",
    "vendor": "Cisco",
    "model": "ISR 4331",
    "role": "Core gateway",
    "vlan": "1",
    "notes": "",
    "status": "online"
  }
}
  • type field stores the device type slug (string, not enum)
  • status is one of: unknown, online, offline, degraded

JSONB Edge Structure

{
  "id": "unique-string",
  "source": "node-id",
  "target": "node-id",
  "label": "Uplink to Core",
  "connectionType": "ethernet",
  "speed": "1 Gbps",
  "notes": "Port Gi0/1 -> Gi0/24"
}
  • connectionType is a free string. Standard defaults: ethernet, fiber, wifi, vpn, vlan, wan
  • speed and notes are additional edge metadata fields (not in original spec, added for MSP usefulness)

JSON Export Schema

{
  "schemaVersion": 1,
  "name": "Office Network",
  "client_name": "Acme Corp",
  "description": "Main office topology",
  "nodes": [...],
  "edges": [...],
  "exportedAt": "2026-04-04T12:00:00Z"
}

API Endpoints

Device Types

All endpoints require authentication via get_current_active_user.

Method Path Description
GET /api/v1/device-types/ List all (system + team custom), ordered by category then sort_order
POST /api/v1/device-types/ Create custom type for team
PUT /api/v1/device-types/{id} Update custom type (team-owned only)
DELETE /api/v1/device-types/{id} Delete custom type (team-owned only, fails if is_system)

Network Diagrams

All endpoints team-scoped via get_current_active_user. Team ownership verified on every single-resource operation.

Method Path Description
POST /api/v1/network-diagrams/ Create diagram
GET /api/v1/network-diagrams/ List for team (exclude archived). Query params: client_name, search
GET /api/v1/network-diagrams/{id} Get single diagram
PUT /api/v1/network-diagrams/{id} Update diagram
DELETE /api/v1/network-diagrams/{id} Soft delete (set is_archived=True)
POST /api/v1/network-diagrams/{id}/duplicate Duplicate diagram (new name: "Copy of {name}")
POST /api/v1/network-diagrams/import Import from JSON file
GET /api/v1/network-diagrams/{id}/export Export as JSON
POST /api/v1/network-diagrams/ai-generate AI diagram generation
GET /api/v1/network-diagrams/clients List distinct client_name values for team (powers combobox)

AI Generate Endpoint

Request:

{
  "description": "Small office with a firewall, core switch, 3 access points, and a file server",
  "client_name": "Acme Corp",
  "mode": "replace",
  "existingBounds": null
}

For merge mode:

{
  "description": "Add a VPN tunnel to a branch office with 2 workstations",
  "client_name": "Acme Corp",
  "mode": "merge",
  "existingBounds": { "minX": 0, "maxX": 800, "minY": 0, "maxY": 600 }
}

System prompt includes:

  • The generation rules from the original spec (JSON-only response, topology layout guidelines)
  • The list of available device type slugs (system + team custom)
  • For merge mode: existing node positions and instruction to place new nodes in available space

Response:

{
  "nodes": [...],
  "edges": [...],
  "suggestedName": "Acme Corp - Main Office",
  "notes": "Assumed site-to-site VPN for branch office connection"
}

Error handling:

  • JSON parse failure: 422 "AI generated an invalid response, please try again"
  • Unknown device type slug: silently fall back to closest category match
  • Timeout: 504 "Generation took too long"

Frontend Architecture

New Files

frontend/src/
├── api/
│   ├── deviceTypes.ts              # Device types API client
│   └── networkDiagrams.ts          # Network diagrams API client
├── components/network/
│   ├── nodes/
│   │   ├── deviceRegistry.ts       # Slug → { icon, color } mapping + category defaults
│   │   ├── DeviceNode.tsx          # React Flow custom node
│   │   └── nodeTypes.ts            # React Flow node type registry
│   ├── edges/
│   │   └── ConnectionEdge.tsx      # Custom edge with connection-type styling
│   ├── panels/
│   │   ├── DeviceToolbar.tsx       # Left panel — categorized, searchable, draggable
│   │   ├── PropertiesPanel.tsx     # Right panel — node/edge property editor
│   │   └── AIAssistPanel.tsx       # Bottom collapsible — AI generation
│   ├── NetworkCanvas.tsx           # React Flow wrapper
│   └── DiagramHeader.tsx           # Top bar — name, save, export
├── pages/NetworkDiagrams/
│   ├── index.tsx                   # List/dashboard page
│   └── DiagramEditor.tsx           # Full editor page (assembles all panels)
└── types/
    └── networkDiagram.ts           # TypeScript interfaces

Modified Files

  • frontend/src/components/layout/Sidebar.tsx — add "Network Maps" nav item
  • frontend/src/router.tsx — add routes
  • frontend/src/api/index.ts — export new API modules
  • frontend/src/types/index.ts — export new types
  • backend/app/api/router.py — register new routers
  • backend/app/models/__init__.py — export new models (if pattern requires)

Page Layouts

List Page (/network-diagrams)

┌──────────────────────────────────────────────────────────────┐
│  Network Maps                          [Import] [New Diagram]│
│  Visual network topology documentation for your clients      │
├──────────────────────────────────────────────────────────────┤
│  [🔍 Search diagrams...]    [Client ▾ combobox filter]       │
├──────────────────────────────────────────────────────────────┤
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                   │
│  │ Diagram  │  │ Diagram  │  │ Diagram  │                   │
│  │ Card     │  │ Card     │  │ Card     │                   │
│  │          │  │          │  │          │                   │
│  │ name     │  │ name     │  │ name     │                   │
│  │ client   │  │ client   │  │ client   │                   │
│  │ 12 devs  │  │ 8 devs   │  │ 5 devs   │                   │
│  │ ⋯ menu   │  │ ⋯ menu   │  │ ⋯ menu   │                   │
│  └──────────┘  └──────────┘  └──────────┘                   │
└──────────────────────────────────────────────────────────────┘

Card three-dot menu: Open, Duplicate, Export JSON, Export PNG, Export PDF, Archive

Editor Page (/network-diagrams/new, /network-diagrams/:id)

┌─────────────────────────────────────────────────────────────┐
│  ← Network Maps  │ Diagram Name  [Acme Corp] │ Save │ Export│
├──────────┬──────────────────────────────┬───────────────────┤
│  [🔍]    │                              │  Properties Panel │
│  Network │                              │  (when selected)  │
│  ├ Router│     React Flow Canvas        │                   │
│  ├ Switch│     (dot background)         │  Hostname [____]  │
│  └ ...   │     (minimap bottom-right)   │  IP       [____]  │
│  Compute │     (controls top-left)      │  Subnet   [____]  │
│  ├ Server│                              │  Vendor   [____]  │
│  └ ...   │                              │  ...              │
│  ...     │                              │                   │
│  [+Custom│                              │  [Delete Device]  │
│   Type]  │                              │                   │
├──────────┴──────────────────────────────┴───────────────────┤
│  [✨ AI Generate]  (collapsed) ──or──  expanded textarea    │
└─────────────────────────────────────────────────────────────┘

Component Details

DeviceToolbar (left, 200px)

  • Search box at top — filters across all categories
  • Collapsible category sections (Network, Compute, Storage, Cloud, Endpoint, Infrastructure, Security)
  • Each device: draggable card with icon + label
  • HTML5 drag-and-drop — onDragStart sets device type slug in dataTransfer
  • "+ Custom Type" button at bottom — opens inline form (slug, label, category dropdown)
  • Fetches device types from API on mount
  • bg-sidebar background, border-default right border

DeviceNode (React Flow custom node)

  • Renders device icon from registry (resolved by slug)
  • Label below icon
  • IP address below label (JetBrains Mono, muted color) if set
  • Status dot: green=online, red=offline, yellow=degraded, gray=unknown
  • Connection handles (top, bottom, left, right) — visible on hover
  • Selected: border changes to accent blue
  • bg-card, border-default, 8px radius, min-width 120px

ConnectionEdge (React Flow custom edge)

  • Renders as smoothstep edge
  • Visual style varies by connectionType:
    • ethernet: solid blue line
    • fiber: solid green, slightly thicker
    • wifi: dotted purple
    • vpn: dashed yellow
    • vlan: solid gray
    • wan: dashed red
    • custom: solid default with label
  • Shows connection label on the edge

PropertiesPanel (right, 260px)

  • Node selected: hostname, IP, subnet, vendor, model, role, VLAN, notes (text inputs), status (styled dropdown)
  • Edge selected: label, connectionType (dropdown with "Custom..." option), speed, notes
  • Nothing selected: "Select a device to edit its properties"
  • Changes update node/edge data immediately (controlled inputs)
  • "Delete" button at bottom (red accent)
  • bg-sidebar, border-default left border

AIAssistPanel (bottom, collapsible)

  • Collapsed: "AI Generate" button in a slim bar
  • Expanded: textarea, mode toggle ("Generate New" / "Add to Diagram"), "Generate" button
  • Replace mode warning: "This will replace your current diagram. Save first if needed."
  • Loading: animated pulse + "Generating your network diagram..."
  • Success: replaces/merges canvas, shows toast with suggested name
  • Error: inline error message

DiagramHeader (top, 56px)

  • Back button: ← Network Maps navigates to list page
  • Inline-editable diagram name (click to edit, Enter to confirm)
  • Client name badge (pill) if set
  • Save button (primary, "Saving..." state)
  • Export dropdown: PNG, PDF, JSON
  • Last saved timestamp (muted, right side)
  • bg-card, border-default bottom border

State Management

DiagramEditor page owns all state via useState:

  • nodes / edges — managed by React Flow's useNodesState / useEdgesState
  • diagramMeta — name, client_name, asset_name, description
  • isDirty — tracks unsaved changes
  • lastSavedAt — timestamp for header display
  • selectedNode / selectedEdge — drives PropertiesPanel

Auto-save: setInterval at 30 seconds, only fires if isDirty is true. Clears dirty flag on save. Does not fire for new unsaved diagrams (must explicitly save first to create the record).

Export Implementation

  • PNG: Client-side using the Canvas API. Render the React Flow viewport to an offscreen canvas via ReactFlow.toObject() + manual SVG-to-canvas rendering, then canvas.toBlob() for download. No external library needed.
  • PDF: Client-side using window.print() with a print-specific stylesheet. Opens a print-optimized view of the diagram with metadata header (name, client, description, date, device count) and device legend. The user selects "Save as PDF" in the browser print dialog. No external library needed.
  • JSON: API call to GET /network-diagrams/{id}/export, triggers file download

Import Implementation

  • "Import" button on list page opens file picker (.json only)
  • Uploads to POST /network-diagrams/import
  • Backend validates schema version and structure
  • Creates new diagram for the team
  • Response includes warnings for unknown device type slugs
  • On success, navigates to the new diagram's editor

Navigation Integration

Sidebar

Add "Network Maps" to both sidebar modes:

Rail groups (icon-only mode): Add as a child under the Flows group:

{
  href: '/trees', icon: GitBranch, label: 'Flows', shortLabel: 'Flows',
  children: [
    { href: '/trees', label: 'Flow Library' },
    { href: '/trees?type=procedural', label: 'Projects' },
    { href: '/network-diagrams', label: 'Network Maps' },  // NEW
    { href: '/step-library', label: 'Solutions Library' },
    { href: '/review-queue', label: 'Review Queue' },
  ],
}

Pinned sections: Add under KNOWLEDGE:

{
  title: 'KNOWLEDGE',
  items: [
    { href: '/trees', icon: GitBranch, label: 'Flow Library', ... },
    { href: '/network-diagrams', icon: Network, label: 'Network Maps', shortLabel: 'NetMap' },  // NEW
    { href: '/scripts', icon: Code2, label: 'Scripts', ... },
    ...
  ],
}

Routes

const NetworkDiagramsPage = lazyWithRetry(() => import('@/pages/NetworkDiagrams'))
const DiagramEditorPage = lazyWithRetry(() => import('@/pages/NetworkDiagrams/DiagramEditor'))

// Inside ProtectedRoute/AppLayout children:
{ path: 'network-diagrams', element: page(NetworkDiagramsPage) },
{ path: 'network-diagrams/new', element: page(DiagramEditorPage) },
{ path: 'network-diagrams/:id', element: page(DiagramEditorPage) },

Future Integration Roadmap

These features are documented for future implementation. They are NOT in scope for Phase 1.

Phase 2: FlowPilot Context Integration

When a FlowPilot session has a client_name matching a saved network diagram, the diagram can be surfaced as read-only reference context. The AI copilot gains topology awareness — it can reference specific devices, connections, and network segments when guiding troubleshooting.

Phase 3: Live Status Overlay

Device status indicators updated from PSA/monitoring integrations (ConnectWise, Datto RMM, etc.). Green/red/yellow dots reflect real-time device state. Engineers see at-a-glance which devices are down before starting troubleshooting.

Phase 4: Session-Linked Diagrams

Attach a diagram to a FlowPilot session. During troubleshooting, highlight the device being investigated. Mark devices as "checked" or "problem found." Session resolution includes a diagram snapshot showing the problem path.

Phase 5: Custom Device Type Icons

Icon picker UI for team-custom device types. Optional SVG upload for vendor logos or specialized equipment icons (with SVG sanitization for security).

Phase 6: Diagram Templates

Pre-built topology templates as starting points: Small Office, Branch + HQ, Data Center, Remote Worker, MSP NOC. Templates include placeholder devices with typical connections.

Phase 7: Collaborative Editing

Multiple engineers editing the same diagram simultaneously. Requires WebSocket infrastructure (Yjs or similar CRDT). Cursor presence, conflict resolution, real-time sync.


Quality Requirements

  • Every file fully typed — no any, no untyped props
  • No inline styles — Tailwind classes only, using design system CSS variable tokens
  • No placeholder TODOs — build the real implementation
  • All API calls handle loading, error, and success states
  • Team-scoping enforced on every backend endpoint
  • Ownership verification on every GET/PUT/DELETE by ID
  • Editor handles both "new diagram" and "edit existing" modes
  • Auto-save only fires when dirty
  • No new npm packages — use only what's installed
  • Follow existing code style exactly