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-1.0.1.tar.gz (37.9 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-1.0.1-py3-none-any.whl (35.3 kB view details)

Uploaded Python 3

File details

Details for the file aethernet_core-1.0.1.tar.gz.

File metadata

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

File hashes

Hashes for aethernet_core-1.0.1.tar.gz
Algorithm Hash digest
SHA256 60ddceae8be95e9cf4e9cafe0fcbeccddba78f1de1b984791fc3d362201626f4
MD5 32ade45dab406e0fa418a3846e3d03a9
BLAKE2b-256 fd830e89e6741051069e8adc35b6dbfeb4717499faf5a4a920a8ef96a4cdeb04

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: aethernet_core-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 35.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for aethernet_core-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5fd202d9c9a5b6fcca7728c00e27b7c95093ade3d94c511d36fb8e6df5dbd16b
MD5 05b70d9a00ddd1429c2092a2f02cbfb5
BLAKE2b-256 2e56ff41ff791d839693c2cdd36c850f1e182c51d6162f343927c62104aae1a6

See more details on using hashes here.

Provenance

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