Metadata-Version: 2.4
Name: hjindep-egress
Version: 0.1.1
Summary: Standalone observation of this machine HTTP egress: application vs proxy-free exit IP, with geolocation.
Author-email: YrralH <hj00@tju.edu.cn>
License-Expression: MIT
Project-URL: Homepage, https://github.com/YrralH/hjindep-egress
Project-URL: Issues, https://github.com/YrralH/hjindep-egress/issues
Keywords: egress,proxy,network,geolocation,cli
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet
Classifier: Topic :: System :: Networking :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: cli
Requires-Dist: typer<1,>=0.12; extra == "cli"
Dynamic: license-file

# hjindep-egress

Observe **this machine's** HTTP egress: where your traffic actually exits, and
whether your proxy configuration changes that.

It probes two paths concurrently and reports both:

- **application path** — honours the environment proxy (`HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY`)
- **proxy-free path** — bypasses it

Each path yields a public exit IP and an approximate geolocation, so you can
see at a glance whether your proxy is actually in the loop (`same_exit: false`)
or silently doing nothing (`same_exit: true`).

## Install

The library core has **zero third-party dependencies** — standard library only:

```bash
pip install hjindep-egress          # library: installs nothing else
pip install "hjindep-egress[cli]"   # + the command, which needs typer
```

Requires Python **3.12+**. The `[cli]` extra pulls `typer>=0.12,<1`, which
brings `rich`, `pygments`, `markdown-it-py`, `mdurl`, `shellingham` and
`annotated-doc` with it. If you only import the library, none of those are
installed.

## Platform

Pure Python, no C extensions — the wheel is `py3-none-any`. Two things differ
per platform, and both are dispatched at runtime, so nothing Windows-only is
imported on POSIX or vice versa:

| | POSIX | Windows |
|---|---|---|
| Cache lock | `fcntl.flock` | `LockFileEx` via `msvcrt` |
| Cache location | `$XDG_CACHE_HOME`, else `~/.cache` | `%LOCALAPPDATA%`, else `~\AppData\Local` |

Both backends are type-checked under `strict`: the default `pyrightconfig.json`
covers the POSIX tree, and `pyrightconfig.windows.json` checks the Windows
backend with `pythonPlatform: Windows` (it cannot be checked on a POSIX host,
where `msvcrt` does not resolve).

**Verified on Linux.** The test suite covers both backends, but the Windows
cases only execute when run on Windows — on other hosts they report as skipped.

## CLI

```bash
hjindep-egress                    # card output on a TTY
hjindep-egress --format json      # structured, with schema_version
hjindep-egress --format plain     # stable line-per-field text
hjindep-egress --compact          # two-line summary
hjindep-egress --watch            # live refresh in an alternate screen
```

Exit code is `1` when either path fails to produce a complete observation, so
it composes into health checks.

## Library

One call gives you everything the CLI prints:

```python
from hjindep_egress import observe_egress_status

status = observe_egress_status()

print(status.status)            # 'ok' | 'degraded' | 'unavailable'
print(status.same_exit)         # True when the proxy changes nothing
print(status.proxy_configured)  # whether a proxy is set at all

for name, path in (('application', status.application),
                   ('proxy-free', status.proxy_free)):
    if path.location is None:
        print('%s: %s (%s)' % (name, path.ip, path.error_code))
        continue
    where = path.location.location
    print('%s: %s -- %s, %s' % (name, path.ip, where.city, where.country))
```

Each path is reported independently, so one failing does not cost you the
other. `observe_egress_ips()` and `query_ip_location()` are also exported if
you want the raw probe without the geolocation join.

Geolocation results are cached under the user cache directory
(`$XDG_CACHE_HOME/hjindep-egress/` or `%LOCALAPPDATA%`), LRU-capped, with
cross-platform file locking. No privileged paths, no daemon, no configuration.

## Layout

| Module | What lives there |
|---|---|
| `observation` | The concurrent two-path probe, trace parsing, IP validation |
| `cache/` | Geolocation cache: document format, LRU, POSIX/Windows locking |
| `status` | Joins probe and cache into one `Egress_Status` |
| `view` | Projects a status into a format-independent view |
| `render` | Turns a view into card, plain, or JSON text |
| `box` | Terminal geometry: display width, panels, card grids |
| `screen` | Alternate-screen driver for `--watch` |
| `cli` | The command itself -- the only module needing typer |

## Endpoints

| Purpose | Endpoint |
|---|---|
| Exit IP discovery | `https://cloudflare.com/cdn-cgi/trace` |
| Geolocation | `https://ipwho.is/<ip>` |

HTTPS is mandatory, redirects are rejected, and responses are size-capped.

## Stability

This is a **standalone snapshot**, published as-is. It is not tracked against
any upstream, and **no API stability is promised across versions**.

Pin the version you tested against. If you need a change, open an issue or fork
it — a fix made here does not propagate anywhere else.
