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'sRestart=alwaysrelaunches 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 tomessage_router.logger.py— configures the root logger once, at startup.cli.py—runis the in-process foreground agent (what systemd'sExecStart=invokes);status/start/stop/restart/logsare a separate concern — they operate on the systemd service itself viaservice_cli.py, so "start" never means two different things.installer.py/uninstaller.py/service_unit.py/system_checks.py— turnpip install jetstan-agentinto a managed, hardened systemd service. See INSTALL.md for the full flow and design rationale.__main__.py— enablespython -m jetstan_agent(defaults torun).
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
-
Run the agent as shown above and leave it running.
-
From the HiveMQ Cloud console (or any MQTT client, e.g.
mosquitto_pub), publish a message tojetstan/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"}'
-
Confirm the agent's log output prints the received message.
Run the test suite
pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jetstan_agent-0.1.12.tar.gz.
File metadata
- Download URL: jetstan_agent-0.1.12.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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bef34a563d055a143142f2e850c74ef3883631b38a309b90d348ab179f53614e
|
|
| MD5 |
b5ce2e79ec79abddf55df401eeffc534
|
|
| BLAKE2b-256 |
f09bc699a60fe81badc9bbb7170e09f873a4ce9ea16b72d8ebc7de03b30eb426
|
File details
Details for the file jetstan_agent-0.1.12-py3-none-any.whl.
File metadata
- Download URL: jetstan_agent-0.1.12-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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b88deb5408a9cd9e2986d290611fc7c00036ad2e52224b23c879e55d685923d9
|
|
| MD5 |
a24b3c15c44cd40a2618254fb8324385
|
|
| BLAKE2b-256 |
9177f740a69e2c794eb553830b199844d24dc5c465a0e89ef7f400587286e6a9
|