Skip to main content

Hub — the picotty package

The management side, packaged as the picotty Python distribution (uv). One process on one asyncio event loop runs two faces at once — a raw TCP server for the node swarm and a FastAPI app for the browser — over a shared in-memory registry and one SQLite database.

One distribution, three import surfaces:

Import What Needs
picotty.hub the server (registry + SQLite + :9000 TCP + FastAPI dashboard) [hub] extra
picotty.client the SDK: HubClient (REST) + HubEvents (WebSocket) base install
picotty.protocol wire framing, validation, PROTOCOL_VERSION base install

The lean base (httpx + websockets) is all a client needs; the server stack (FastAPI, uvicorn, aiosqlite, pydantic, pyyaml) comes from the [hub] extra, so a Pi Zero 2 W running only the Telegram sidecar stays small.

Layout

hub/
  pyproject.toml        # the picotty distribution (uv_build backend)
  uv.lock               # pinned, reproducible installs
  .python-version       # 3.11 floor
  src/picotty/
    __init__.py         # version, single-sourced from distribution metadata
    protocol.py         # public wire-protocol surface (re-exports hub/protocol.py)
    client/             # the SDK: HubClient (REST) + HubEvents (WS async iterator)
    sim.py              # the node simulator (picotty-sim console script)
    static/             # the dashboard, shipped inside the wheel (served at /)
    hub/                # the server
      main.py           # wires TCP + FastAPI + background tasks on one loop
      config.py         # process config (env) + operator-tunable defaults
      protocol.py       # length-prefixed JSON framing (authoritative)
      registry.py       # in-memory NodeState registry
      db.py             # aiosqlite: schema, queries, batched output, retention
      eventbus.py       # WebSocket fan-out with subscription filtering
      core.py           # the shared Hub: dispatch, ping/pong, audit, view-merge
      tcp_server.py     # swarm face on :9000
      telegram_setup.py # writes the sidecar .env for the dashboard's Telegram page
      tasks.py          # sweep, output flush, retention, stats, loop-lag
      api/{rest,ws,models}.py
  tests/                # test_db.py, test_integration.py, driver.py
  scripts/              # install / run / systemd unit

Run it (from source, with uv)

bash scripts/install.sh          # uv sync --extra hub (+ fetches terminal libs)
bash scripts/run.sh              # foreground (dev), loads private/hub-token.txt
# or install as a service that starts on boot:
bash scripts/install-service.sh  # renders + enables the systemd unit
journalctl -u swarm-hub -f       # follow logs

Manual equivalent:

cd hub
uv sync --extra hub
uv run --extra hub picotty-hub

Or install it as a tool (no repo checkout): uv tool install picotty, which puts picotty-hub and picotty-sim on PATH. See ../docs/packaging.md.

On first start the hub mints a shared node token and prints it once — put it in each node's settings.toml as NODE_TOKEN. It listens on :9000 (swarm) and :8080 (browser). Open http://localhost:8080 for the dashboard.

Configuration via environment (all optional): HUB_TCP_PORT, HUB_HTTP_PORT, HUB_DB_PATH, HUB_STATIC_DIR, HUB_TCP_HOST, HUB_HTTP_HOST, TELEGRAM_ENV_PATH. Runtime state (the SQLite DB) defaults to ~/.local/share/picotty/hub.db (honors XDG_DATA_HOME); the systemd unit uses /var/lib/picotty. Static assets ship inside the wheel. Operator-tunable settings (heartbeat, stale timeout, retention, confirm-dangerous, alerts) live in the DB and change via PATCH /api/settings.

Run under one uvicorn worker only. The single event loop is the design: a second worker would get its own registry and node sockets and the two would disagree about who is online.

Test without hardware

Start the hub, grab the printed token, then run one or more fake nodes with the packaged simulator:

uv run picotty-sim --id node-01 --token <TOKEN>
uv run picotty-sim --id node-02 --token <TOKEN>

Each simulator connects, heartbeats, answers commands, and streams fake serial output — enough to exercise the full dashboard. The suites:

uv run python tests/test_db.py           # offline db checks
uv run python tests/test_integration.py  # end-to-end over real TCP + REST

Using the client SDK

from picotty.client import HubClient

async with HubClient("http://hub:8080") as hub:
    print(await hub.health())
    async with hub.events_stream() as stream:   # the /ws feed
        await stream.subscribe("node-01")
        async for ev in stream:
            ...

This is what the Telegram sidecar imports; it needs only the base install.

Notes

  • Registry is disposable. On restart it starts empty and refills as nodes reconnect; the SQLite record survives.
  • Output is batched to SQLite (default every 500 ms) to protect SD-card write throughput; the live WebSocket stream is immediate and independent.
  • Auth is optional and off by default — the design assumes an isolated management VLAN reached through a VPN/tunnel, not port exposure.

Release files for picotty 1.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for picotty 1.0.3
File Size Uploaded
picotty-1.0.3.tar.gz 284.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for picotty 1.0.3
File Interpreter ABI Platform
picotty-1.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 585.5 kB

Release files / picotty-1.0.3.tar.gz

Download URL picotty-1.0.3.tar.gz
Size 284.5 kB
Tags Source
SHA-256 checksum
How to use checksums
91c634250a5f3ab824263360b9b6c87454b3cfdcb7d5cda144d4c11a10e6f41e
BLAKE2b-256 checksum
How to use checksums
a82f2303a9f3e744ad588850924ff1379b399d244358c8eda0a826784ba95426
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.

Transparency log

Release files / picotty-1.0.3-py3-none-any.whl

Download URL picotty-1.0.3-py3-none-any.whl
Size 301.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a0c59a20bd7e9a3736f759cf46356069aef176e20c9d66fa86af81e1a44316fb
BLAKE2b-256 checksum
How to use checksums
684a5554d3928999e270b8017f2cb3b2ea8f18466efd3e90d678d6aae2098983
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.

Transparency log

Release history Release notifications | RSS feed

1.2.0

2 release files

1.1.0

2 release files

This release

1.0.3 This release

2 release files

1.0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page