Skip to content

Repository files navigation

Mist Disconnect Console

Client disconnect RCA for Juniper Mist. Paste an Observer (read-only) API token, pick a site and client MAC, and get a verdict that correlates RF, 802.11 reason codes, DHCP/DNS, Marvis, Radio Management occupancy, 7-day radio events (including Post radar / DFS), and Microsoft Teams / Zoom calls.

Three ways to run the same engine: a Chrome / Edge extension (no install beyond the browser), the stdlib Python console, and the published web companion.

Latest: v1.4 — Chrome / Edge MV3 extension. Same RCA engine, no local server and no Python.

Click Run sample investigation on the home page to walk the demo with no token. Sample data uses fictional DEMO-AP-F2-* names only.

Python Mist Token Extension Release

What’s new in v1.4

A Chrome / Edge extension. The RCA engine is transcribed 1:1 from mist_disconnect_console.py; the local ThreadingHTTPServer is deleted rather than ported, because host_permissions let the extension page reach the Mist API directly. Nothing about the analysis changed — same correlations, same same-AP radar gate, same verdict.

Change Why
No local server, no Python The browser is the whole runtime. No 127.0.0.1:8765 hop, no py -3, nothing listening on the machine.
One package for both browsers Chrome and Edge install the same directory. The two APIs that actually diverge (chrome.sidePanel, browser.* promises) are unused, so there is no Edge-specific code.
Token is in-memory only It lives in one variable, is passed as an argument to the fetch layer, and dies with the tab. No chrome.storage, no localStorage, no cookies, and it never reaches the service worker. Idle wipe after 30 minutes.
Minimal permission surface Zero entries in permissions. Nine explicit host_permissions, one per Mist region — never <all_urls>. No content scripts, no dependencies, explicit CSP.
Parity is enforced, not asserted tests/parity.test.js is the port of the Python self_test(), all 182 assertions. tests/token-hygiene.test.js greps the shipped package for every storage and logging path the policy forbids. 35 blocks / 189 assertions.

The Python console stays in the repo as the reference implementation the parity suite is checked against.


What’s new in v1.3

Removed scoring mechanism as it was misleading — this app is meant to help provide RCA for troublesome client disconnect issues. The app is meant to help investigations of troublesome wireless clients by doing causative correlative analysis of data from a single client.

No 0–100 number, no Healthy / Degraded / Critical. The board leads with the RCA finding (primary cause + evidence). Same-AP DFS still gets the Alert · session on radar AP banner. Correlations are unchanged.


What’s new in v1.2.2

Publish install fix only. No RCA engine change.

Fix Why
Lockfile in sync A clean npm ci failed (ajv 6 vs 8). The companion site can install and publish again.

What’s new in v1.2.1

Bug fixes on the v1.2 radar store.

Fix Why
One radar banner Mist often returns the same association twice (connect times a fraction of a second apart). One DFS hit is now one Alert · session on radar AP card.
Radio events full screen Expand the 7-day table so every kept event is reachable.
Diagnose fetch hint A busy site (lots of devices and radio events) can take up to 60 seconds.
Teams timestamps Millisecond call start/end still overlap same-AP radar.

What’s new in v1.2

Code fixes. Same-AP radar correlation still requires a session on the AP that took DFS; neighbor radar is not a hit.

Fix Why
RadioEventStore Site RRM cannot filter by AP. Correlation now looks up radars by the session AP (and radio_stat BSSID aliases) instead of scanning the firehose.
Time-sliced fetch 24h / 7d lookbacks are split into windows so a radar storm in the last hour cannot hide a hit from hour 18.
Scrollable client-radar panel Dedicated “Radar hits on this client’s APs” table. Banner and radio-event tables scroll — the matching DFS row is no longer off-screen.
Open-session overlap Mist disconnect: 0 is treated as still associated. RRM ap / ap_mac / BSSID aliases are accepted.
Faster login and live Token validate is /self only. Live polls walk the newest hour of RRM, not the full 7-day page walk.

Python --self-test covers a buried client DFS among 2,000 neighbor radars, BSSID-family match, and disconnect: 0.


What’s new in v1.1

Feature Why it matters
Alert · session on radar AP If a client session was associated to the AP that took a DFS / Post radar hit, a pulsing banner names that session and that exact radio event. Juniper’s RRM docs: the AP deauthenticates every associated station.
Same-AP gate Radar on a neighbor, or on today’s serving AP when the client was elsewhere, is not a correlation. Session AP at the radar timestamp must equal the radar AP.
Exact rows under the banner The matching session (connect / disconnect / AP / SSID / band) and the matching Radio Event (date, channel 36 → 149, width, power, Post radar) are drawn under the alert — not just a paragraph.
7-day Radio Events Same source as Mist Site → Radio Management → Radio Events. The API requires band; the console fetches 5, 24, and 6 in parallel (5 GHz paginated — that is where DFS lives). Labels match the portal: Post radar, Interference AP non wifi, Scheduled site RRM.
Teams / Zoom (7 days) Call window, meeting id, audio/video quality. A Teams meeting in progress during same-AP Post radar is highlighted as the media failure.
Full session list Every association in the window, newest first, scrollable table. Rows that overlapped radar on that AP are tagged radar.
CLIENT_IP_ASSIGNED is OK DHCP Success / IP Assigned are positive events (Mist Insights). Only timed-out / denied / terminated / bad-IP are FAIL.

v1.1 alert: session on DEMO-AP-F2 during Post radar, with the exact session row and radio event (channel 36 → 149)

7-day Radio Events table matching the Mist portal (Post radar, Interference AP non wifi, Scheduled site RRM)

Entire session history with the radar-overlapped association highlighted

Microsoft Teams / Zoom calls for this MAC over 7 days


Install and run (Chrome / Edge extension)

The quickest path. Chrome or Edge 116+; nothing else to install.

From the download

  1. Download mist-disconnect-console-extension.zip and unzip it. You get a mist-disconnect-console-extension/ folder. (It is also attached to the v1.4 release.)
  2. Open chrome://extensions (Chrome) or edge://extensions (Edge).
  3. Turn on Developer mode.
  4. Load unpacked → select that folder.
  5. Click the toolbar icon. The console opens in its own tab — pick your API region, paste the Observer token, and diagnose. Run sample investigation works with no token at all.

From a clone

git clone https://github.com/InterconnectedSystems/lilac-lilac-maple-ruby.git
cd lilac-lilac-maple-ruby
git checkout v1.4

Then Load unpackedchrome-edge-extension/.

Test it

cd chrome-edge-extension
npm test          # node --test tests/ — no dependencies to install

Enterprise deployment — pack to CRX and push it with the ExtensionInstallForcelist policy. Same policy name on Chrome and Edge.

Store submission — a Web Store / Partner Center zip has manifest.json at the root with no wrapping folder, so it is built separately from the download above; exclude tests/, package.json, and chrome-edge-extension/README.md. Each of the nine host permissions needs its own justification in the listing (one per Mist API region — the user picks their region at connect time).

The extension's manifest.json reads "version": "1.0.0". That is the extension's own first version, which is what a store listing carries; it is independent of this repo's v1.4 tag.

chrome-edge-extension/README.md documents the token policy and its seven hardening measures in full.


Install and run (Python) — the v1.4 console

No pip packages. Windows, macOS, and Linux.

git clone https://github.com/InterconnectedSystems/lilac-lilac-maple-ruby.git
cd lilac-lilac-maple-ruby
git checkout v1.4

# Windows
py -3 mist_disconnect_console.py

# macOS / Linux
python3 mist_disconnect_console.py

It opens a local browser page. Ctrl+C stops the server. The API token is sent from the browser to this process, then to Mist over HTTPS GET — it is not written to disk.

python3 mist_disconnect_console.py --self-test

Install and run (web)

The Vite / React tree in this repo is the published companion UI. The RCA engine and UI live in mist_disconnect_console.py, chrome-edge-extension/, and src/components/console/app.tsx. Use the Python command above for the local RCA engine.

git clone https://github.com/InterconnectedSystems/lilac-lilac-maple-ruby.git
cd lilac-lilac-maple-ruby
git checkout v1.4
npm install
npm run dev

Screenshots

Captured from the built-in sample investigation (Sample HQ — Floor 2, fictional APs).

Home — Observer token gate

Desktop iPhone
Home page with API region, Observer token field, and Run sample investigation Mobile home with Standard practice and sample investigation

The console only issues GET requests. Use an Observer / read-only token from Organization → Settings → API Tokens. Org Admin and write-enabled keys do not belong here.

Investigation board

The banner is the first thing on the board when a session was on the radar AP.

Sample investigation: DFS session alert, RCA finding, correlated causes

Verdict Phone fold
RCA finding — Post radar on the AP this client was connected to Mobile board

Correlated causes

Correlated causes: Post radar on the connected AP, Microsoft Teams during Post radar, coverage, DHCP after roam

Radar and Teams cards include the call name, meeting id, session AP, radar timestamp, and pre → post channel.

Current radio values (occupancy)

Same stacked histogram Mist shows under Site → Radio Management → Current Radio Values.

Channel occupancy stacked bars: teal External APs, orange Site APs, red Non-Wi-Fi

Color Meaning
Orange Site APs
Teal External APs
Red Non-Wi-Fi interference

Event timeline and Marvis

Client events Marvis
Event timeline Marvis naming the demo AP

CLIENT_IP_ASSIGNED shows OK. DHCP timed out / denied stay FAIL.

Full board

Full sample investigation board


What it does

  1. Connect — Select the Mist region (default api.gc2.mist.com) and paste an Observer token. /self validates the org.
  2. Scope — Pick org, site, client MAC, and lookback (1h / 6h / 1d / 1w).
  3. Diagnose — Per-MAC stats, events, all sessions (paginated), Marvis, AP inventory, occupancy, RRM events by band (time-sliced), Teams/Zoom calls.
  4. Alert — If a session covered a Post radar / radar-detected event on that same AP, the banner shows that session and that radio row.
  5. RCA finding — Primary cause + evidence notes + same-AP radar / Teams correlations. No health score.
  6. Live monitor — Re-query stats/events plus the newest hour of radio events. Auto-pauses on Mist HTTP 429.

What gets correlated

Signal Gate
Post radar / DFS on the AP this session was on Session connect…disconnect covers the radar timestamp and session.ap (or BSSID family) equals the radar AP
Teams/Zoom in progress during that radar Call window overlaps the radar time and same-AP gate
RSSI / SNR vs deauth reason Coverage vs idle timeout vs handshake
DHCP / DNS after roam or assoc Failures only (not IP Assigned)
AP ping-pong and 5 → 2.4 Sticky / oscillating client
Serving-AP occupancy Site vs external vs non-Wi-Fi
RRM power / channel change Client associated to that AP at the change
Marvis narrative Names the AP the client used most of the time

A radar event on a different AP than the session is dropped. Same channel on another AP is coincidence, not a cause.


Occupancy vs the Mist portal

The histogram is this AP’s 20-minute RRM scan (/sites/{id}/rrm/current/devices/{device}/band/{band}).

Radio Events are GET /sites/{id}/rrm/events?band={5\|24\|6} with start/end matching the lookback (band is required; 400 valid band is required otherwise).


API usage (GET only)

Step Endpoint
Validate token GET /self
List sites GET /orgs/{org}/sites
Live client GET /sites/{site}/stats/clients/{mac}
Client search / events / sessions GET /sites/{site}/clients/search, .../clients/{mac}/events, .../clients/sessions/search
Marvis GET /orgs/{org}/troubleshoot
AP inventory + stats GET /sites/{site}/devices?type=ap, GET /sites/{site}/stats/devices
Occupancy GET /sites/{site}/rrm/current/devices/{device}/band/{24|5|6}
Radio events GET /sites/{site}/rrm/events?band={24|5|6}&start=&end=
Teams / Zoom GET /sites/{site}/stats/calls/search?mac={mac}&duration=7d

No configuration is written. The token stays in the browser tab and is never stored in a database.

The extension issues these same GETs straight from its own page under host_permissions, with no intermediary process; the Python console proxies them through the local server it opens.


Token practice

  • Create the key under Organization → Settings → API Tokens with Observer privileges.
  • Default region is api.gc2.mist.com.
  • Never paste Org Admin, Super User, or write-enabled keys.
  • In the extension the token is held in memory only — wiped on tab close and after 30 idle minutes, never written to chrome.storage or localStorage.

Repo layout

mist_disconnect_console.py   v1.4 RCA engine (stdlib only) — the reference implementation
chrome-edge-extension/       MV3 Chrome / Edge extension (1:1 engine port, no local server)
src/lib/mist/radio.ts        published companion radar store (same rules)
screenshots/                 sample investigation captures (fictional DEMO-AP-F2 names)
src/                         published web companion

About

Mist Disconnect Console — Juniper Mist client disconnect RCA (Observer token, occupancy, Marvis AP matching)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages