Skip to main content

Production-grade OTA agent for Linux-based vehicles — MQTT communication backbone.

Project description

Jetstan Agent

A production-grade OTA agent for Linux-based vehicles. It currently runs on a Raspberry Pi, but the architecture is designed to move to automotive Linux hardware without major changes.

It connects securely to HiveMQ Cloud, subscribes to the update topic, routes incoming commands, and — for ota.available — upgrades itself via pip and restarts. See INSTALL.md for running it as a real systemd service (boot-start, crash/hang recovery, the OTA update loop).

Project layout

jetstan_agent/
    __init__.py      Package version.
    __main__.py      Enables `python -m jetstan_agent`.
    cli.py            argparse entrypoint (`jetstan-agent` console script).
    config.py         All tunable values and environment-variable loading.
    mqtt.py           TLS connection, subscribe, publish, auto-reconnect.
    agent.py          Wires MQTT to message routing; keeps the process alive.
    message_router.py Parses JSON commands and dispatches by "type".
    updater.py         Self-upgrade via pip + restart-to-apply.
    sd_notify.py       Minimal systemd readiness/watchdog notifications.
    logger.py          Central logging configuration.
    system_checks.py   Read-only root/systemd/distro checks for the installer.
    service_unit.py    Renders the packaged systemd unit template.
    service_cli.py     systemctl/journalctl wrappers.
    installer.py        `jetstan-agent-install` entrypoint.
    uninstaller.py      `jetstan-agent-uninstall` entrypoint.
    templates/          Packaged systemd unit + default agent.env.
tests/
    (one test module per file above)

Why each file exists

  • config.py — the single source of truth for connection settings. Non-secret values (port, keepalive, topic) have defaults; the broker host, username, and password have none and must come from environment variables, so credentials never live in source control.
  • mqtt.py — everything specific to talking to the broker (TLS handshake, subscribe, reconnect, systemd readiness/watchdog signaling). It knows nothing about OTA — it only hands raw (topic, payload) pairs to a callback.
  • message_router.py — parses each payload as JSON and dispatches by "type". Unknown types and invalid JSON are logged and swallowed, never raised — a single bad message can't crash the agent.
  • updater.py — the actual OTA logic: pip install --upgrade jetstan-agent==<version>, then a deliberate process exit so systemd's Restart=always relaunches into the newly installed code. No custom download/signing pipeline — PyPI already does atomic package installs.
  • agent.py — the composition root. It owns the MQTT client and hands incoming payloads to message_router.
  • logger.py — configures the root logger once, at startup.
  • cli.pyrun is the in-process foreground agent (what systemd's ExecStart= invokes); status/start/stop/restart/logs are a separate concern — they operate on the systemd service itself via service_cli.py, so "start" never means two different things.
  • installer.py / uninstaller.py / service_unit.py / system_checks.py — turn pip install jetstan-agent into a managed, hardened systemd service. See INSTALL.md for the full flow and design rationale.
  • __main__.py — enables python -m jetstan_agent (defaults to run).

Configuration

All configuration is via environment variables (read by config.py).

Variable Required Default
MQTT_HOST Yes
MQTT_USERNAME Yes
MQTT_PASSWORD Yes
MQTT_PORT No 8883
MQTT_TOPIC No jetstan/demo/update
JETSTAN_MQTT_CLIENT_ID No jetstan-agent
JETSTAN_LOG_LEVEL No INFO

In production (systemd on the vehicle, CI, etc.) set these as real environment variables — e.g. a systemd unit's EnvironmentFile= — never hardcode them in a file that gets committed.

Local development: .env file

For local development, config.py automatically loads a .env file from the project root (via python-dotenv) before reading any configuration — no manual export needed, and no code changes.

cp .env.example .env
# then edit .env and fill in MQTT_HOST / MQTT_USERNAME / MQTT_PASSWORD

.env is git-ignored, so credentials never get committed. If a real environment variable is already set, it takes precedence over .env — so this has no effect on production deployments where .env won't even exist.

Install locally (editable)

python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -e ".[dev]"

Run

Either export the variables directly:

export MQTT_HOST="xxxxxxxx.s1.eu.hivemq.cloud"
export MQTT_USERNAME="your-username"
export MQTT_PASSWORD="your-password"

or, for local development, create a .env file as shown above and skip the export lines entirely. Then:

python -m jetstan_agent
# or, equivalently:
jetstan-agent run

On startup the agent connects over TLS, subscribes to jetstan/demo/update, and logs every message it receives. If the connection drops (or the broker is briefly unreachable), it reconnects automatically with backoff — no restart needed.

Test connectivity

  1. Run the agent as shown above and leave it running.

  2. From the HiveMQ Cloud console (or any MQTT client, e.g. mosquitto_pub), publish a message to jetstan/demo/update:

    mosquitto_pub -h <your-cluster-host> -p 8883 --cafile /etc/ssl/certs/ca-certificates.crt \
      -u <username> -P <password> -t jetstan/demo/update -m '{"version": "1.0.1"}'
    
  3. Confirm the agent's log output prints the received message.

Run the test suite

pytest

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

jetstan_agent-0.1.11.tar.gz (39.8 kB view details)

Uploaded Source

Built Distribution

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

jetstan_agent-0.1.11-py3-none-any.whl (35.2 kB view details)

Uploaded Python 3

File details

Details for the file jetstan_agent-0.1.11.tar.gz.

File metadata

  • Download URL: jetstan_agent-0.1.11.tar.gz
  • Upload date:
  • Size: 39.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for jetstan_agent-0.1.11.tar.gz
Algorithm Hash digest
SHA256 71adde4a2971a8b6bce8f0249cfd145c6724359061cb4b2f04f264fe552649dc
MD5 71e832d39d8f5700f137fe9a6085c560
BLAKE2b-256 f9eb3675d803d4967b1be3f8f8ba4f90fa9b3da3353918601ce8e838655f54d4

See more details on using hashes here.

File details

Details for the file jetstan_agent-0.1.11-py3-none-any.whl.

File metadata

  • Download URL: jetstan_agent-0.1.11-py3-none-any.whl
  • Upload date:
  • Size: 35.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for jetstan_agent-0.1.11-py3-none-any.whl
Algorithm Hash digest
SHA256 1ad36b70457a95b7596a4b8a5fbf17f67811e48387f3b0c94d01fa9544dd1442
MD5 5a5564ae65c69f2aab01c1f1c42a6f40
BLAKE2b-256 4206082eb787d239261bdfcaf97d60ceaf42b4a31b6b3fc7861cae3f4904c03c

See more details on using hashes here.

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