# Device API Map

Device: `EOC 2-32`
Host: `http://ghn2e53.local`
Mapped from:
- local mirror under `mirror/auth/site/cgi-bin`
- live endpoint checks against `ghn2e53.local` on March 18, 2026

## Firmware Integration Notes

Frontend package to embed:
- `replacement-gui/index.html`
- `replacement-gui/login.html`
- `replacement-gui/axing-eoc.js`
- `replacement-gui/axing-eoc.css`
- `replacement-gui/login.js`
- `replacement-gui/device-config.js`
- `replacement-gui/md5.js`

Expected integration model:
- serve the replacement GUI from the device origin, not from an external host
- keep the existing CGI backend under `/cgi-bin/*`
- keep the replacement package assets together, including `replacement-gui/axing-logo.png` and `replacement-gui/axing-favicon.png`
- allow the replacement GUI to `GET` the existing CGI pages and `POST` back to the same CGI endpoints

Important packaging note:
- `replacement-gui/axing-eoc.js` now reads live data from `/cgi-bin/*` first
- it only falls back to `../mirror/...` when used as a local development preview in this repository
- for firmware, the `mirror/` directory is reference material, not a runtime dependency

Replacement GUI scope:
- the replacement navigation exposes `info`, `wifinet`, `wifiradio`, `wifivap2g`, `wifivap5g`, `vlan`, `activechg`, `user`, and `upload`
- the legacy reachable page `/cgi-bin/wifi` is documented below for completeness, but it is intentionally not exposed in the replacement GUI
- branding, titles, and AXING product links are configured through `replacement-gui/device-config.js`

## Overview

This device does not expose a modern JSON or REST API.

The web GUI is implemented as classic CGI endpoints under `/cgi-bin/*`:
- reads are done with `GET /cgi-bin/<page>`
- writes are done with `POST /cgi-bin/<page>` back to the same page
- current values are embedded directly into the returned HTML as JavaScript variables
- forms are standard HTML forms, not XHR or `fetch`

In practice, a custom web GUI can:
- read device state by requesting CGI pages and parsing embedded JavaScript variables
- write configuration by posting the same form field names the legacy pages use

Important live finding from March 18, 2026:
- the readable GUI pages were directly reachable with `GET` even though the previously working password no longer authenticated successfully
- this means the read surface appeared to be exposed without a valid session at the time of testing

## Authentication

### `GET /cgi-bin/index`

Purpose:
- returns the login bootstrap page
- exposes the challenge token used for login hashing

Returned fields:
- hidden `LOGIN_USER` with value `admin`
- hidden `Challenge`
- hidden `LOGIN_RESPONSE`

Behavior:
- the page JavaScript computes `LOGIN_RESPONSE = md5(admin + password + challenge)`
- the browser then posts that result to `/cgi-bin/login`

### `POST /cgi-bin/login`

Purpose:
- attempts login using the challenge-response flow

Expected form fields:
- `LOGIN_USER`
- `Password`
- `Challenge`
- `LOGIN_RESPONSE`

Observed behavior:
- historically worked with the earlier mirrored credential
- on March 18, 2026, the previously used password returned `Invalid Username or Password.`

## Menu And Shell Endpoints

### `GET /cgi-bin/left`

Purpose:
- returns the navigation menu
- defines the visible admin surface

Current visible menu targets:
- `/cgi-bin/info`
- `/cgi-bin/wifinet`
- `/cgi-bin/wifiradio`
- `/cgi-bin/wifivap2g`
- `/cgi-bin/wifivap5g`
- `/cgi-bin/vlan`
- `/cgi-bin/activechg`
- `/cgi-bin/user`
- `/cgi-bin/upload`

Additional reachable legacy page:
- `/cgi-bin/wifi`

Notes:
- the page uses `left.js` and `L0T0(...)` calls to build the menu
- `OrgRunMode = 'hn'` controls the visible set
- the replacement GUI intentionally omits `/cgi-bin/wifi` because the required inputs are already covered by `/cgi-bin/wifiradio`, `/cgi-bin/wifivap2g`, and `/cgi-bin/wifivap5g`

## Read/Write Endpoint Inventory

### `GET /cgi-bin/info`

Purpose:
- status dashboard
- primary live device information page

Reads and exposes:
- firmware version: `StatusVer`
- G.hn MAC: `StatusGhnMac`
- network mode: `StatusNetMode`
- IP address: `StatusIp`
- subnet mask: `StatusMask`
- gateway: `StatusGw`
- DNS servers: `StatusDNS1`, `StatusDNS2`
- radio state: `StatusRadio`
- VAP state: `StatusVap`
- SSIDs: `StatusVapSsid`
- Wi-Fi modes: `StatusVapMode`
- channels: `StatusVapCh`
- power levels: `StatusVapPwr`
- rates: `StatusVapRate`
- connected client count: `StatusUserCnt`
- per-client data: `StatusUserMac`, `StatusUserRssi`, `StatusUserTxRate`, `StatusUserRxRate`

How the legacy GUI uses it:
- JavaScript writer functions build tables from the `Status*` variables

Write behavior:
- page contains a `POST` form and a `Refresh` button
- the button submits the form back to the same page
- no distinct apply flag was found here
- practical effect appears to be refresh/reload rather than configuration change

Likely custom-GUI use:
- safe read source for live status widgets

### `GET /cgi-bin/wifinet`

Purpose:
- network mode and addressing configuration

Reads and exposes:
- `OrgNetMode`
- bridge static fields: `OrgBrIp`, `OrgBrMask`, `OrgBrGw`, `OrgBrDns0`, `OrgBrDns1`
- LAN fields for NAT mode: `OrgLIp`, `OrgLMask`, `OrgDhcpSIp`, `OrgDhcpEIp`
- WAN fields for NAT mode: `OrgWIp`, `OrgWMask`, `OrgWGw`, `OrgWDns0`, `OrgWDns1`

Modes exposed by the page:
- `0`: `Bridge (Static IP)`
- `1`: `Bridge (DHCP Client)`
- `2`: `NAT (Static IP)`
- `3`: `NAT (DHCP Client)`

Validation performed by the page:
- IP address format validation
- subnet consistency checks
- DHCP range subnet checks for NAT modes

### `POST /cgi-bin/wifinet`

Purpose:
- writes network configuration

Form fields used:
- `NetMode`
- `BrIp`
- `BrMask`
- `BrGw`
- `BrDns0`
- `BrDns1`
- `LIp`
- `LMask`
- `DhcpSIp`
- `DhcpEIp`
- `WIp`
- `WMask`
- `WGw`
- `WDns0`
- `WDns1`
- `ApplyNet=Apply`

What it does:
- updates operation mode and relevant IP configuration
- legacy JS only submits when values differ from the `Org*` baseline

Likely custom-GUI use:
- full network settings editor

### `GET /cgi-bin/wifiradio`

Purpose:
- radio-level Wi-Fi configuration page

Reads and exposes:
- country: `OrgCountryCode`
- client isolation: `OrgClientIso`
- SSID hiding: `OrgHideSSID`
- 2.4 GHz enable: `OrgWifiOn`
- 2.4 GHz mode: `OrgRMode`
- 2.4 GHz channel index and resolved channel: `OrgRChIdx`, `OrgRCh`
- 2.4 GHz channel list: `OrgRChList`
- 2.4 GHz transmit power: `OrgTxPwr`
- 5 GHz enable: `OrgWifiOn5g`
- 5 GHz mode: `OrgRMode5g`
- 5 GHz channel index and resolved channel: `OrgRChIdx5g`, `OrgRCh5g`
- 5 GHz channel list: `OrgRChList5g`
- 5 GHz transmit power: `OrgTxPwr5g`
- VAP backing state arrays also appear here:
  - `OrgVapOn`
  - `OrgWpa`
  - `OrgCipher`
  - `OrgSsid`
  - `OrgPsk`

Page behavior:
- derives real channel numbers from the selected channel index before submit
- keeps hidden shadow fields for values not directly edited in the visible section

### `POST /cgi-bin/wifiradio`

Purpose:
- writes radio configuration and hidden dependent Wi-Fi values

Form fields used:
- `CountryCode`
- `ClientIso`
- `HideSSID`
- `WifiOn`
- `TxPwr`
- `RMode`
- `RCh`
- `WifiOn5g`
- `TxPwr5g`
- `RMode5g`
- `RCh5g`
- `RChIdx`
- `RChIdx5g`
- `HVapOn_0` through `HVapOn_7`
- `HSsid_0` through `HSsid_7`
- `HWpa_0` through `HWpa_7`
- `HCipher_0` through `HCipher_7`
- `HPsk_0` through `HPsk_7`
- `ApplyWireless=Apply`

What it does:
- changes core radio settings for both bands
- also carries the current VAP-related values in hidden fields so the backend receives a complete Wi-Fi state payload

Likely custom-GUI use:
- radio band settings editor

### `GET /cgi-bin/wifivap2g`

Purpose:
- VAP configuration editor, focused on 2.4 GHz presentation but carrying both band contexts

Reads and exposes:
- `OrgCountryCode`
- `OrgClientIso`
- `OrgHideSSID`
- `OrgWifiOn`
- `OrgTxPwr`
- `OrgRMode`
- `OrgRCh`
- `OrgWifiOn5g`
- `OrgTxPwr5g`
- `OrgRMode5g`
- `OrgRCh5g`
- `OrgVapOn`
- `OrgWpa`
- `OrgCipher`
- `OrgSsid`
- `OrgPsk`

Visible edit groups:
- per-VAP enable/disable
- per-VAP SSID
- per-VAP WPA mode
- per-VAP pre-shared key

Field naming pattern:
- `HVapOn_0..7`
- `HSsid_0..7`
- `HWpa_0..7`
- `HPsk_0..7`

### `POST /cgi-bin/wifivap2g`

Purpose:
- writes VAP configuration

Form fields used:
- `CountryCode`
- `ClientIso`
- `HideSSID`
- `WifiOn`
- `TxPwr`
- `RMode`
- `RCh`
- `WifiOn5g`
- `TxPwr5g`
- `RMode5g`
- `RCh5g`
- `HVapOn_0` through `HVapOn_7`
- `HSsid_0` through `HSsid_7`
- `HWpa_0` through `HWpa_7`
- `HPsk_0` through `HPsk_7`
- `ApplyWireless=Apply`

What it does:
- updates VAP service state, SSIDs, WPA mode, and PSKs
- includes hidden radio context values in the same submit

Notable exposure:
- PSKs are embedded in clear text in the page source

### `GET /cgi-bin/wifivap5g`

Purpose:
- VAP configuration editor for the 5 GHz page variant

Reads and exposes:
- same data shape as `wifivap2g`

### `POST /cgi-bin/wifivap5g`

Purpose:
- writes VAP configuration through the 5 GHz page variant

Form fields used:
- same submit shape as `wifivap2g`
- `ApplyWireless=Apply`

What it does:
- same overall configuration submit pattern as `wifivap2g`

### `GET /cgi-bin/vlan`

Purpose:
- VLAN and port tagging configuration

Reads and exposes:
- `OrgVlanOn`
- `OrgVlanVid[0..3]`
- `OrgVlanPri[0..3]`
- `OrgVlanInRule[0..3]`
- `OrgVlanOutAction[0..3]`
- `OrgIgmpOn`
- `OrgQosBaseOn`

Port mapping used by the page:
- `0`: `LAN1`
- `1`: `LAN2`
- `2`: `EoC`
- `3`: `WiFi`

Per-port field naming:
- `Vid_0..3`
- `Pri_0..3`
- `InRule_0..3`
- `OutAction_0..3`

Rule meanings from page options:
- `InRule`: `Accept All` or `VID Matched`
- `OutAction`: `Untag`, `Tag`, or `Bypass`

### `POST /cgi-bin/vlan`

Purpose:
- writes VLAN state and port mapping rules

Form fields used:
- `VlanOn`
- `Vid_0` through `Vid_3`
- `Pri_0` through `Pri_3`
- `InRule_0` through `InRule_3`
- `OutAction_0` through `OutAction_3`
- `IgmpOn`
- `QosBaseOn`
- `ApplyStcfg=Apply`

What it does:
- toggles VLAN mode
- sets per-port VID, 802.1p priority, ingress rule, and egress tagging action

Likely custom-GUI use:
- port/VLAN policy editor

### `GET /cgi-bin/user`

Purpose:
- password change page

Visible fields:
- `USER_ORG_PSWD`
- `USER_NEW_PSWD`
- `USER_CNF_PSWD`

Page behavior:
- client-side check ensures new and confirm password match before submit
- user is warned the system will log out automatically after change

### `POST /cgi-bin/user`

Purpose:
- changes admin password

Form fields used:
- `USER_ORG_PSWD`
- `USER_NEW_PSWD`
- `USER_CNF_PSWD`

What it does:
- requests a password change
- exact backend confirmation/result flow was not exercised

### `GET /cgi-bin/upload`

Purpose:
- firmware upgrade page

Reads and exposes:
- current firmware version via `WifiVer`

Client-side checks performed:
- file must be selected
- file size must be `<= 24 MB`
- file name must contain `_PATCH_V` or `_FULL_V`
- patch uploads are restricted to matching major/minor version with `WifiVer`

### `POST /cgi-bin/upload`

Purpose:
- uploads firmware image

Encoding:
- `multipart/form-data`

Form fields used:
- `SelFile` for the chosen file

Local-only progress display fields:
- `UpgFile`
- `Proc`

What it does:
- sends the selected upgrade image to the device
- page JS hides the upload button area and shows a basic processing indicator

### `GET /cgi-bin/activechg`

Purpose:
- activation page for pending configuration changes

Visible action:
- one button named `ActiveButton`

### `POST /cgi-bin/activechg`

Purpose:
- activates pending changes immediately

Form fields used:
- `ActiveButton=Active`

What it does:
- confirms and applies queued changes

Likely custom-GUI use:
- explicit “apply pending changes” action after config edits

### `GET /cgi-bin/wifi`

Purpose:
- legacy combined Wi-Fi/VAP page

Reads and exposes:
- `OrgVapOn[]`
- `OrgSsid[]`
- `OrgPsk[]`

Notes:
- not part of the visible `hn` menu
- still reachable and functional
- not used by the replacement GUI

### `POST /cgi-bin/wifi`

Purpose:
- writes combined Wi-Fi VAP settings through the legacy page

Form fields used:
- `VapOn_0` through `VapOn_7`
- `Ssid_0` through `Ssid_7`
- `Psk_0` through `Psk_7`
- `ActiveButton=Active`

What it does:
- updates VAP enable state, SSIDs, and PSKs using the older combined page
- not required for the replacement GUI integration

## Data Exposure Summary

The following data categories are directly exposed by readable CGI pages:
- firmware version
- device MAC address
- network operating mode
- current IP, subnet, gateway, and DNS values
- radio state for 2.4 GHz and 5 GHz
- channel lists and current channel indexes
- transmit power values
- all configured SSIDs
- all configured PSKs
- WPA and cipher modes
- VLAN per-port configuration
- currently connected client list with RSSI and rates

## Practical Integration Model For A New GUI

If you build a replacement frontend, the workable model is:
- use `GET /cgi-bin/info` for live status
- use `GET /cgi-bin/wifinet`, `/wifiradio`, `/wifivap2g`, `/wifivap5g`, `/vlan`, `/user`, and `/upload` to pull current config and forms
- parse the embedded JavaScript `Org*` and `Status*` variables
- when saving, submit back to the same endpoint using the exact form field names listed above
- for changes that require activation, call `POST /cgi-bin/activechg` with `ActiveButton=Active`
- ignore `/cgi-bin/wifi` unless you explicitly want to restore the older combined Wi-Fi page

## Limits Of This Map

What was verified:
- endpoint existence
- readable page content
- embedded variable names
- visible form field names
- hidden apply flags
- client-side submit behavior

What was not executed:
- mutating configuration posts
- firmware uploads
- successful login using the old password on March 18, 2026

Inference:
- the backend likely expects full page-state payloads on some Wi-Fi submits, because several pages include hidden fields for values not directly edited on screen
- for that reason, a replacement GUI should submit complete field sets, not only changed values
