Skip to main content

Full HTTP and WebSocket traffic over any text-based transport — messengers, SMS, or anything else that can carry a string.

Project description

Aethernet (Æthernet)

Full HTTP and WebSocket traffic over any text-based transport — messengers, SMS, or anything else that can carry a string.

Aethernet is a lightweight tunneling library that lets you use the familiar httpx and websockets APIs without a direct internet connection. All traffic is serialized, optionally encrypted, and forwarded through a custom transport that you implement in a few lines of code.


Why Aethernet?

Some environments can send and receive text but cannot make direct TCP connections — think Telegram bots, SMS gateways, air-gapped systems, or embedded radios. Aethernet bridges that gap: your code keeps using standard HTTP and WebSocket clients while the actual bytes travel over whatever channel you have available.


Features

  • Drop-in HTTP client — fully compatible httpx.AsyncClient transport
  • WebSocket support — API modeled on websockets 16.0
  • End-to-end encryption — AEAD_CHACHA20_POLY1305, key is shared out-of-band
  • Configurable constraints — message size, character set, and rate limits match your transport's capabilities
  • Bring your own transport — implement one abstract class and you're done

Installation

# pip
pip install aethernet-core

# uv
uv add aethernet-core

Quick Start

Aethernet has two sides: a client that issues HTTP/WebSocket requests, and a server that has real internet access and executes those requests on behalf of the client.

Both sides share the same LowTransport implementation and the same encryption_key.

1. Define Your Transport

A transport is any object that can send and receive short text strings — a Telegram message, an SMS, a serial port line, and so on. Implement the three abstract methods:

from aethernet import LowTransport


class MyTransport(LowTransport):
    """
    Replace the bodies of send() and recv() with your real
    channel logic (Telegram API, SMS gateway, etc.).
    """

    def __init__(self) -> None:
        super().__init__()
        # Tune these to match your channel's actual limits:
        self.max_message_chars = 1000
        self.alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
        self.min_send_interval = 0.2   # seconds between sends
        self.min_recv_interval = 0.2   # seconds between reads

    def close(self) -> None:
        pass  # release connections, file handles, etc.

    def send(self, text: str) -> None:
        pass  # deliver `text` through your channel

    def recv(self, timeout: float | None = None) -> str:
        pass  # return the next incoming message

2. Client Side

import asyncio
import secrets
import httpx
from aethernet import AethernetHttpx, AethernetWebSockets, get_transport


SHARED_KEY = secrets.token_bytes(32)  # generate once, share securely


async def main() -> None:
    transport = await get_transport(MyTransport(), encryption_key=SHARED_KEY)

    # --- HTTP ---
    async with httpx.AsyncClient(transport=transport) as client:
        response = await client.get("https://httpbin.org/get")
        print(response.status_code, response.json())

    # --- WebSocket ---
    async with AethernetWebSockets(transport).connect("wss://echo.websocket.events") as ws:
        await ws.send("hello")
        message = await ws.recv()
        print("Echo:", message)


if __name__ == "__main__":
    asyncio.run(main())

3. Server Side

Run this on a machine that does have internet access and can receive messages from the client's transport.

import asyncio
import secrets
from aethernet import AethernetServer


SHARED_KEY = b"..."  # must match the client's key exactly


async def main() -> None:
    server = await AethernetServer.create(
        MyTransport(),
        encryption_key=SHARED_KEY,
    )
    print("Aethernet server is running...")
    await server.start_and_wait()


if __name__ == "__main__":
    asyncio.run(main())

Project Structure

aethernet-core/
├── .github/
│   └── workflows/
├── src/
│   └── aethernet/
│       ├── transport/
│       ├── __init__.py
│       ├── exceptions.py
│       ├── server_router.py
│       └── typing.py
├── tests/
├── pyproject.toml
├── LICENSE
├── NOTICE
└── README.md

Development

# Install all dependencies including dev extras
uv sync --extra dev

# Run the test suite
pytest

# Linting, formatting, and type checking
black --check .
ruff check .
mypy .

# Run the full CI pipeline locally (requires act)
act -W .github/workflows/ci.yml

Compatibility

Python Platforms
3.12 + Linux, macOS, Windows

Tested on Ubuntu with Python 3.12 and 3.13.


Bug Reports

Please open an issue and include:

  1. Expected behavior — what you intended to happen
  2. Actual behavior — what happened instead
  3. Reproduction steps — the minimal code or sequence of actions that triggers the problem

License

Apache License 2.0


Author

Walter Kerrigan (esolment)github.com/esolment

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aethernet_core-2.0.1rc1.tar.gz (50.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aethernet_core-2.0.1rc1-py3-none-any.whl (41.4 kB view details)

Uploaded Python 3

File details

Details for the file aethernet_core-2.0.1rc1.tar.gz.

File metadata

  • Download URL: aethernet_core-2.0.1rc1.tar.gz
  • Upload date:
  • Size: 50.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for aethernet_core-2.0.1rc1.tar.gz
Algorithm Hash digest
SHA256 04b4424211963d817d0441a8b22cf1d1b4fbc4f36d86b20cb931099e0ae3e37e
MD5 5f82a37e9e9e98ed058df1552a22562f
BLAKE2b-256 c9b9ff315942c2d42a34aa0596c43bf59c4ba845e6ffd4d23b779539ad0952ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for aethernet_core-2.0.1rc1.tar.gz:

Publisher: release.yml on solment-slet/aethernet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aethernet_core-2.0.1rc1-py3-none-any.whl.

File metadata

File hashes

Hashes for aethernet_core-2.0.1rc1-py3-none-any.whl
Algorithm Hash digest
SHA256 73ae0b4ee7df194fc2a1b3965104b28344dbda29f8e52d6d9ea8b8639147f7a2
MD5 4b0e69e10d0ac4b88cc87f67c0410334
BLAKE2b-256 b63a6e98b1a01db4d62ac818982a54599c5e257adfc7125ac291a28525712fa0

See more details on using hashes here.

Provenance

The following attestation bundles were made for aethernet_core-2.0.1rc1-py3-none-any.whl:

Publisher: release.yml on solment-slet/aethernet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page