{
  "device": {
    "model": "EOC 2-32",
    "host": "http://ghn2e53.local"
  },
  "sources": {
    "mirror_root": "mirror/auth/site/cgi-bin",
    "markdown_map": "device-api-map.md",
    "live_checks_date": "2026-03-18"
  },
  "replacement_gui": {
    "entry_points": [
      "replacement-gui/login.html",
      "replacement-gui/index.html"
    ],
    "assets": [
      "replacement-gui/axing-eoc.css",
      "replacement-gui/axing-eoc.js",
      "replacement-gui/login.js",
      "replacement-gui/device-config.js",
      "replacement-gui/md5.js",
      "replacement-gui/axing-logo.png",
      "replacement-gui/axing-favicon.png"
    ],
    "visible_pages": [
      "/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"
    ],
    "legacy_pages_not_exposed": [
      "/cgi-bin/wifi"
    ],
    "runtime_notes": [
      "Serve the replacement GUI from the device origin so same-origin GET and POST calls to /cgi-bin/* work without proxying.",
      "The repository mirror is a local preview fallback only and must not be shipped as a firmware runtime dependency.",
      "Branding, titles, and the AXING product link are configured through replacement-gui/device-config.js."
    ]
  },
  "api_model": {
    "style": "classic-cgi",
    "transport": "html-pages-with-embedded-javascript-state",
    "read_pattern": "GET /cgi-bin/<page>",
    "write_pattern": "POST /cgi-bin/<page>",
    "xhr_found": false,
    "json_api_found": false,
    "notes": [
      "Current values are embedded directly into returned HTML as JavaScript variables.",
      "Configuration pages post back to themselves.",
      "Some Wi-Fi pages submit complete state, including hidden fields for values not directly edited on screen."
    ]
  },
  "authentication": {
    "bootstrap_endpoint": {
      "method": "GET",
      "path": "/cgi-bin/index",
      "purpose": "Returns the login bootstrap page with the challenge token."
    },
    "login_endpoint": {
      "method": "POST",
      "path": "/cgi-bin/login",
      "purpose": "Attempts login using challenge-response MD5.",
      "fields": [
        "LOGIN_USER",
        "Password",
        "Challenge",
        "LOGIN_RESPONSE"
      ],
      "algorithm": "LOGIN_RESPONSE = md5(admin + password + challenge)"
    },
    "observations": [
      "The login page exposes LOGIN_USER=admin and a hidden Challenge field.",
      "On 2026-03-18, the previously used password no longer authenticated successfully."
    ]
  },
  "live_observations": {
    "date": "2026-03-18",
    "readable_without_working_login_observed": true,
    "reachable_get_endpoints": [
      "/cgi-bin/left",
      "/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",
      "/cgi-bin/wifi"
    ]
  },
  "endpoints": [
    {
      "path": "/cgi-bin/left",
      "method_read": "GET",
      "method_write": null,
      "category": "shell",
      "purpose": "Returns the navigation menu and defines the visible admin surface.",
      "read_exposes": [
        "OrgRunMode",
        "menu targets via L0T0(...)"
      ],
      "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_targets": [
        "/cgi-bin/wifi"
      ],
      "write_fields": [],
      "apply_flags": [],
      "mirror_source": "mirror/auth/site/cgi-bin/left.html"
    },
    {
      "path": "/cgi-bin/info",
      "method_read": "GET",
      "method_write": "POST",
      "category": "status",
      "purpose": "Status dashboard and primary live device information page.",
      "read_exposes": [
        "StatusRadio",
        "StatusVap",
        "StatusVapSsid",
        "StatusVapMode",
        "StatusVapCh",
        "StatusVapPwr",
        "StatusVapRate",
        "StatusUserCnt",
        "StatusUserMac",
        "StatusUserRssi",
        "StatusUserTxRate",
        "StatusUserRxRate",
        "StatusGhnMac",
        "StatusVer",
        "StatusNetMode",
        "StatusIp",
        "StatusMask",
        "StatusGw",
        "StatusDNS1",
        "StatusDNS2"
      ],
      "write_fields": [
        "Refresh"
      ],
      "apply_flags": [],
      "behavior": {
        "submit_model": "self-post",
        "effect": "Refresh/reload rather than explicit configuration apply."
      },
      "mirror_source": "mirror/auth/site/cgi-bin/info.html"
    },
    {
      "path": "/cgi-bin/wifinet",
      "method_read": "GET",
      "method_write": "POST",
      "category": "network",
      "purpose": "Network mode and addressing configuration.",
      "read_exposes": [
        "OrgNetMode",
        "OrgBrIp",
        "OrgBrMask",
        "OrgBrGw",
        "OrgBrDns0",
        "OrgBrDns1",
        "OrgLIp",
        "OrgLMask",
        "OrgDhcpSIp",
        "OrgDhcpEIp",
        "OrgWIp",
        "OrgWMask",
        "OrgWGw",
        "OrgWDns0",
        "OrgWDns1"
      ],
      "write_fields": [
        "NetMode",
        "BrIp",
        "BrMask",
        "BrGw",
        "BrDns0",
        "BrDns1",
        "LIp",
        "LMask",
        "DhcpSIp",
        "DhcpEIp",
        "WIp",
        "WMask",
        "WGw",
        "WDns0",
        "WDns1",
        "ApplyNet"
      ],
      "apply_flags": [
        {
          "name": "ApplyNet",
          "value": "Apply"
        }
      ],
      "enumerations": {
        "NetMode": {
          "0": "Bridge (Static IP)",
          "1": "Bridge (DHCP Client)",
          "2": "NAT (Static IP)",
          "3": "NAT (DHCP Client)"
        }
      },
      "notes": [
        "The page performs subnet and DHCP range validation in JavaScript.",
        "The legacy UI only submits when values differ from Org* baselines."
      ],
      "mirror_source": "mirror/auth/site/cgi-bin/wifinet.html"
    },
    {
      "path": "/cgi-bin/wifiradio",
      "method_read": "GET",
      "method_write": "POST",
      "category": "wifi-radio",
      "purpose": "Radio-level Wi-Fi configuration for both bands.",
      "read_exposes": [
        "OrgCountryCode",
        "OrgClientIso",
        "OrgHideSSID",
        "OrgRChIdx",
        "OrgRChList",
        "OrgWifiOn",
        "OrgRMode",
        "OrgRCh",
        "OrgTxPwr",
        "OrgRChIdx5g",
        "OrgRChList5g",
        "OrgWifiOn5g",
        "OrgRMode5g",
        "OrgRCh5g",
        "OrgTxPwr5g",
        "OrgVapOn",
        "OrgWpa",
        "OrgCipher",
        "OrgSsid",
        "OrgPsk"
      ],
      "write_fields": [
        "CountryCode",
        "ClientIso",
        "HideSSID",
        "WifiOn",
        "TxPwr",
        "RMode",
        "RCh",
        "WifiOn5g",
        "TxPwr5g",
        "RMode5g",
        "RCh5g",
        "RChIdx",
        "RChIdx5g",
        "HVapOn_0",
        "HVapOn_1",
        "HVapOn_2",
        "HVapOn_3",
        "HVapOn_4",
        "HVapOn_5",
        "HVapOn_6",
        "HVapOn_7",
        "HSsid_0",
        "HSsid_1",
        "HSsid_2",
        "HSsid_3",
        "HSsid_4",
        "HSsid_5",
        "HSsid_6",
        "HSsid_7",
        "HWpa_0",
        "HWpa_1",
        "HWpa_2",
        "HWpa_3",
        "HWpa_4",
        "HWpa_5",
        "HWpa_6",
        "HWpa_7",
        "HCipher_0",
        "HCipher_1",
        "HCipher_2",
        "HCipher_3",
        "HCipher_4",
        "HCipher_5",
        "HCipher_6",
        "HCipher_7",
        "HPsk_0",
        "HPsk_1",
        "HPsk_2",
        "HPsk_3",
        "HPsk_4",
        "HPsk_5",
        "HPsk_6",
        "HPsk_7",
        "ApplyWireless"
      ],
      "apply_flags": [
        {
          "name": "ApplyWireless",
          "value": "Apply"
        }
      ],
      "notes": [
        "The page derives real channel values from channel index selectors before submit.",
        "The submit carries hidden VAP-related state as part of a full Wi-Fi payload."
      ],
      "mirror_source": "mirror/auth/site/cgi-bin/wifiradio.html"
    },
    {
      "path": "/cgi-bin/wifivap2g",
      "method_read": "GET",
      "method_write": "POST",
      "category": "wifi-vap",
      "purpose": "VAP configuration editor for the 2.4 GHz page variant.",
      "read_exposes": [
        "OrgCountryCode",
        "OrgClientIso",
        "OrgHideSSID",
        "OrgWifiOn",
        "OrgTxPwr",
        "OrgRMode",
        "OrgRCh",
        "OrgWifiOn5g",
        "OrgTxPwr5g",
        "OrgRMode5g",
        "OrgRCh5g",
        "OrgVapOn",
        "OrgWpa",
        "OrgCipher",
        "OrgSsid",
        "OrgPsk"
      ],
      "write_fields": [
        "CountryCode",
        "ClientIso",
        "HideSSID",
        "WifiOn",
        "TxPwr",
        "RMode",
        "RCh",
        "WifiOn5g",
        "TxPwr5g",
        "RMode5g",
        "RCh5g",
        "HVapOn_0",
        "HVapOn_1",
        "HVapOn_2",
        "HVapOn_3",
        "HVapOn_4",
        "HVapOn_5",
        "HVapOn_6",
        "HVapOn_7",
        "HSsid_0",
        "HSsid_1",
        "HSsid_2",
        "HSsid_3",
        "HSsid_4",
        "HSsid_5",
        "HSsid_6",
        "HSsid_7",
        "HWpa_0",
        "HWpa_1",
        "HWpa_2",
        "HWpa_3",
        "HWpa_4",
        "HWpa_5",
        "HWpa_6",
        "HWpa_7",
        "HPsk_0",
        "HPsk_1",
        "HPsk_2",
        "HPsk_3",
        "HPsk_4",
        "HPsk_5",
        "HPsk_6",
        "HPsk_7",
        "ApplyWireless"
      ],
      "apply_flags": [
        {
          "name": "ApplyWireless",
          "value": "Apply"
        }
      ],
      "notes": [
        "PSKs are embedded in clear text in the page source.",
        "The submit includes hidden radio context values in addition to visible VAP fields."
      ],
      "mirror_source": "mirror/auth/site/cgi-bin/wifivap2g.html"
    },
    {
      "path": "/cgi-bin/wifivap5g",
      "method_read": "GET",
      "method_write": "POST",
      "category": "wifi-vap",
      "purpose": "VAP configuration editor for the 5 GHz page variant.",
      "read_exposes": [
        "OrgCountryCode",
        "OrgClientIso",
        "OrgHideSSID",
        "OrgWifiOn",
        "OrgTxPwr",
        "OrgRMode",
        "OrgRCh",
        "OrgWifiOn5g",
        "OrgTxPwr5g",
        "OrgRMode5g",
        "OrgRCh5g",
        "OrgVapOn",
        "OrgWpa",
        "OrgCipher",
        "OrgSsid",
        "OrgPsk"
      ],
      "write_fields": [
        "CountryCode",
        "ClientIso",
        "HideSSID",
        "WifiOn",
        "TxPwr",
        "RMode",
        "RCh",
        "WifiOn5g",
        "TxPwr5g",
        "RMode5g",
        "RCh5g",
        "HVapOn_0",
        "HVapOn_1",
        "HVapOn_2",
        "HVapOn_3",
        "HVapOn_4",
        "HVapOn_5",
        "HVapOn_6",
        "HVapOn_7",
        "HSsid_0",
        "HSsid_1",
        "HSsid_2",
        "HSsid_3",
        "HSsid_4",
        "HSsid_5",
        "HSsid_6",
        "HSsid_7",
        "HWpa_0",
        "HWpa_1",
        "HWpa_2",
        "HWpa_3",
        "HWpa_4",
        "HWpa_5",
        "HWpa_6",
        "HWpa_7",
        "HPsk_0",
        "HPsk_1",
        "HPsk_2",
        "HPsk_3",
        "HPsk_4",
        "HPsk_5",
        "HPsk_6",
        "HPsk_7",
        "ApplyWireless"
      ],
      "apply_flags": [
        {
          "name": "ApplyWireless",
          "value": "Apply"
        }
      ],
      "notes": [
        "Same overall submit model as /cgi-bin/wifivap2g."
      ],
      "mirror_source": "mirror/auth/site/cgi-bin/wifivap5g.html"
    },
    {
      "path": "/cgi-bin/vlan",
      "method_read": "GET",
      "method_write": "POST",
      "category": "vlan",
      "purpose": "VLAN and per-port tagging configuration.",
      "read_exposes": [
        "OrgVlanOn",
        "OrgVlanVid",
        "OrgVlanPri",
        "OrgVlanInRule",
        "OrgVlanOutAction",
        "OrgIgmpOn",
        "OrgQosBaseOn"
      ],
      "write_fields": [
        "VlanOn",
        "Vid_0",
        "Vid_1",
        "Vid_2",
        "Vid_3",
        "Pri_0",
        "Pri_1",
        "Pri_2",
        "Pri_3",
        "InRule_0",
        "InRule_1",
        "InRule_2",
        "InRule_3",
        "OutAction_0",
        "OutAction_1",
        "OutAction_2",
        "OutAction_3",
        "IgmpOn",
        "QosBaseOn",
        "ApplyStcfg"
      ],
      "apply_flags": [
        {
          "name": "ApplyStcfg",
          "value": "Apply"
        }
      ],
      "port_map": [
        "LAN1",
        "LAN2",
        "EoC",
        "WiFi"
      ],
      "enumerations": {
        "InRule": [
          "Accept All",
          "VID Matched"
        ],
        "OutAction": [
          "Untag",
          "Tag",
          "Bypass"
        ]
      },
      "mirror_source": "mirror/auth/site/cgi-bin/vlan.html"
    },
    {
      "path": "/cgi-bin/user",
      "method_read": "GET",
      "method_write": "POST",
      "category": "account",
      "purpose": "Password change page.",
      "read_exposes": [
        "password change form fields only"
      ],
      "write_fields": [
        "USER_ORG_PSWD",
        "USER_NEW_PSWD",
        "USER_CNF_PSWD"
      ],
      "apply_flags": [],
      "notes": [
        "The page checks that new and confirm password match before submit.",
        "The user is warned that the system will log out automatically after a successful password change."
      ],
      "mirror_source": "mirror/auth/site/cgi-bin/user.html"
    },
    {
      "path": "/cgi-bin/upload",
      "method_read": "GET",
      "method_write": "POST",
      "category": "firmware",
      "purpose": "Firmware upgrade page.",
      "read_exposes": [
        "WifiVer"
      ],
      "write_fields": [
        "SelFile"
      ],
      "apply_flags": [],
      "encoding": "multipart/form-data",
      "validation": {
        "max_size_bytes": 25165824,
        "accepted_name_patterns": [
          "_PATCH_V",
          "_FULL_V"
        ],
        "patch_version_rule": "Patch image major/minor must match WifiVer major/minor."
      },
      "local_only_progress_fields": [
        "UpgFile",
        "Proc"
      ],
      "mirror_source": "mirror/auth/site/cgi-bin/upload.html"
    },
    {
      "path": "/cgi-bin/activechg",
      "method_read": "GET",
      "method_write": "POST",
      "category": "apply",
      "purpose": "Activation page for pending changes.",
      "read_exposes": [
        "activation confirmation form"
      ],
      "write_fields": [
        "ActiveButton"
      ],
      "apply_flags": [
        {
          "name": "ActiveButton",
          "value": "Active"
        }
      ],
      "mirror_source": "mirror/auth/site/cgi-bin/activechg.html"
    },
    {
      "path": "/cgi-bin/wifi",
      "method_read": "GET",
      "method_write": "POST",
      "category": "wifi-legacy",
      "purpose": "Legacy combined Wi-Fi/VAP page.",
      "read_exposes": [
        "OrgVapOn",
        "OrgSsid",
        "OrgPsk"
      ],
      "write_fields": [
        "VapOn_0",
        "VapOn_1",
        "VapOn_2",
        "VapOn_3",
        "VapOn_4",
        "VapOn_5",
        "VapOn_6",
        "VapOn_7",
        "Ssid_0",
        "Ssid_1",
        "Ssid_2",
        "Ssid_3",
        "Ssid_4",
        "Ssid_5",
        "Ssid_6",
        "Ssid_7",
        "Psk_0",
        "Psk_1",
        "Psk_2",
        "Psk_3",
        "Psk_4",
        "Psk_5",
        "Psk_6",
        "Psk_7",
        "ActiveButton"
      ],
      "apply_flags": [
        {
          "name": "ActiveButton",
          "value": "Active"
        }
      ],
      "notes": [
        "This page is reachable but not part of the visible hn-mode menu.",
        "PSKs are directly embedded in form fields.",
        "The replacement GUI intentionally does not expose this page."
      ],
      "mirror_source": "mirror/auth/site/cgi-bin/wifi.html"
    }
  ],
  "integration_guidance": {
    "recommended_read_sources": [
      "/cgi-bin/info",
      "/cgi-bin/wifinet",
      "/cgi-bin/wifiradio",
      "/cgi-bin/wifivap2g",
      "/cgi-bin/wifivap5g",
      "/cgi-bin/vlan",
      "/cgi-bin/user",
      "/cgi-bin/upload"
    ],
    "recommended_apply_step": {
      "path": "/cgi-bin/activechg",
      "method": "POST",
      "field": {
        "name": "ActiveButton",
        "value": "Active"
      }
    },
    "important_notes": [
      "A replacement GUI should submit complete field sets for Wi-Fi pages, not just changed values.",
      "Read data is embedded in JavaScript variables, so parsing HTML/JS is required.",
      "The device appears to expose highly sensitive values, including Wi-Fi PSKs, through readable CGI pages.",
      "Do not treat /cgi-bin/wifi as required for the new GUI unless you intentionally want to restore the older combined Wi-Fi editor."
    ]
  }
}
