1
0
Fork 0
Mobile phone modem details for Librem5 https://spacecruft.org/librem/ModemLibre
  • Rust 92%
  • Python 7.5%
  • Shell 0.5%
Find a file
2026-10-31 11:21:41 -06:00
data Release ModemLibre 0.1.1 2026-10-29 11:51:56 -06:00
debian Fix 0.1.1 package test teardown 2026-10-29 12:39:50 -06:00
docs Update phase 4 docs 2026-10-31 11:21:41 -06:00
examples implement phase 4 2026-10-31 09:23:46 -06:00
LICENSES Implement phase 1 2026-10-24 21:41:06 -06:00
po Release ModemLibre 0.1.1 2026-10-29 11:51:56 -06:00
resources Release ModemLibre 0.1.1 2026-10-29 11:51:56 -06:00
scripts Release ModemLibre 0.1.1 2026-10-29 11:51:56 -06:00
src Format SIM fixture test 2026-10-29 14:27:40 -06:00
tests implement phase 4 2026-10-31 09:23:46 -06:00
.gitignore Rework for .deb builds, not flatpaks. Root access rework 2026-10-28 22:29:20 -06:00
AGENTS.md Clean up agents, mission 2026-10-30 15:40:59 -06:00
build.rs Rework for .deb builds, not flatpaks. Root access rework 2026-10-28 22:29:20 -06:00
Cargo.lock Release ModemLibre 0.1.1 2026-10-29 11:51:56 -06:00
Cargo.toml Release ModemLibre 0.1.1 2026-10-29 11:51:56 -06:00
CONTRIBUTING.md Document agent attribution and privacy rules 2026-10-29 14:44:45 -06:00
README.md Update phase 4 docs 2026-10-31 11:21:41 -06:00

ModemLibre

ModemLibre is a local, read-only modem diagnostics application for the Librem 5, PureOS Crimson/Phosh, Wayland, and adaptive workstation use. It requires no active data connection, online account, remote service, or telemetry.

Current scope

The selected Phase 4 slice adds MM-SIG-001, DER-RF-001, and fixed-query QMI support to the Phase 2/3 shared snapshot. Five adaptive destinations—Overview, Modem, Connection, Cells, and Diagnostics & Export—share one bounded observation owner. Cells provides serving, unclassified, component-carrier, and neighbor surfaces, per-RAT units/source age, targeted refresh, independent stale/error/ unsupported states, and canonical Overview RAT/band, signal, and serving-cell links. Advanced details are lazy; the 24-neighbor display limit does not truncate acquisition or snapshot completeness. Diagnostics & Export remains the only development placeholder.

The strict ModemManager client uses numeric-child introspection and exact asynchronous Properties.Get calls. Cached modem SignalQuality is read without Signal.Setup or Signal.SetupThresholds. Registration, emergency-only, bearer/IP, credential-disposal, and path-plus-epoch behavior are retained. There is no recursive bearer discovery, GetManagedObjects, GetAll, per-SMS access, content-bearing InterfacesAdded, InitialEpsBearerSettings, bearer Stats, NetworkManager, counter/sysfs, call/IMS/messaging/USSD/CBS, or MM extended-signal/ cell query or UI. Optional location/time retains its separate explicit request path. Phase 5 AT is not implemented. Bounded 2026-10-30 physical BM818-A1 research established a local ID_MM_PORT_TYPE_AT_PPP route and exact firmware/ RAT/serving-cell responses under support-contract-6. This selects fixed local read-only operations for implementation; it does not add a production AT backend, external service, arbitrary command route, E1 evidence, complete neighbors, or active-call-RAT support. The planned mediated route uses fixed AT-only protocol 2 and org.spacecruft.modemlibre.at.read, independently authorized from QMI protocol 1/qmi.read; that AT protocol and policy are not implemented.

Native package and QMI privilege boundary

The native Debian .deb is the only current package target. One ModemLibre package contains the unprivileged GUI, privileged QMI helper, system D-Bus activation, polkit policy, and required service integration. It declares ordinary OS dependencies, including stock ModemManager and qmicli/qmi-proxy; it does not bundle those services or require a second ModemLibre helper package or manual helper installation. Installation must not replace or modify NetworkManager, ModemManager, qmi-proxy, or their existing configuration.

The fixed integration identifiers are:

Item Value
Helper service org.spacecruft.modemlibre.QmiHelper
Helper object /org/spacecruft/modemlibre/QmiHelper
Helper executable /usr/libexec/modemlibre-qmi-helper
polkit action org.spacecruft.modemlibre.qmi.read
Helper protocol 1

The GUI never runs as root and has no production direct-qmicli, direct-QMI-device, sudo, or pkexec fallback. The system D-Bus activates the helper as root. On the first fixed batch of each GUI launch, the helper obtains one polkit authorization for the exact caller's D-Bus unique name. Subsequent batches in that launch reuse the in-memory launch session without another interactive prompt; denial does not trigger repeated prompts. Disconnecting the GUI or restarting the helper forgets the session. Each batch still independently validates the selected modem's current Ports relationship and stock /usr/bin/qmicli 1.32.2 before modem operations. Callers cannot choose arbitrary operations, device paths, executables, arguments, or environment.

Only these eight fixed operations are allowed through qmi-proxy:

  • --dms-get-ids, --dms-get-revision, and --dms-get-band-capabilities;
  • --nas-get-serving-system, --nas-get-system-info, --nas-get-rf-band-info, --nas-get-signal-info, and --nas-get-cell-location-info.

The helper uses one same-modem QMI CDC-WDM port (MMModemPortType QMI = 6, not AT = 3), fixed argument arrays without a shell, and a cleared environment with only LC_ALL=C, LANG=C, PATH=/usr/bin:/bin, and TZ=UTC. Workers, output, time, parsing, collections, and per-resource execution are bounded. Authorization denial, version rejection, independent operation failure, cancellation, timeout, close, suspend, owner/device/Ports changes, and stale completion are explicit. Cleanup must demonstrate pipe EOF and no surviving helper-spawned qmicli descendants. QMI failure does not erase MM data or successful sibling operations.

MM and QMI acquisition occurs once during initial load. Later backend/property/ lifecycle changes invalidate or queue stale work without issuing another query; only the user's reload action refreshes observations. Revealing a value is a UI- only operation and never triggers a backend query.

qmi-proxy is not a read-only operation allowlist. The fixed helper API and lack of a GUI bypass are required package properties; their installed behavior must be validated rather than inferred from source or package metadata.

Snapshots, RF, privacy, and export

Immutable revisions use snapshot schema 2, event model 2, parser 1.2.0, supported-table metadata 3.0.0, export schema 1, and export dictionary 2. The typed cell-location projection is LTE-centric; emitted content without a selected typed projection remains bounded sensitive raw-only evidence. QMI arguments, unknown fields, parser warnings, status/timing, and stdout/stderr are memory-only and never exported.

RF calculations use exact integer tables for LTE EARFCN, UTRA FDD/TDD UARFCN and TDD options, fixed GERAN ARFCN, and 28-bit LTE ECI. NR is raw-only; dynamic GSM and NB-IoT mappings are excluded. Missing or ambiguous context remains unresolved instead of becoming a guessed value. Sources and table provenance are pinned in docs/references.md.

Sensitive identifiers are masked by default and export-redacted. In-memory export is deny-by-default: object identities are omitted and raw payloads, commands, parser warnings, raw/free-form errors, credentials, message content, and history never export. Raw Diagnostics, clipboard, snapshot-file, and export actions remain absent. A future export must use portal-mediated user file selection; portals are also applicable to native applications and do not justify blanket home access.

Validation status

Tagged 0.1.1 build and offline archive checks pass. The release commit is a051af2c4eaf98e01bd60685beb520aa181fe678, exactly matching annotated tag 0.1.1. The final artifact is build/native-source/modemlibre_0.1.1_arm64.deb, SHA-256 276e0778667580315281db19b5f399184dd20b689ab6fa69d3a1671d0a2e5a4f. Its post-tag build and offline release suite passed 288/288 tests. The archive passed all 12 required-file and integration/boundary checks; helper linkage excludes GTK/libadwaita, and systemd-analyze verify passed.

This candidate authorizes once per GUI D-Bus connection/launch and retains that decision only until disconnect/helper restart. It also makes post-initial MM/QMI acquisition manual-only: backend changes invalidate or queue work, and only the user reload action queries again. Resume re-enables Refresh without starting an acquisition.

Current installed-package/helper core checks for P4-BM818-A1-QMI-CELLS have a bounded A1 pass. The exact 0.1.1 archive was already installed and its GUI/helper payloads match the installed binaries. The unprivileged GUI, root D-Bus helper, polkit allow/failed-authorization paths, same-launch authorization reuse, all eight live QMI operations, typed provenance, and tested idle/no-survivor cleanup passed on the phone. The canonical A1-only reduced scope and frozen candidate are defined in docs/support-matrix.md. Its EV-P4-A1 bundle is incomplete, including exact current QMI DMS firmware and real fixtures, so the claim remains BLOCKED and unadvertised. This is not a full Phase 4, real-fixture-bundle, exceptional-lifecycle, or E1 result. Lintian, sustained performance, and the remaining physical/support gates are NOT RUN or BLOCKED as detailed in docs/device-validation.md.

Current source evidence also includes formatting, strict all-target/all-feature Clippy, two serialized 288/288 test runs, both locked release binaries, and all seven notice checks. Supplemental isolated X11 testing also passed populated and empty structural AT-SPI plus nine structural/shell invocations at 360×720, 740×300, 1100×760, and 640×360. Shell assertions used a disposable navigation adapter and reveal timed out; these are not unmodified-harness or full-phone passes. The hardened development-only capture path passed independent privacy/ correctness re-review, 6/6 Rust sanitizer tests, 29/29 Python tests, and a clean-launcher self-test. No real capture was attempted because exact current QMI firmware provenance was not confirmed; no real fixture files were written. Exact source, artifact, and check details live in docs/device-validation.md.

Historical source evidence includes the 2026-10-28 native Crimson gate (238 tests), the earlier Phase 4 host gate (212 tests), and the 2026-10-29 checkout suite (247/247 tests), with the other checks and limits recorded in docs/device-validation.md. Historical device evidence includes limited real MM Overview/manual Modem checks and the explicitly authorized development-checkout fixed-root qmicli route. That sudo route is retired development evidence only, not a production fallback or native-helper validation. Retired Flatpak results in the evidence log are historical only and are not Debian/helper passes.

Full-phone acceptance, real A1/E1 fixtures, sustained performance, comprehensive phone accessibility/AT, remaining native package authorization, and Phase 5 AT remain BLOCKED or NOT RUN as applicable. Synthetic fixtures are not real A1/E1 captures.

Compatibility and dependency pins

Component Selected version or minimum
Rust edition 2024; MSRV 1.92; recorded Crimson development toolchain 1.99.1
gtk4-rs / libadwaita-rs 0.11.5 / 0.9.2
glib-rs / gio-rs 0.22.10; gettext-rs 0.8.0; roxmltree 0.21.x
Native API minima GLib/Gio 2.62, GTK 4.8, libadwaita 1.2
Measured Crimson native libraries GTK 4.8.3, libadwaita 1.2.2, GLib/Gio 2.74.6
Target OS/session PureOS Crimson; Phosh 0.34.0-2pureos6; phoc 0.33.0+ds-1pureos6
Modem APIs ModemManager 1.20.4-1pureos2; NetworkManager 1.42.4-1+deb12u1pureos1 is an unselected inventory pin
QMI route stock /usr/bin/qmicli 1.32.2 through qmi-proxy, mediated by the fixed native helper; P4-BM818-A1-QMI-CELLS bounded installed core PASS, full fixture/lifecycle/E1 evidence NOT RUN/BLOCKED
BM818 route A1 PPP-AT route/firmware/RAT/serving-cell research PASS under VAL-AT-001; production AT NOT RUN. E1, complete neighbors, active-call RAT, and lifecycle/package claims BLOCKED/NOT RUN

Crimson is the selected stable target; other PureOS suites are not inferred to be compatible. Record daemon, plugin, modem firmware, helper, and portal versions for each device claim. Host results do not establish phone compatibility, and one BM818 variant's evidence does not cover another.

Prerequisites

For on-device details see docs/BUILD-LIBREM5-ON-DEVICE.md. The following are setup instructions, not actions performed by this documentation update. Install packages only when explicitly desired:

sudo apt update
sudo apt install build-essential pkg-config libgtk-4-dev libadwaita-1-dev \
  gettext desktop-file-utils appstream dpkg-dev debhelper lintian \
  libglib2.0-dev dbus modemmanager systemd libqmi-utils libqmi-proxy polkitd

Use Rust 1.92 or newer; Crimson's older packaged Rust is insufficient. See the on-device guide for a user-local rustup toolchain. UI/AT-SPI checks also need an isolated display and AT-SPI bindings. Do not treat a host display as Phosh or physical-touch evidence.

Build, format, lint, and test

Run the canonical native gate from the repository root:

cargo fmt --all -- --check
cargo clippy --all-targets --all-features --locked -- -D warnings
cargo test --locked -- --test-threads=1
cargo build --release --locked
cargo build --release --locked --no-default-features --bin modemlibre-qmi-helper

Apply cargo fmt --all first when formatting changed. These are source/host checks, not installed-helper or real-modem evidence.

Regenerate dependency inputs with Cargo rather than hand-editing them:

cargo generate-lockfile
cargo vendor --locked

Regenerate Cargo.lock only for intentional dependency changes and vendor inputs only when required for the reviewed offline source package. Debian builds use locked offline Cargo with the reviewed vendored source replacement and separate GUI/helper commands; the helper uses --no-default-features to exclude GTK/ libadwaita. See docs/packaging.md.

Translations and metadata

Source, GTK UI, desktop, AppStream, and polkit action strings are included in gettext. The German catalog is intentionally partial; untranslated strings fall back to the source language and fuzzy matches are not used blindly.

sh scripts/update-pot.sh
msgmerge --update po/de.po po/modemlibre.pot
sh scripts/install-translations.sh build/prefix build/i18n
msgfmt --check -o /dev/null po/de.po
desktop-file-validate build/prefix/share/applications/org.spacecruft.modemlibre.desktop
appstreamcli validate --pedantic \
  build/prefix/share/metainfo/org.spacecruft.modemlibre.metainfo.xml

GNU gettext 0.24 has no Rust parser, so update-pot.sh uses its C parser for the current gettext("...") calls and Glade/Desktop/AppStream extractors for other inputs. Review the POT after changing Rust string syntax. Compiled catalogs and merged metadata are build outputs, not committed files.

Run from source

cargo run --locked

The GUI runs as the normal desktop user. Full QMI operation requires the installed native package's protocol-compatible helper integration and successful polkit authorization; there is no source-tree direct/sudo production fallback. Set MODEMLIBRE_LOCALEDIR to the absolute build/prefix/share/locale path for development translations. There is no development GSettings schema.

Native Debian package

The canonical staged source/binary package workflow is:

sh scripts/build-native.sh
candidate=build/native-source/modemlibre_VERSION_ARCH.deb
dpkg-deb --info "$candidate"
dpkg-deb --contents "$candidate"
lintian "$candidate"
PYTHONDONTWRITEBYTECODE=1 python3 tests/debian_package_smoke.py "$candidate"

The build script checks debian/changelog and rejects a Cargo/changelog version mismatch, prepares explicit staged source and locked vendor inputs, then runs dpkg-buildpackage -b -us -uc there. The 3.0 (native) source requires a version without a Debian revision suffix (0.1.1, not 0.1.1-1). Routine agent-managed releases increment only the third version digit; the user directs first/second- digit changes. Update debian/changelog and all version inputs, optionally pre- build/test, commit the intended source with a Co-Authored-By: <provider>/<actual-model> trailer identifying the model that performed the work, create the exact matching tag, then run the final build from that tagged revision. Every agent-created commit names its actual model. Before committing, audit tracked content for secrets and real device identifiers and use synthetic fixture identifiers. The build script performs no Git mutations. A separately provisioned toolchain not registered with dpkg requires the explicit -d build flag, as explained in the on-device guide. Replace the placeholder with the one exact candidate artifact when running or recording validation. Source preparation also verifies locked crate identities/checksums and deterministically generates the installed THIRD_PARTY_NOTICES from upstream notices; that attribution aid is not a legal-compliance determination. Install only that authorized candidate through apt so declared dependencies are resolved:

sudo apt install "$PWD/$candidate"

Then exercise modemlibre as the unprivileged desktop user. Package inspection, installation, service activation, polkit denial/allowance, all-eight-live-QMI, and cleanup checks are mandatory separate gates described in docs/testing.md and docs/packaging.md. Do not manually copy a helper, create a second helper package, or alter system service/proxy configuration.

Design and evidence

Contributors must not commit built outputs, expose prohibited content, weaken read-only or privilege boundaries, or claim unrun checks. See CONTRIBUTING.md.