release: Mark VIII 1.0

This commit is contained in:
c4ch3c4d3
2026-08-11 20:24:24 -07:00
commit 3e1805dab8
86 changed files with 20678 additions and 0 deletions
@@ -0,0 +1,215 @@
# Recon Mark VII Parity — Design Spec
- **Date:** 2026-08-11
- **Status:** Approved
- **Owner:** WiFi Pineapple Pager expansion project
- **Applies to:** `payload/user/general/pager-webui/www/` (front-end) and
`payload/user/general/pager-webui/server.py` (one backend change)
## 1. Goal
Rework the Pager WebUI Recon section so it looks and behaves as close as
possible to the stock Hak5 WiFi Pineapple (Mark VII) UI's Recon at
`http://172.16.42.1:1471/#/Recon`, using only data the Pager backend already
exposes plus one small optional-body change to `POST /api/recon/start`.
Source of truth for the Mark VII layout: the Angular bundle
`main.ce5a318adf590e170f6d.js` (captured 2026-08-11, cached at
`%TEMP%\opencode\oldui_main.js`). The scanning component (`app-scanning-view`)
template, component CSS, and Chart.js configs were extracted from it.
## 2. Mark VII Reference (extracted from bundle)
### Scanning page (`/Recon`, tab "Scanning")
```
div.recon-title-card-container (flex, wrap, justify: space-between, gap 10px)
div.recon-title-card-latest-overview
mat-card (height: 200px)
mat-card-title "Wireless Landscape" (font-size 20px)
mat-card-content > div > div.recon-wifidata-chart-container
canvas#landscapeChart (doughnut)
div.recon-center-text > span.recon-no-data " No wireless landscape data is available yet. "
div.recon-title-card-latest-overview "Channel Distribution" → canvas#channelChart (bar)
div.recon-title-card-latest-overview
mat-card (height: 200px)
mat-card-title > a.recon-card-title-link [routerLink /Recon/handshakes] " Handshakes "
content: <span font-size:32px><b>{totalHandshakes}</b></span>
<span color:grey>Handshakes Captured</span>
<mat-slide-toggle>Automatically Collect Any Handshakes</mat-slide-toggle>
div.recon-title-card-latest-overview
mat-card (height: 200px)
mat-card-title "Previous Scans"
content: mat-form-field (width 100%) > mat-label "Previous Scan" + mat-select
button[mat-icon-button] mat-icon "file_download"
button[mat-icon-button] mat-icon "delete"
mat-card (scan bar)
mat-card-content > div (height 48px, flex align-center)
mat-slide-toggle "Scan" (reconToggleState → startRecon/stopRecon)
div (margin-left 20px) mat-select (width 100px) scan_time
options: 30 Seconds/1 Minute/2 Minutes/5 Minutes/10 Minutes/Continuous
div (margin-left 20px) mat-select band (0 = 2.4GHz, 1 = 5GHz, 2 = 5GHz-DFS)
span[fxFlex] spacer
button[mat-icon-button] mat-icon "settings" (opens settings sidebar)
div (position: relative; min-height: 500px)
div.recon-scan-results-card
mat-card
mat-card-content
div (48px row) [AP table header: search mat-form-field "Search" + mat-paginator]
mat-table APs columns: SSID, MAC, Clients, OUI, Sec, WPS, MFP, Chan, Sign, FirstSeen, LastSeen
mat-card-content [Clients table: search + mat-paginator]
mat-table Clients columns: Client MAC, Signal, Channel, First seen, Last seen
```
Charts (from `generateLandscapeChart` / `generateChannelChart`):
- Landscape: `type:"doughnut"`, labels `["Access Points","Clients","Unassociated"]`,
backgroundColor `["#2ecc71","#2980b9","#8e44ad","#e74c3c"]`, `legend:{position:"bottom"}`.
- Channel: `type:"bar"`, one bar per channel sorted ascending, no legend, each bar
a color from a 64-color array, `yAxes:[{ticks:{reverse:!1,stepSize:1,min:0}}]`.
Component CSS (key rules, light theme):
- `.recon-title-card-container{display:flex;width:100%;flex-wrap:wrap;justify-content:space-between}`
- `.recon-title-card-latest-overview{flex:1;margin-bottom:1em}`
- `.recon-title-card-title{font-size:20px;margin-bottom:15px;display:flex;align-items:center}`
- `.recon-card-title-link{color:inherit;text-decoration:none}` `:hover{text-decoration:underline}`
- `.recon-title-card-content{display:flex;justify-content:center;align-items:center;height:70%}`
- `.recon-no-data{font-style:italic;color:#787878;display:flex;justify-content:center}`
- `.recon-scan-results-card{width:100%}`
- table rows cursor pointer; `.recon-table-row-active{background-color:#eaeaea}`
- dark: `.recon-table-row-active-dark{background-color:#565656}`
Tabs: Recon has exactly two tabs — `[Scanning → /Recon]`, `[Handshakes → /Recon/handshakes]`. No Events tab.
### Handshakes page (`/Recon/handshakes`)
Card "Captured WPA Handshakes" (title flex, settings icon button at right),
`table-container` with `mat-table`: columns `BSSID, Client, Source, Type, Captured,
Message 1, Message 2, Message 3, Message 4, Beacon Frame, [action]`. Rows show
M1M4 check/close/missing glyphs. Delete + download per row.
## 3. Design (Pager)
### 3.1 Tabs
`RECON_TABS` becomes two entries: **Scanning** (`#/recon`) and **Handshakes**
(`#/recon/handshakes`). Remove `views.recon_events`, the `#/recon/events`
route in `app.js`, and the old `views.recon_events` definition. Backend
`GET /api/recon/events` is left untouched.
### 3.2 Scanning view layout (`views.recon`)
Mark VII structure implemented with vanilla JS `h()` helpers and existing
`PagerAPI` endpoints. State per scan: `GET /api/recon/scans` → list;
`GET /api/recon/scans/{id}` → detail `{scan, aps[], clients[], handshakes[]}`.
- **Title cards row** (`.recon-title-card-container`, 200px cards):
1. **Wireless Landscape**`MiniChart.doughnut` with legend; segments
`[Access Points, Clients, Unassociated]` = `[aps.length, clients.length, 0]`,
colors `#2ecc71 / #2980b9 / #8e44ad`; placeholder text when no data.
2. **Channel Distribution**`MiniChart.bar`; per-channel counts from
detail APs (unknown channel grouped as `?`), Mark VII 64-color palette,
no legend, Y step 1.
3. **Handshakes Captured** — 32px bold count = selected scan handshakes
length; grey "Handshakes Captured" subtitle; "Automatically Collect Any
Handshakes" slide toggle wired to `GET/POST /api/pineap/settings`
(`collect_handshakes`); card title links to `#/recon/handshakes`.
4. **Previous Scans** — "Previous Scan" select (newest first, `Scan #id — time`
labels), `file_download` icon button → existing
`/api/recon/scans/{id}/download/json`, `delete` icon button → existing
`DELETE /api/recon/scans/{id}` (confirm dialog), refresh via poll.
- **Scan bar card** (48px row): "Scan" slide toggle (`POST /api/recon/start|stop`),
Duration select (30 Seconds / 1 Min / 2 Min / 5 Min / 10 Min / Continuous),
spacer, `settings` icon button → settings sidebar. No Band select (Pager
cannot single-band scan). Duration persisted in `localStorage`
(`pw_scan_duration`) and sent on start.
- **Results area** (`min-height:500px`), one card per table, each with a
Material-look "Search" field (client-side filter) and a paginator
(10/25/50/100, first/last/prev/next buttons, "110 of N" range label):
- **Access Points** — columns SSID, BSSID, Channel, Signal, Encryption,
Hidden; row click selects the AP (highlight) — no focus sidebar (backend
has no scan-AP deauth/capture APIs).
- **Clients** — columns MAC, Signal, Frequency, Packets.
- **Settings sidebar**: fixed right panel (top 64px, width 270px, shadow,
close button). Section "Access Points": Show SSID/MAC/Channel/Signal/
Encryption/Hidden toggles. Section "Clients": Show MAC/Signal/Frequency/
Packets toggles. Persisted as JSON in `localStorage` key `pw_recon_cols`.
- **Polling**: refresh scans/detail every 10s while the view is mounted
(destroy clears interval), same as today's behaviour (interval bumped from
15s → 10s to keep charts/table fresh).
### 3.3 Handshakes view (`views.recon_handshakes`)
Restyle to Mark VII look: card title "Captured WPA Handshakes" with a
`settings` icon button (non-functional on Pager; omitted or rendered as a
disabled hint — render it, clicking shows a toast "Settings not available on
Pager"). Table `File / Type / Size / Modified` plus per-row `Download` and
`Delete` actions (existing `/api/pineap/handshakes` endpoints + `/api/loot/zip`
for "Download all"). Keep existing zip/archive actions row.
### 3.4 Charts (`js/chart.js`)
- `MiniChart.doughnut(canvas, segments, { legend: true, height })` — redraw as
a true doughnut (inner hole via `arc` ring) and draw a legend row beneath
(color dot + label) when `legend` is set.
- `MiniChart.bar(canvas, items, opts)` — items `{label, value, color}`; draws
Y gridlines, bars from baseline, X labels, `maxY = ceil(max/1)`; Mark VII
palette cycles through `items[].color`.
- Keep existing `MiniChart.draw` (line) for the dashboard unchanged.
### 3.5 Icons (`js/icons.js`)
Add to `PineappleIcons`: `refresh`, `file_download`, `delete`, `settings`,
`search`, `first_page`, `last_page`, `chevron_left`, `chevron_right`
(Material Design path data; `wifi`/`extension`/`receipt` already present).
### 3.6 CSS (`css/app.css`)
Add Mark VII recon styles (light + `html.dark`): `.recon-title-card-container`,
`.recon-title-card`, `.recon-title-card-title`, `.recon-card-title-link`,
`.recon-title-card-content`, `.recon-no-data`, `.recon-chart-box`,
`.recon-scan-bar`, `.recon-scan-bar .sel`, `.recon-search`,
`.recon-paginator`, `.recon-settings-sidebar`, `.recon-settings-section`,
`.recon-settings-toggle`, `.recon-settings-close`, `.recon-row-selected`.
Card background follows `--surface`; title cards get the existing elevation
shadow; dark overrides via `html.dark`.
### 3.7 Backend (`server.py`)
`h_recon_start(ctx)` forwards an optional `scan_time` from the request body to
the daemon:
```python
def h_recon_start(ctx):
body = {}
scan_time = (getattr(ctx, 'body', None) or {}).get('scan_time')
if scan_time is not None:
body['scan_time'] = int(scan_time)
status, data = daemon_sock_call('POST', '/api/pineap/log/recon/start', body=body)
...
```
Default remains `{}` when no `scan_time` is sent, so the current behaviour is
unchanged. (Whether the Pager daemon honours `scan_time` is up to the daemon;
the UI persists and sends it either way.)
### 3.8 Routing (`js/app.js`)
Remove `'#/recon/events': 'recon_events'` from the `routes` map. No other
route changes.
## 4. Data flow
Unchanged API surface (except optional `scan_time` on recon start). The view
loads scans, auto-selects the newest, fetches the detail, renders charts +
tables, and re-fetches on a 10s interval. Search/pagination/column settings
are client-side state (`localStorage` keys `pw_scan_duration`, `pw_recon_cols`).
## 5. Testing
- Python: extend `tests/test_recon.py` `DaemonSockTest` with a case asserting
`h_recon_start` forwards `{'scan_time': 60}` when present and `{}` otherwise
(existing socket tests must stay green).
- Front-end: manual on-device smoke pass — every element of the scanning page,
tabs (2), scan start/stop, duration persistence, auto-handshake toggle,
previous-scan select + download + delete, table search/pagination, column
settings sidebar + persistence, handshakes page, dark theme, keyboard
shortcut `r`.
- Deploy via `scripts/deploy.ps1`; `curl` the served JS/CSS assets for 200.
## 6. Out of scope
- AP/client "focus" sidebars (deauth/capture/clone) — no backend support.
- 2D/3D cartography graph view — no AP↔client association data.
- Chart.js dependency — hand-rolled canvas rendering with identical palettes.
- Events tab, band select, "highlight active devices" aux settings.
- Backend removal of `/api/recon/events`.