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.0rc1.tar.gz (51.1 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.0rc1-py3-none-any.whl (40.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: aethernet_core-2.0.0rc1.tar.gz
  • Upload date:
  • Size: 51.1 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.0rc1.tar.gz
Algorithm Hash digest
SHA256 77fd29bc3c0796f8da6af084007870abb4ab562ceb7a2dcd43309fa641aed1d0
MD5 34103f69615bda1edcd45e3fb64cf64b
BLAKE2b-256 bab637c9795888179c230d61b701a041a692b8c7ac1fbb9d4aca5fd952d6dec5

See more details on using hashes here.

Provenance

The following attestation bundles were made for aethernet_core-2.0.0rc1.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.0rc1-py3-none-any.whl.

File metadata

File hashes

Hashes for aethernet_core-2.0.0rc1-py3-none-any.whl
Algorithm Hash digest
SHA256 b36a868c70e535583fc897c6c86a6e2a19eb41872278fea25ddbd99e5c244c00
MD5 1be7b24d1a9b0c21846feb1c700138a6
BLAKE2b-256 e7c28ae237b7e1a4355047afde24d295d2e28c1176bff37e5e5a5dbf91dd63df

See more details on using hashes here.

Provenance

The following attestation bundles were made for aethernet_core-2.0.0rc1-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