How jcreek/opennetworkdiagram works
1. Overall shape
Open Network Diagram is a SvelteKit-based single-page app with a small Node runtime server for persistence. The app is designed around a JSON file as the source of truth, so the architecture is split between:
• a browser UI for viewing/editing diagrams,
• a server/API layer for loading and saving network data,
• static vendored assets for icons and demo data.
2. Primary runtime modes
The repo supports two meaningful deployment modes:
• Self-hosted writable mode: the UI edits network.json through the API and persists changes to disk.
• Read-only/public mode: used for demos or Netlify-style hosting, where the UI reads static data and writes are disabled.
This mode split is important because it changes whether the server’s persistence layer is active.
3. UI layer
The user-facing application lives under src/routes and src/lib/components. The main visible pieces are:
• NetworkDiagram.svelte for rendering the topology/diagram,
• Modal.svelte for editing machines/devices,
• route entry points in +page.svelte and +layout.svelte.
The UI is built to update the diagram live as the underlying JSON changes, rather than treating the diagram as a separate model.
4. Data model and schema boundary
The canonical data shape is defined in src/lib/data/networkSchema.ts and shared validation/core logic in src/lib/shared/networkSchemaCore.mjs. This is the boundary between arbitrary JSON input and the app’s internal expectations.
The schema covers the repository’s core entities: machines, devices, VMs, software, hardware, ports, IP addresses, and roles. This is the main contract that both UI editing and persistence must respect.
5. Load/edit/save flow
The data flow is:
• load JSON from the configured data source,
• validate/normalize it against the schema,
• let the UI edit it,
• persist the updated JSON back through the API/server.
src/lib/data/loadNetworkData.ts and src/lib/data/networkEditor.ts sit on the client-side data handling path, while src/lib/server/networkPersistence.ts and src/lib/shared/networkPersistenceCore.mjs handle the write path and atomic file operations.
6. API boundary
src/routes/api/network-data/+server.ts exposes the persistence API, documented in the README as GET/PUT /api/network-data.
This endpoint is the key boundary between browser state and durable storage:
• GET serves the current diagram JSON,
• PUT accepts edits from the UI,
• responses can include writability diagnostics when the app is running read-only.
The API is what makes the app usable both locally and in Docker with a mounted data volume.
7. Diagram transformation pipeline
The raw network JSON is not rendered directly. Instead, src/lib/graph/transformNetworkData.ts and src/lib/graph/types.ts convert the persisted schema into a graph-friendly structure for display.
This transformation layer is where the app derives diagram-specific concerns such as:
• node/edge relationships,
• network vs non-network presentation,
• VM expansion behavior,
• label/connection layout details.
It is a clear separation between stored domain data and visual representation.
8. Icon system and local asset catalog
The project includes a local icon registry in src/lib/config/iconRegistry.ts and a generated manifest in src/lib/config/vendorIconManifest.ts.
Icons are vendored into static/icons/vendor and sourced from third_party/homarr-dashboard-icons, with a helper script under scripts/generate-vendor-icon-manifest.mjs.
This means the UI does not depend on an external icon service at runtime, which matters for offline/self-hosted deployments.
9. Theme and browser state
The app has a small store layer in src/lib/stores/theme.ts for theme handling. This is client-side state only and is separate from the persisted network JSON.
That split helps keep local UX concerns distinct from the authoritative infrastructure model.
10. Server/runtime packaging
server.mjs, Dockerfile, docker-compose.yml, and nginx/default.conf indicate a deployment model where the built app can be served as a containerized Node/static app behind a web server.
The server is responsible for serving the UI and backing the API in writable deployments, while the Docker/compose setup mounts a host data directory so the JSON file survives restarts.
11. Data locations and environment-specific sources
There are three important data locations:
• data/network.json.example for seeding a new writable install,
• data/network.json for default local/server persistence,
• static/data/network.json for the bundled read-only/demo dataset.
This separation supports both local editing and static hosting without changing application code.
12. Operational boundaries and CI/CD
The repo’s GitHub workflows and build targets reflect two different artifacts:
• a Docker/runtime artifact for self-hosted writable use,
• a static/Netlify artifact for read-only demo hosting.
The build variants are important because they encode the app’s operational boundary: writable persistence is only available when the runtime can access a filesystem-backed data path.
13. Architecture summary for diagramming
If you draw this system, model it as:
• a browser UI at the center,
• a JSON schema/domain layer beneath it,
• a transformation layer from domain data to graph visuals,
• a server/API persistence layer on the side for GET/PUT to a JSON file,
• vendored static icons and demo data as supporting assets.
The most important conceptual boundary is that the diagram is a projection of persisted JSON, not a separate hand-authored drawing model.
Main entry points: +page.svelte App page · +server.ts Network API · NetworkDiagram.svelte Network diagram · loadNetworkData.ts Load data
Components 23
Browser app
Client-side SvelteKit UI and state
- App page · Top-level SvelteKit entry for the main diagram view
src/routes/+page.svelte - Layout · Shared page shell and app framing
src/routes/+layout.svelte - Network diagram · Renders the topology and live diagram interactions
src/lib/components/NetworkDiagram.svelte - Edit modal · Machine and device editing UI
src/lib/components/Modal.svelte - Load data · Fetches and prepares network data for the UI
src/lib/data/loadNetworkData.ts - Editor state · Client-side mutation and normalization helpers
src/lib/data/networkEditor.ts - Theme store · Client-only theme and appearance state
src/lib/stores/theme.ts
Domain model
Shared schema and edit logic
- Network schema · Canonical network JSON contract
src/lib/data/networkSchema.ts - Schema core · Shared schema and validation logic
src/lib/shared/networkSchemaCore.mjs - Graph transform · Turns persisted network JSON into renderable graph data
src/lib/graph/transformNetworkData.ts - Graph types · Types for diagram nodes and edges
src/lib/graph/types.ts
Persistence
Server-side JSON storage and API
- Network API · GET and PUT API for the current network JSON
src/routes/api/network-data/+server.ts - File storage · Atomic read/write persistence for the network file
src/lib/server/networkPersistence.ts - Persist core · Shared JSON file operations and write semantics
src/lib/shared/networkPersistenceCore.mjs
Assets
Vendored icons and demo data
- Icon registry · Maps diagram entities to vendored icons
src/lib/config/iconRegistry.ts - Icon manifest · Generated list of available vendor icons
src/lib/config/vendorIconManifest.ts - Vendor icons · Bundled icon files used at runtime without external services
static/icons/vendor - Demo data · Read-only bundled network dataset for demo or public hosting
static/data/network.json - Seed data · Example JSON for a fresh writable install
data/network.json.example
Deployment
Container and hosting packaging
- Server runtime · Container or local Node server that serves the app and API
server.mjs - Container build · Docker image and compose setup for writable self-hosted use
Dockerfile - Web server · Nginx config used in packaged deployments
nginx/default.conf - Static deploy · Read-only public hosting configuration
netlify.toml
Connections 23
- App page → Layout: uses shell · The main route is rendered inside the shared layout
- App page → Load data: loads data · The page initializes the current network state from the data source
- App page → Network diagram: renders · The page passes transformed state into the diagram view
- App page → Edit modal: edits · Editing flows open the modal from the page UI
- Load data → Network API: GET data · Client loading fetches current JSON from the API when writable mode is active
- Load data → Demo data: fallback data · Public or static mode can load the bundled dataset
- Editor state → Network schema: validates · Client edits are checked against the canonical schema
- Network schema → Schema core: shares rules · The UI schema builds on shared validation logic
- Network diagram → Graph transform: projects · Rendered graph state is derived from persisted network data
- Graph transform → Graph types: uses types · The projection emits the diagram-specific graph structure
- Network diagram → Icon registry: icons · Diagram nodes are decorated using the local icon catalog
- Icon registry → Icon manifest: reads manifest · The registry is backed by a generated icon list
- Icon manifest → Vendor icons: references · The manifest points to vendored icon assets
- Edit modal → Editor state: mutates · The edit modal updates the client-side network model
- Network API → File storage: writes · PUT requests are persisted to the server-side JSON store
- Network API → Persist core: delegates I/O · The endpoint relies on shared persistence operations
- File storage → Persist core: uses core · Server persistence is implemented on top of shared file logic
- File storage → Seed data: seeds · Fresh writable installs can start from the example JSON
- Server runtime → Network API: serves endpoint · The Node runtime hosts the network-data API
- Server runtime → Container build: packaged by · The runtime is the target of the containerized deployment
- Container build → Web server: fronted by · Packaged deployments include an Nginx layer
- Static deploy → Demo data: publishes · Static hosting uses the bundled read-only dataset
- Theme store → App page: ui state · Theme is client-only presentation state for the app