# Network Sketcher User Guide

Network Sketcher is an open-source toolkit from Cisco for designing,
documenting, and rendering enterprise networks layer-by-layer (L1 / L2 / L3)
straight out of an Excel **master file**. You drive every change with a
five-verb command-line — `add`, `delete`, `rename`, `show`, `export` — and
get back PowerPoint, SVG, Visio (`.vsdx`-friendly) and draw.io artifacts
that match what your network actually looks like.

The project ships in two editions that share an engine:

- **Network Sketcher Online** — a self-hosted **Flask web app** (HTTPS on
  port `5443` by default) that you upload a master file to and click
  through.
- **Network Sketcher Offline** — a **Tkinter desktop GUI** plus the same
  CLI, bundled for direct use on a workstation.

This guide covers everyday usage for both editions. If you maintain the
service for a team, deploy it on a shared host, or want to understand the
engine internals, see the **Administrator & Developer Guide**.

> [!NOTE]
> Network Sketcher does **not** ingest live device telemetry, scrape running
> configs, or talk to controllers. It is a **deterministic renderer** of the
> data you put in the master file. Drawings are reproducible byte-for-byte
> when the master file is unchanged.

---

## 1. Quick Start

The fastest path to your first diagram is the **Online** edition: it
auto-generates a self-signed TLS certificate, opens an HTTPS endpoint at
`https://localhost:5443`, and lets you drag-and-drop a master file in your
browser. No CLI knowledge is required to render.

```bash
# From the cloned repository root
python start_ns_online.py
```

```text
========================================================
  Network Sketcher Online — Start
========================================================
  Server started (PID: 28144)
  PID file: network-sketcher_online/ns_online.pid
========================================================
```

Open `https://localhost:5443/` in your browser. On the first page:

1. Drop your `[MASTER]<name>.xlsx` master file in the upload zone (or click
   **Create New Master File** for an empty template).
2. Wait for the **Available Outputs** list to populate. Each item has its
   own **Generate** button, or click **Generate Selected** to build
   everything queued.
3. **Preview** renders inline (PowerPoint and Excel preview without Office
   installed). **Download** ships the file.

> [!TIP]
> Don't have Excel installed and just want to see what the tool produces?
> Click **Create New Master File**, then **Generate Selected** with the
> default outputs checked — you'll get an L1, L2, and L3 sample diagram
> from the built-in `[MASTER]no_data.xlsx` template in under a minute.

If you'd rather drive everything from a terminal, the same engine is
available as a CLI:

```bash
cd network-sketcher_offline
python ns_cli.py export l1_diagram \
  --master "[MASTER]my_design.xlsx" \
  --type all_areas_tag
```

```text
--- L1 Diagram exported successfully ---
[L1_DIAGRAM]AllAreasTag_my_design.pptx
```

### What you'll need

- [x] Python 3.9 or newer (3.11+ recommended)
- [x] `openpyxl`, `pandas`, `pyarrow`, `python-pptx`, `networkx`, `numpy`
- [x] **Online edition only**: `flask`, `cryptography` (for self-signed
      certs)
- [x] **Offline edition only**: a working Tk install (`python-tk` /
      `tkinterdnd2`) for the desktop GUI; the CLI also runs without Tk
- [x] About 200 MB of disk space for source plus dependencies

> [!IMPORTANT]
> Master files **must** start with the literal prefix `[MASTER]` and end in
> `.xlsx` or `.nsm`. The web upload route rejects everything else with
> `Filename must start with [MASTER]`.

### What you'll see in your first 60 seconds

| You did | Network Sketcher produced |
|---|---|
| Uploaded `[MASTER]demo.xlsx` | One **L1 All-Areas** PowerPoint, one per-area L1 diagram per area in the master, an **L2** diagram per L2 broadcast domain, one **L3 All-Areas** diagram, and a `[DEVICE]demo.xlsx` summary workbook. |
| Clicked **Generate** on a single output | A `.pptx` (default) or `.svg` (if you toggle the format), saved to the per-job upload directory and offered for download. |
| Pasted CLI commands like `add device R5 area=DC1` | A new **versioned master** (`[MASTER]demo_2.xlsx`) reflecting the change — your original is preserved for rollback. |

### Where to go next

- **First-time tour:** keep reading **Section 4 (Core Usage)**.
- **Already drawing networks?** skip to **Section 5 (Common Tasks)** for
  recipes such as multi-area pods and AI-context generation.
- **Hosting it for your team?** start with the Administrator & Developer
  Guide.

---

## 2. Installation

Network Sketcher is distributed as **source** from GitHub. There are no
PyPI wheels, no Docker images shipped by Cisco, and no installer — you
clone, you install dependencies, you run. The two editions live
side-by-side in the same repository.

### 2.1 Choose an edition

| Capability | Offline (`network_sketcher.py`) | Online (`ns_web_start.py`) |
|---|---|---|
| Tkinter desktop GUI | Yes | No (headless / browser only) |
| Browser UI | No | Yes (HTTPS, port `5443` by default) |
| CLI (`add` / `delete` / `rename` / `show` / `export`) | Yes (`ns_cli.py`) | Yes (in-process via `ns_engine`) |
| External-system import (`*.yaml`, `*.svg`, `*.csv`) | Yes (drag-and-drop pane) | No (CLI/manual) |
| Multi-user | No (single-process desktop) | Yes (per-`job_id` upload dirs) |
| Parallel diagram generation | Single-threaded | Up to `parallel_limit` workers (default 6) |
| Self-signed TLS auto-generation | N/A | Yes (`Certs/server.crt|key`) |
| Heartbeat session cleanup | N/A | Yes (5-minute idle expiry by default) |

> [!TIP]
> If you only need to render diagrams locally, the Offline CLI is the
> simplest path — no Flask, no certs, no browser. The web UI is the right
> choice when more than one person needs to use the tool at once or when
> you want the polished drag-and-drop experience.

### 2.2 Get the source

```bash
git clone https://github.com/cisco-open/network-sketcher.git
cd network-sketcher
```

### 2.3 Install dependencies

The two editions ship separate requirements files
(`network-sketcher_offline/requirements_offline.txt` and
`network-sketcher_online/requirements_online.txt`). Install whichever
edition(s) you plan to run:

=== "Offline edition"
    ```bash
    cd network-sketcher_offline
    python -m pip install -r requirements_offline.txt
    ```

=== "Online edition"
    ```bash
    cd network-sketcher_online
    python -m pip install -r requirements_online.txt
    ```

=== "Both"
    ```bash
    python -m pip install -r network-sketcher_offline/requirements_offline.txt
    python -m pip install -r network-sketcher_online/requirements_online.txt
    ```

> [!IMPORTANT]
> The Offline GUI imports `tkinter` and `tkinterdnd2`. On Linux distributions
> that don't ship Tk by default, install your distro's `python3-tk` package
> first (e.g. `sudo apt install python3-tk`). The Online edition is
> **headless** — it stubs out `tkinter` and runs without any display server,
> so it works fine in containers and on locked-down servers.

### 2.4 Verify

=== "Offline CLI"
    ```bash
    cd network-sketcher_offline
    python ns_cli.py
    ```

    ```text
    [ERROR] Master file must be selected --master <filepath>.
    ```

    Seeing the `--master` error means the CLI dispatcher is wired up.

=== "Offline GUI"
    ```bash
    cd network-sketcher_offline
    python network_sketcher.py
    ```

    A window titled `Network Sketcher Offline  ver 3.0.1h` should open with
    a **Main Panel** tab.

=== "Online"
    ```bash
    python start_ns_online.py
    ```

    Then open `https://localhost:5443/`. You will see a TLS warning the
    first time because the cert is self-signed — see Troubleshooting.

### 2.5 Stopping the Online server

`stop_ns_online.py` finds every running `ns_web_start.py` process (even
ones launched outside the start script) and terminates them, removing the
PID file on success.

```bash
python stop_ns_online.py
```

```text
========================================================
  Network Sketcher Online — Stop
========================================================
  Found 1 running server process(es): [28144]
  Stopping PID 28144...
  Stopped 1 process(es).
========================================================
```

> [!WARNING]
> `stop_ns_online.py` sends `SIGTERM` (POSIX) or `taskkill /F /T` (Windows).
> Any in-flight diagram generation is aborted immediately. Wait for the
> **All server processes stopped** line before restarting —
> `start_ns_online.py` already does this for you when you use it to
> restart, but a manual `python ns_web_start.py` will not.

### 2.6 First-launch artifacts (Online)

On first start, the server creates these directories and files. They're
all under `network-sketcher_online/`:

| Path | Purpose | First-run behavior |
|---|---|---|
| `Certs/server.crt`, `Certs/server.key` | TLS certificate plus private key | Auto-generated 4096-bit RSA self-signed cert valid for 10 years (`ssl_cert_validity_days: 3650`) if either is missing |
| `logs/access_YYYYMMDD.log` | Per-day access log (one file per local date) | Created on first request; old files older than `access_log_retention_days` (default 90) are deleted automatically |
| `logs/server.log` | stdout / stderr from the Flask app | Created by `start_ns_online.py` |
| `uploads/<uuid>/` | Per-job working directory | Created on each `/upload` POST; cleared after the heartbeat expires |
| `ns_online.pid` | PID of the running server | Used by `stop_ns_online.py` |

---

## 3. Configuration

The Online edition reads `network-sketcher_online/ns_web_config.json` once
at startup. Every key is documented inline in that file. The Offline CLI
takes no config file — all behavior is controlled by command-line flags.

### 3.1 The `ns_web_config.json` keys you'll touch most

| Key | Default | What it controls |
|---|---|---|
| `host` | `localhost` | Bind address. Use `0.0.0.0` to expose to the LAN, or pin to a specific NIC IP |
| `port` | `5443` | HTTPS listen port |
| `fqdn` | `null` | If non-null, used as the certificate **Common Name** instead of `host` |
| `max_file_size_mb` | `50` | Upload size cap; rejected uploads return HTTP 413 |
| `subprocess_timeout` | `14400` (4 hours) | Wall-clock cap on each diagram subprocess; `null` for no limit |
| `heartbeat_timeout` | `300` (5 min) | Idle window after which a session's `uploads/<job_id>/` is wiped |
| `cleanup_interval` | `60` | Seconds between idle-sweep runs |
| `parallel_limit` | `6` | Max diagrams generated concurrently per `/svg_grid_stream` call |
| `log_level` | `INFO` | One of `DEBUG`, `INFO`, `WARNING`, `ERROR` |
| `access_log_retention_days` | `90` | How many `access_*.log` files to keep |
| `ai_context_button_label` | `Copy & Open LLM` | The button text on the AI Context panel |
| `ai_context_button_url` | `https://claude.ai` | Where the **Copy & Open LLM** button opens |

The full reference, including TLS fields (`ssl_cert_path`, `ssl_key_path`,
`ssl_min_version`, `ssl_max_version`, `ssl_ciphers`,
`ssl_cert_validity_days`, `ssl_key_size`) and the AI-context show-command
list (`ai_context_show_commands`), lives in the Administrator & Developer
Guide.

### 3.2 Environment variables

The Online server sets `NS_WEB_SERVER=1` for every child process it
spawns; engine modules check this to suppress GUI fall-backs. You don't
normally set it yourself unless you're embedding the engine.

| Variable | Read in | Effect |
|---|---|---|
| `NS_WEB_SERVER` | `nsm_l2_diagram_create.py` and other engine modules | When set to any value, engine code follows the headless / server code path (no Tk, no print spam) |
| `NS_DEBUG_L3` | `nsm_l3_diagram_create.py` | Set to `1` to dump verbose L3-layout diagnostics to stdout. Default `0`. Helpful when investigating L3 layout bugs (see Troubleshooting issue #59) |

> [!NOTE]
> Master file paths and CLI subcommand names are **not** environment-driven.
> Every input is a flag on `ns_cli.py` or a JSON config key. This makes
> Network Sketcher trivially reproducible across machines.

### 3.3 Editing config safely

```bash
# 1. Stop the server cleanly
python stop_ns_online.py

# 2. Edit the JSON
${EDITOR:-vi} network-sketcher_online/ns_web_config.json

# 3. Restart
python start_ns_online.py
```

> [!WARNING]
> Comments are **not** supported in `ns_web_config.json` (it's strict JSON,
> not JSONC). Each top-level field is `{"value": ..., "description": "..."}`;
> the loader strips the description and only the `value` is read. Any field
> whose name starts with an underscore (e.g. `_description`) is ignored.

---

## 4. Core Usage

Network Sketcher is built around one pivotal artifact — the **master
file** — and three production paths that read it.

### 4.1 The master file

Every workflow starts here. The master file is an Excel workbook (`.xlsx`)
with three well-known worksheets:

- **`Master_Data`** — single source of truth for areas, devices, L1 wires,
  positions, styles, and device attributes. Sections are tagged with
  `<<ROOT_FOLDER>>`, `<<POSITION_FOLDER>>`, `<<STYLE_FOLDER>>`,
  `<<POSITION_SHAPE>>`, `<<STYLE_SHAPE>>`, `<<POSITION_LINE>>`,
  `<<POSITION_TAG>>`, `<<ATTRIBUTE>>`, and `<<END_MARK>>`.
- **`Master_Data_L2`** — L2 broadcast-domain memberships
  (`<<L2_TABLE>>`).
- **`Master_Data_L3`** — L3 interface and IP-address assignments
  (`<<L3_TABLE>>`).

The Online server also accepts a compact binary form, **`.nsm`**, which is
just a ZIP of Parquet sections (`ROOT_FOLDER.parquet`,
`POSITION_FOLDER.parquet`, `STYLE_FOLDER.parquet`, `POSITION_SHAPE.parquet`,
`STYLE_SHAPE.parquet`, `POSITION_LINE.parquet`, `POSITION_TAG.parquet`,
`ATTRIBUTE.parquet`, `L2_TABLE.parquet`, `L3_TABLE.parquet`,
`END_MARK.parquet`). Uploads of `.xlsx` are converted to `.nsm` immediately
on receipt for faster downstream operations; the original is kept.

> [!TIP]
> Don't have a master file to start from? Either click **Create New Master
> File** in the web UI, or run
> `python ns_cli.py export master_file_nodata --master /tmp/dummy.xlsx`
> and grab the resulting `[MASTER]no_data.xlsx`.

### 4.2 The five CLI verbs

```text
ns_cli.py {add|delete|rename|show|export} <object> [--<flag> <value> ...] --master <path>
```

The five top-level verbs and their object surfaces:

| Verb | Objects |
|---|---|
| `show` | `area`, `area_device`, `area_location`, `area_waypoint`, `attribute`, `device`, `device_interface`, `device_location`, `l1_interface`, `l1_link`, `l2_broadcast_domain`, `l2_interface`, `l3_broadcast_domain`, `l3_interface`, `waypoint`, `waypoint_interface`, `waypoint_location` |
| `add` | `area_location`, `device`, `device_location`, `ip_address`, `ip_address_bulk`, `l1_link`, `l1_link_bulk`, `l2_segment`, `l2_segment_bulk`, `portchannel`, `portchannel_bulk`, `virtual_port`, `virtual_port_bulk`, `vport_l1if_direct_binding`, `vport_l2_direct_binding`, `waypoint` |
| `delete` | `area`, `device`, `ip_address`, `l1_link`, `l2_segment`, `portchannel`, `virtual_port`, `vport_l1if_direct_binding`, `vport_l2_direct_binding`, `waypoint` |
| `rename` | `area`, `attribute_bulk`, `device`, `l3_instance`, `port`, `port_info_bulk` |
| `export` | `ai_context_file`, `master_file_backup`, `master_file_nodata`, `device_file`, `l1_diagram`, `l2_diagram`, `l3_diagram` |

Every verb requires `--master <path>` pointing at a `[MASTER]*.xlsx` (or
`.nsm`) file. The `export` verb adds object-specific flags such as
`--type`, `--area`, `--attribute`, and `--format`.

> [!NOTE]
> The `--master` flag is positional in spirit but technically a named flag
> — it can appear anywhere on the line. The CLI rejects any command that
> doesn't include it with `[ERROR] Master file must be selected --master
> <filepath>.`.

### 4.3 The `export` flag matrix

These are the flags the engine actually inspects, by output type:

| Export | Required flags | Optional flags | Default behavior |
|---|---|---|---|
| `l1_diagram` | `--master` | `--type {all_areas, all_areas_tag, per_area, per_area_tag}`, `--attribute <title>`, `--format {pptx, svg}` | `--type all_areas_tag --format pptx` |
| `l2_diagram` | `--master`, `--area <name>` | `--format {pptx, svg}` | `--format pptx` |
| `l3_diagram` | `--master` | `--type {all_areas, per_area}`, `--attribute <title>`, `--format {pptx, svg}` | `--type all_areas --format pptx` |
| `device_file` | `--master` | — | Always emits `[DEVICE]<basename>.xlsx` |
| `ai_context_file` | `--master`, `--accept-security-risk` | — | Emits `[AI_Context]<basename>.txt` |
| `master_file_nodata` | `--master` | — | Emits `[MASTER]no_data.xlsx` next to the input |
| `master_file_backup` | `--master` | — | Emits a timestamped backup |

> [!IMPORTANT]
> `export ai_context_file` will refuse to run unattended unless you pass
> `--accept-security-risk`. The interactive prompt requires a literal
> uppercase `YES`. This is intentional: the AI Context bundles your entire
> network plan (areas, devices, IPs, segments, VPN targets) into a single
> text file meant to be pasted into a hosted LLM — see Section 5.4.

### 4.4 The Online HTTP surface

When the web UI does anything, it's calling these routes. They're useful
to know if you're scripting against the server.

| Method | Route | Purpose |
|---|---|---|
| `GET` | `/` | Renders the single-page UI |
| `POST` | `/upload` | Uploads a master file; returns `{job_id, filename}` |
| `POST` | `/create_empty_master` | Streams down a fresh `[MASTER]no_data.xlsx` |
| `POST` | `/heartbeat/<job_id>` | Resets the idle-eviction clock for a session |
| `GET` | `/restore/<job_id>` | Rehydrates a session (master, areas, device count, generated files) |
| `POST` | `/generate_step/<job_id>/<step>` | Generates a single output type (e.g. `l1_diagram`, `l2_diagram`, `l3_diagram`, `device_file`, `ai_context_file`) |
| `POST` | `/run_commands/<job_id>` | Executes a batch of `add`/`delete`/`rename`/`show` commands and creates a versioned master |
| `GET` | `/svg_grid_stream/<job_id>` | Server-Sent-Events stream of all SVGs in parallel |
| `GET` | `/files/<job_id>` | JSON list of generated artifacts |
| `GET` | `/download/<job_id>/<filename>` | Raw download |
| `GET` | `/download_visio/<job_id>/<filename>` | SVG converted to a Visio-friendly form |
| `GET` | `/download_drawio/<job_id>/<filename>` | SVG converted to draw.io XML |
| `GET` | `/download_all/<job_id>` | ZIP of every artifact for the session |
| `GET` | `/preview/<job_id>/<filename>` | In-browser preview (SVG, PowerPoint, Excel) |

`<job_id>` is always a UUID4. The server validates it strictly with
`re.match(r'^[a-f0-9\-]{36}$', job_id)`; anything else returns `400`.

---

## 5. Common Tasks

### 5.1 Build an L1 diagram for every area in one go

```bash
cd network-sketcher_offline
python ns_cli.py export l1_diagram \
  --master "[MASTER]campus.xlsx" \
  --type all_areas_tag \
  --format pptx
```

```text
--- L1 Diagram exported successfully ---
[L1_DIAGRAM]AllAreasTag_campus.pptx
```

| `--type` value | Output |
|---|---|
| `all_areas` | One diagram of the entire network, **without** interface name tags |
| `all_areas_tag` | One diagram of the entire network, **with** interface name tags |
| `per_area` | One diagram per area, **without** tags |
| `per_area_tag` | One diagram per area, **with** tags |

The web UI calls the same engine with `--type all_areas_tag` for the main
preview pane and `--type per_area_tag` for the per-area thumbnails.

### 5.2 Render an L2 broadcast domain to SVG

L2 diagrams are area-scoped — there's no "all L2" view because broadcast
domains rarely span an entire backbone.

```bash
python ns_cli.py export l2_diagram \
  --master "[MASTER]campus.xlsx" \
  --area "DC1-LEAF" \
  --format svg
```

```text
--- L2 Diagram exported successfully ---
[L2_DIAGRAM]DC1-LEAF_campus.svg
```

> [!NOTE]
> If you pass an area name that doesn't exist in the master file's
> `Master_Data_L2` sheet, the command emits `[WARNING] No L2 segments
> found in area '<name>'` and exits without writing a file.

### 5.3 Color devices by an attribute (e.g. role / vendor)

L1 and L3 diagrams accept `--attribute <title>`, which paints each device
according to the column you name in the master file's **Attribute** sheet.

```bash
python ns_cli.py export l1_diagram \
  --master "[MASTER]campus.xlsx" \
  --type all_areas_tag \
  --attribute "Role"
```

In the web UI, the same control is the **Attribute** dropdown on the main
panel. Switching it forces the SVG grid to regenerate; the previous
attribute value is cached so flipping back-and-forth is free.

### 5.4 Bulk-update the master with LLM-generated commands

The fastest way to mutate a large master file is to ask an LLM to produce
the right `add` / `delete` / `rename` / `show` commands and paste them
into the **Update Master** panel of the web UI (or feed them line-by-line
through `ns_cli.py`).

1. Click **Generate** on the **AI Context File** card. The server runs all
   `show` commands in parallel and writes
   `[AI_Context]<basename>.txt`.
2. Type a request in the **LLM Prompt** field (e.g. *"Add a new
   aggregation switch AGG-3 to area DC2-AGG with two L1 links to AGG-1
   and AGG-2"*).
3. Click **Copy & Open LLM** — the AI Context plus your prompt is copied
   to the clipboard and Claude (or your configured LLM) opens in a new
   tab.
4. Paste the LLM-generated command list into the **CLI Commands** box and
   click **Run**.

The server dispatches them with `--skip_sync` on every step except the
last of a syncable run, then performs one consolidated L2/L3 sync. A new
versioned master (`[MASTER]<basename>_<n>.xlsx`) is written so your
original is never overwritten.

> [!CAUTION]
> The AI Context file contains your **entire** network plan in plain
> text: device names, IP plans, VLANs, VPN targets, area hierarchy. Treat
> it like any other configuration export. The Offline CLI requires
> `--accept-security-risk` to skip the interactive `YES` prompt — see
> Section 7 of the Administrator & Developer Guide for the recommended
> handling guidance.

### 5.5 Export a Visio- or draw.io-friendly diagram

The web UI exposes two derived downloads beside every SVG output:

- **Download Visio** — calls `/download_visio/<job_id>/<filename>`, which
  inlines CSS classes, drops SVG2-only attributes (`dominant-baseline`,
  `auto-start-reverse` markers), strips outer-frame `<rect>` elements,
  and forces `fill="white"` on transparent rects so Visio doesn't fill
  them black.
- **Download draw.io** — calls `/download_drawio/<job_id>/<filename>`,
  which parses the Network Sketcher SVG and emits a fully-mapped
  `mxGraphModel` with `mxCell` nodes for rects, edges for lines, and
  merged labels for text inside rects. Open the `.drawio` file in
  [draw.io](https://www.drawio.com/) or
  [diagrams.net](https://app.diagrams.net/) and edit it as a normal
  diagram.

Both transforms run on the cached SVG bytes — no engine re-execution is
required, so they're fast.

### 5.6 Generate a device summary workbook

```bash
python ns_cli.py export device_file --master "[MASTER]campus.xlsx"
```

```text
--- Device file created successfully ---
```

The output, `[DEVICE]campus.xlsx`, contains four normalized sheets — L1
table, L2 table, L3 table, and Attribute table — derived from the master
data. This is the workbook to hand to a network engineer who wants
"the table of every interface."

### 5.7 Restore a browser session after refresh / disconnect

The web UI persists every session in
`network-sketcher_online/uploads/<job_id>/` for up to `heartbeat_timeout`
seconds (5 minutes by default) of idleness. If you reload the page, the
front-end calls `/restore/<job_id>` with the UUID it saved in
`localStorage`; you get back the master filename, area list, device
count, link count, and the inventory of generated files.

> [!TIP]
> If you closed the tab more than five minutes ago, the upload directory
> has been wiped. Re-upload your master file — Network Sketcher is
> deterministic, so identical input gives identical output.

### 5.8 Stand up a multi-area pod from scratch

This is the recipe for a typical leaf-spine fabric in a brand-new master
file.

1. **Create an empty master.** `POST /create_empty_master` (or click
   **Create New Master File**) downloads `[MASTER]no_data.xlsx`.
2. **Add areas.** Use the **CLI Commands** panel:
   ```text
   add area_location DC1-SPINE 0 0
   add area_location DC1-LEAF  0 1
   add area_location DC2-SPINE 1 0
   add area_location DC2-LEAF  1 1
   ```
3. **Add devices** with explicit area assignment:
   ```text
   add device SPINE-1 area=DC1-SPINE
   add device SPINE-2 area=DC1-SPINE
   add device LEAF-1  area=DC1-LEAF
   add device LEAF-2  area=DC1-LEAF
   ```
4. **Wire L1 links** (use the bulk form for many at once):
   ```text
   add l1_link_bulk [["SPINE-1","Eth1/1","LEAF-1","Eth1/49"],["SPINE-1","Eth1/2","LEAF-2","Eth1/49"],["SPINE-2","Eth1/1","LEAF-1","Eth1/50"],["SPINE-2","Eth1/2","LEAF-2","Eth1/50"]]
   ```
5. **Render.** Click **Generate Selected** with L1, L2, and L3 outputs
   all checked.

---

## 6. Troubleshooting

A diagnostic decision tree:

```text
diagram won't render?
├── output file is missing entirely?
│   └── check stdout/stderr for [ERROR] / [WARNING] markers (see below)
├── output file exists but is empty / blank?
│   ├── L2 diagram?  → confirm the area name with `show area`
│   └── L3 diagram?  → set NS_DEBUG_L3=1 and re-run for verbose layout output
├── browser shows TLS warning?
│   └── that's expected on first connect — see issue below
└── server says "stuck"?
    └── check `network-sketcher_online/logs/server.log` for traceback
```

### Error: `[ERROR] Master file must be selected --master <filepath>.`

**Cause:** the CLI was invoked without `--master <path>`.

**Solution:** always pass `--master`. Even `show` commands require it.

```bash
python ns_cli.py show area --master "[MASTER]campus.xlsx"
```

**Prevention:** wrap your most-used invocations in shell aliases that
already include `--master "$NS_MASTER"`.

### Error: `Filename must start with [MASTER]`

**Cause:** the web upload validator rejected a file that didn't begin
with the literal prefix `[MASTER]`.

**Solution:** rename the file. Network Sketcher uses the prefix to
disambiguate generated outputs (`[L1_DIAGRAM]`, `[L2_DIAGRAM]`,
`[L3_DIAGRAM]`, `[DEVICE]`, `[AI_Context]`) from inputs.

**Prevention:** the **Create New Master File** button always names files
correctly.

### Error: `Sheet '<name>' not found in the workbook.`

**Cause:** the master file is missing one of the well-known sheets
(`Master_Data`, `Master_Data_L2`, or `Master_Data_L3`). Most often this
happens after an external edit in Excel where someone renamed a sheet by
hand.

**Solution:** open the workbook in Excel, restore the original sheet
name, and re-upload. The full list of expected sheet plus section pairs
is in `nsm_io.py` (see Section 8 of the Administrator & Developer
Guide).

**Prevention:** treat the master file as machine-managed. Make changes
through CLI verbs (`add` / `rename` / `delete`) instead of direct Excel
edits.

### Error: `Single-core fallback failed: Worker context not initialized`

**Cause:** the L2 diagram generator's multiprocessing pool failed to
start and the single-core retry path also failed to bootstrap. This is
the diagnostic from `nsm_l2_diagram_create.py:507`
(`ns_l2_diagram_create.py:503` in the Offline edition).

**Solution:** restart the Online server (`python stop_ns_online.py &&
python start_ns_online.py`) so a fresh pool is created. If the problem
persists, set `parallel_limit` to `1` in `ns_web_config.json` to force
the single-core path and re-test.

**Prevention:** keep at least 2 GB of free RAM per concurrent diagram on
busy servers. Out-of-memory kills are the most common cause.

### Error: `[ERROR] 処理がタイムアウトしました (600秒超過)。`

**Cause:** a diagram subprocess exceeded `subprocess_timeout`. The full
message advises reducing the dataset (*"for L3 All Areas, reduce the
number of areas or devices, or confirm with Per Area"*).

**Solution:** for L3, switch from `--type all_areas` to `--type
per_area` to render area-by-area. For all diagram types, raise
`subprocess_timeout` in `ns_web_config.json` (or set it to `null` for no
limit) on a host with enough RAM.

**Prevention:** profile your largest expected master file once and pin
`subprocess_timeout` accordingly.

### Error: `All random numbers (9000-9999) have been exhausted.`

**Cause:** the temporary-name generator in
`ns_option_convert_to_master.py` creates short numeric suffixes between
`9000` and `9999`. If you import 1000+ external diagrams without
restarting the GUI, every slot is taken.

**Solution:** restart the Offline GUI (this resets the generator) or
close and reopen the file dialog.

**Prevention:** import in batches of a few hundred and restart between
them.

### Issue: Browser shows "Your connection is not private" on first launch

**Cause:** Network Sketcher Online auto-generates a self-signed TLS
certificate (`Certs/server.crt`) on first start. Browsers correctly flag
it because no public CA signed it.

**Solution:** click **Advanced → Proceed to localhost (unsafe)**. If
you're exposing the server to a LAN, replace `Certs/server.crt` and
`Certs/server.key` with a cert from your internal CA and restart.

**Prevention:** see "Production TLS" in the Administrator & Developer
Guide.

### Issue: "L3 Configuration Diagram Bug Report" — known issue [#59](https://github.com/cisco-open/network-sketcher/issues/59)

**Status:** open as of 2026-04-28. Layout glitches in some edge-case L3
topologies. Set `NS_DEBUG_L3=1` and capture stdout when reporting; that
output gives the maintainers the layout traces they need.

### Issue: "L2 diagram/Per Area causes AttributionError" — fixed in [#56](https://github.com/cisco-open/network-sketcher/issues/56)

**Status:** closed. Make sure you're on the latest `main` (commit
`ae4fafb` or newer).

### Issue: "Adding an L1 wire on a device may delete L2 information on other wires" — fixed in [#55](https://github.com/cisco-open/network-sketcher/issues/55)

**Status:** closed. The fix lives in `nsm_sync_between_layers.py`. If
you hit this on an older checkout, upgrade.

### Issue: "File path concatenation error causes 'No such file or directory'" — fixed in [#54](https://github.com/cisco-open/network-sketcher/issues/54)

**Status:** closed. Was Windows path-separator handling.

### Issue: `add area_location` doesn't update the wire location — fixed in [#58](https://github.com/cisco-open/network-sketcher/issues/58)

**Status:** closed. After adding a new area, an `add l1_link` against
devices in that area now updates wire coordinates correctly.

> [!WARNING]
> If a master file becomes unparseable (`openpyxl` raises), the engine
> has no auto-repair. **Always** keep the timestamped backups in place —
> the CLI's `export master_file_backup` and the web upload's
> auto-versioning are your safety net. Do not delete
> `[MASTER]<name>_<n>.xlsx` files until you're confident the latest
> version is good.

---

## 7. FAQ

**Q: What's the difference between `.xlsx` and `.nsm`?**

`.xlsx` is the human-editable Excel form; `.nsm` is a compact ZIP of
Parquet form the engine uses internally for speed. The web upload route
accepts either and converts `.xlsx` to `.nsm` immediately (`xlsx_to_nsm`
in `nsm_io.py`). For day-to-day use, stick with `.xlsx` — it's the
source of truth.

**Q: Can I run Network Sketcher headlessly in a container?**

Yes — the Online edition is headless by design. Its
`ns_engine/nsm_adapter.py` injects no-op stubs for `tkinter`,
`tkinter.ttk`, `tkinter.filedialog`, and `tkinter.messagebox`, so no
display server is needed. You only need a Python 3.9+ runtime and the
`requirements_online.txt` dependencies.

**Q: Can I integrate with NetBox / CML / Visio / draw.io?**

Yes for ingestion: the **Offline GUI's External Systems** tab accepts
CML YAML, Visio SVG, draw.io SVG, and NetBox CSV exports and converts
them into a master file. For round-tripping out, every SVG output has a
one-click **Visio** and **draw.io** transform (see Section 5.5).

**Q: Why did the web UI create `[MASTER]campus_2.xlsx` after I ran some
commands?**

Any batch of CLI commands containing `add`, `delete`, or `rename` is
treated as a mutation and produces a new versioned master. Your original
is untouched. The "active master" pointer is moved to the new file
automatically; you can revert by editing `.active_master` in the upload
directory.

**Q: How do I prevent the AI Context export prompt from blocking my
script?**

Pass `--accept-security-risk` on the CLI:

```bash
python ns_cli.py export ai_context_file \
  --master "[MASTER]campus.xlsx" \
  --accept-security-risk
```

This skips the interactive `YES` prompt. **Only do this in environments
where you've separately addressed where the resulting file goes** — the
AI Context bundles your entire network plan in plain text.

**Q: Where do generated files go?**

| Edition | Path |
|---|---|
| Offline CLI | Same directory as the input `[MASTER]*.xlsx` |
| Offline GUI | Same directory as the input file |
| Online | `network-sketcher_online/uploads/<job_id>/`, downloadable via the web UI; cleared after `heartbeat_timeout` seconds of inactivity |

**Q: How do I increase the upload size limit beyond 50 MB?**

Edit `network-sketcher_online/ns_web_config.json` and bump
`max_file_size_mb`. Restart the server.

**Q: Does Network Sketcher support multiple users at the same time?**

The Online server isolates every upload by `job_id` (UUID4), so
multiple users can use the same instance concurrently. The Offline GUI
is single-user per process; run separate copies for separate users.

**Q: Where is logging?**

| Log | Path | Format |
|---|---|---|
| Access log | `network-sketcher_online/logs/access_YYYYMMDD.log` | One file per local date, retained `access_log_retention_days` days |
| Server log | `network-sketcher_online/logs/server.log` | Application stdout/stderr |
| Offline CLI | stdout only | Use shell redirection for persistence |

**Q: How do I report a bug?**

Open an issue at
[github.com/cisco-open/network-sketcher/issues](https://github.com/cisco-open/network-sketcher/issues).
Include the master file (or a minimized one that reproduces), the exact
CLI command or web-UI clicks, and the captured output (`server.log` for
the Online edition).

**Q: Is there a license fee?**

No. Network Sketcher is licensed under
[Apache 2.0](https://github.com/cisco-open/network-sketcher/blob/main/LICENSE).
Cisco Systems holds the copyright; community contributions are welcome
under the Cisco Open project's CLA.

---

_Generated April 2026 from cisco-open/network-sketcher at ae4fafb. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
