Metadata-Version: 2.4
Name: bridge-checker
Version: 1.0.0
Summary: Fast, concurrent OBFS4 Tor bridge reachability checker with a terminal UI
License: MIT
Keywords: tor,obfs4,bridges,censorship-circumvention,tui
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet
Classifier: Topic :: Security
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: textual>=0.60
Requires-Dist: rich>=13.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Dynamic: license-file

# bridge-checker

A fast, concurrent reachability checker for [Tor](https://www.torproject.org/) **OBFS4
bridges**, with an interactive terminal UI.

Bridges are unlisted Tor relays that help people get online in places where Tor
is blocked or throttled. Public bridge lists rot quickly — many entries go
offline — so this tool re-tests a list and tells you which bridges are
actually up right now.

![status](https://img.shields.io/badge/status-beta-yellow)
![license](https://img.shields.io/badge/license-MIT-blue)

## What it does

- Parses a plain-text list of `obfs4 ...` bridge lines (from a file or a URL).
- Opens a raw TCP connection to each `host:port` concurrently (default: 150 at
  once) and records whether it's reachable and how long it took.
- Streams results into a live terminal UI, or runs headlessly for scripts/CI.
- Writes the working (and, optionally, failed) bridges back out to plain
  bridge-list files you can drop straight into `torrc`.

**Note on what "reachable" means:** this only proves the host is accepting TCP
connections on that port — it does **not** perform the actual obfs4
handshake, which requires the real Tor/obfs4proxy stack. A bridge that passes
this check is very likely a live relay, but a full Tor connection is the only
true test.

## Install

```bash
pip install -r requirements.txt
pip install -e .
```

Requires Python 3.10+. Only dependencies are [`textual`](https://github.com/Textualize/textual)
(the TUI) and [`rich`](https://github.com/Textualize/rich) (headless progress output).

## Usage

### Interactive TUI (default)

```bash
bridge-checker
```

With no arguments it fetches a default public bridge list and drops you into
the TUI. Point it at your own list instead:

```bash
bridge-checker path/to/bridges.txt
bridge-checker https://example.com/my-bridges.txt
```

Keys: `s` start/refresh, `w` save working bridges to disk, `q` quit. You can
also change the source in the input field and click **Start**.

### Headless / scripting mode

```bash
bridge-checker bridges.txt --no-tui --output working.txt --failed failed.txt
```

```
Loading bridges from bridges.txt …
Found 214 bridge(s). Checking with concurrency=150, timeout=4.0s …

  ✔ 203.0.113.4:443              212 ms
  ✘ 203.0.113.9:8443
  ...
checking ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 214/214  up 137  down 77   0:00:06

Results
  Total checked : 214
  Working       : 137
  Failed        : 77
  Time elapsed  : 6.1s (35.1 bridges/s)
```

### All options

```
usage: bridge-checker [-h] [--workers CONCURRENCY] [--timeout TIMEOUT]
                       [--output OUTPUT] [--failed FAILED] [--no-tui] [--quiet]
                       [--version]
                       [source]

positional arguments:
  source                Path to a local bridge list, or a URL to fetch one
                        from

options:
  --workers, -w         Number of concurrent connection attempts (default: 150)
  --timeout, -t         Per-connection timeout in seconds (default: 4.0)
  --output, -o          Where to write working bridges
  --failed, -f          Also write failed bridges to this file
  --no-tui              Run headlessly instead of the interactive TUI
  --quiet, -q           Headless mode only: summary output only
```

## Project layout

```
bridge_checker/
  models.py     # Bridge / BridgeResult / RunStats dataclasses
  parser.py     # obfs4 line parsing
  fetch.py      # load a bridge list from a file or URL
  checker.py    # async concurrent TCP reachability checks
  tui.py        # Textual-based interactive UI
  headless.py   # rich-based non-interactive mode
  cli.py        # argument parsing / entry point
tests/
  test_parser.py
```

## Development

```bash
pip install -e ".[dev]"
pytest
```

## Releasing

`.github/workflows/publish.yml` builds and publishes the package to PyPI
whenever a tag matching `v*.*.*` is pushed (e.g. `v1.0.1`), or when run
manually from the Actions tab. It uses
[PyPI's trusted publishing](https://docs.pypi.org/trusted-publishers/) via
OIDC, so no API token/secret is needed — just:

1. Create a `pypi` environment on the PyPI project page and add this
   repository as a trusted publisher, pointing at the `publish.yml` workflow.
2. Bump the `version` in `pyproject.toml`.
3. Push a matching tag: `git tag v1.0.1 && git push origin v1.0.1`.

A separate `.github/workflows/ci.yml` runs the test suite on every push and
pull request.

## Contributing

Issues and pull requests are welcome. Please keep changes small and covered
by a test where practical.

## License

MIT — see [LICENSE](LICENSE).
