Metadata-Version: 2.4
Name: glinet-profiler
Version: 0.3.0
Summary: Local capture launcher + registry for GL.iNet device API profiles.
Project-URL: Homepage, https://github.com/shauneccles/glinet-profiler
Project-URL: Repository, https://github.com/shauneccles/glinet-profiler
Project-URL: Changelog, https://github.com/shauneccles/glinet-profiler/blob/main/CHANGELOG.md
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Networking
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: aiohttp>=3.8.4
Requires-Dist: libpass>=1.8
Requires-Dist: paramiko>=3
Description-Content-Type: text/markdown

# glinet-profiler

A small **local** web launcher that captures a GL.iNet router's API surface
(read-only), sanitizes it into a shareable **profile**, checks whether that
device + firmware is already in the registry, and lets you download it or open
a prefilled submission.

Your **password never leaves your machine**: it goes from your browser to a
local server (`127.0.0.1`) to your own router. Nothing is uploaded unless you
deliberately submit.

> **Built with AI assistance.** Most of this code was written by Claude — human-directed,
> reviewed change by change, and verified against real hardware. It's open source; read it
> and judge for yourself. The read-only-by-default behaviour and the sanitization are the
> parts to scrutinise.

> **Why a local launcher and not a public site?** GL.iNet's RPC sends no CORS
> headers, so a public browser page cannot talk to your local router. The
> enumeration therefore runs server-side in this launcher (native Python,
> which is not subject to CORS), and the UI is served locally.

## Quick start

Once published you'll be able to run it with no install:

```bash
uvx glinet-profiler
```

From a source checkout:

```bash
uv run glinet-profiler            # starts the launcher, opens your browser
uv run glinet-profiler --no-browser --port 8765
```

Then enter your router URL (e.g. `http://192.168.8.1`), username (`root`), and
password, and click **Capture**. You'll get a sanitized profile, a
"already-known / new" banner, and **Download** / **Submit** actions.

## What's in the profile (and what isn't)

The published profile keeps the device **model + firmware**, non-identifying
**capability flags** (regulatory region + the software/hardware feature map), and
the **per-method API shape**: status, risk, [gli4py](https://github.com/shauneccles/gli4py)
coverage, params, and a response **signature**.

The **signature** is distilled from a real response — on your machine, before
anything is written. Field **structure** and *safe* example values (numbers,
booleans, and short enum-like strings such as `"5g"` / `"ap"`) are kept because
they're the API contract; **anything that could identify you or your network is
replaced with a format label, never a real value**:

| kept verbatim | replaced with a format label |
| --- | --- |
| numbers, booleans, enums (`"5g"`) | MAC → `<mac>` · IPv4 / IPv6 → `<ipv4>` / `<ipv6>` |
| field names + nesting | timestamps → `<datetime>` |
| | passwords / keys / tokens / serials → `<secret>` |
| | SSIDs / hostnames / domains / free text → `<string>` |

**Dropped entirely:** device identifiers (`mac`, `sn`, `sn_bak`), credentials, and
every raw response body. Your real IPs, hostnames, SSIDs, MACs, and secrets are
**never published — only their format.** (`--keep-data` keeps the redacted values
*locally* for your own analysis; that output is not registry-publishable, and the
registry's validator independently rejects any MAC, serial, or raw value.)

Enumeration is strictly **read-only** (a built-in catalog tier, plus an optional
SSH read tier if you tick the box and have SSH access).

## Security

- The launcher binds **`127.0.0.1` only** and guards its API with a per-run
  session token plus a localhost host check (so no other web page can drive it).
- The password is used only to log into your router from the local process and
  is never persisted, logged, or sent anywhere remote.

## How it fits with gli4py and the registry

This package is the **capture launcher** only. The enumeration **engine** lives
inside it (`glinet_profiler/enumerator/`, originally developed in the gli4py
project) — there is **no runtime dependency on gli4py** (deps are just
`aiohttp`, `paramiko`, `libpass`).

- **[gli4py](https://github.com/shauneccles/gli4py)** — the typed GL.iNet Python
  **client library**. Each captured profile records, per method, whether the
  gli4py client already wraps it ("coverage") — a lens for Python developers.
- **[glinet-registry](https://github.com/shauneccles/glinet-registry)** — the
  public, community registry of device profiles (browse site + submission bot).
  The launcher fetches its manifest to tell you whether a device is already
  known, and **Submit** opens its issue form. It releases independently of this
  package.

## Development

```bash
uv sync --all-extras --dev
uv run pytest -q
uv run ruff check . && uv run mypy src && uv run pylint $(git ls-files '*.py')
```

## The three repos

- **glinet-profiler** (this repo) — the capture launcher + enumeration engine.
- **[glinet-registry](https://github.com/shauneccles/glinet-registry)** — the
  device-profile data, browse site, and submission bot.
- **[gli4py](https://github.com/shauneccles/gli4py)** — the GL.iNet Python client
  library (the "coverage" lens shown in each profile).

## License

GPL-3.0-or-later.
