Linux userspace IMS daemon
  • C++ 93.2%
  • Shell 6.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jorijn van der Graaf a625248e18
packaging, ci: build the apks with the Crafter.Build Flatpak
Crafter.Build's CI publishes a Flatpak and nothing else since
2026-10-02: the rolling `latest` release's
`crafter-build-linux-x86_64-musl-v2.tar.gz` that build-package.sh
fetched is gone (404 since 2026-10-03), so a push of a release would
fail at `fetch crafter-build` and publish nothing.

Install the Flatpak (`net.catcrafts.CrafterBuild//stable` from
flatpak.catcrafts.net) in the job container and run it as crafter-build.
bubblewrap inside the container needs a privileged runner, so the job
runs on the `flatpak` label. The sysroot stays Alpine edge and gains
llvm-runtimes, the package with the std module source of its own libc++
(libc++-dev ships none). make-sysroot.sh adds its list with --upgrade:
run over a sysroot from before this change (libc++ 22.1.8, no std module
source) a plain add installed llvm-runtimes 23.1.2 alone, and its
std.cppm did not compile against the 22.1.8 headers (`'text_encoding'
file not found`); with --upgrade libc++, libc++-dev and llvm-runtimes
came out at 23.1.2 and it compiled.

AGENTS.md §4.1, §4.3, §8.3 and §9.4 describe the route as it runs, and
the README's Building section carries the install lines. Three comments
in the files this touches said what is not so and are corrected:
package.yml had fp6-img install the package by a pinned version and
sha256 (it takes the newest in the registry), build-package.sh had
fp6-img's image build run `make check` on the aarch64 binaries (it
builds no imsd: no suite runs on them), and make-sysroot.sh's usage line
lacked the --march without which the cross build fails on an x86 host.

Tested: build-package.sh run end to end on this commit in a privileged
alpine:edge container (podman) on the workstation.
2026-10-06 14:01:33 +02:00
.forgejo/workflows packaging, ci: build the apks with the Crafter.Build Flatpak 2026-10-06 14:01:33 +02:00
implementations README, AGENTS.md: a short README for people, the detail in AGENTS.md 2026-10-06 14:01:33 +02:00
interfaces imsd, imsd-ofonod: let polkit decide call control, not the account name 2026-10-06 14:01:33 +02:00
packaging packaging, ci: build the apks with the Crafter.Build Flatpak 2026-10-06 14:01:33 +02:00
tests ims-pdn-up, imsd: register on the PDN's own address, not the kernel's temporary one 2026-10-06 14:01:33 +02:00
.gitignore imsd 0.2.7 — initial public snapshot 2026-07-22 23:04:21 +02:00
AGENTS.md packaging, ci: build the apks with the Crafter.Build Flatpak 2026-10-06 14:01:33 +02:00
LICENSE imsd 0.2.7 — initial public snapshot 2026-07-22 23:04:21 +02:00
lint-rules.h imsd 0.2.7 — initial public snapshot 2026-07-22 23:04:21 +02:00
Makefile imsd, imsd-ofonod: let polkit decide call control, not the account name 2026-10-06 14:01:33 +02:00
project.cpp imsd, imsd-ofonod: let polkit decide call control, not the account name 2026-10-06 14:01:33 +02:00
README.md packaging, ci: build the apks with the Crafter.Build Flatpak 2026-10-06 14:01:33 +02:00

imsd

Userspace IMS/VoLTE daemon for mainline Linux phones.

Modem firmware on typical mainline-Linux phones will bring up the IMS PDN but can't be used for actual call audio. imsd implements the whole IMS client in userspace instead: SIP registration with USIM AKA authentication over kernel IPsec (ESP), call signalling, and the AMR-WB RTP media plane wired into PipeWire so a stock mobile dialer — Plasma Dialer, or GNOME Calls under Phosh and GNOME Mobile — can place and receive real VoLTE calls on a commercial network.

Status

Call-capable, both directions: outgoing calls place, answer, carry full-duplex AMR-WB audio and tear down; incoming calls ring, answer and carry media the same way, driven end-to-end from a stock dialer over a frozen D-Bus ABI. Not there yet: DTMF (SendDtmf is a stub), hold/multiparty/call waiting, USSD, P-CSCF discovery (the address is configured by hand).

Verified by community members to be fully working:

Device OS Carrier
The Fairphone (Gen. 6) postmarketOS, Linux 7.1.2 KPN NL
The Fairphone (Gen. 6) postmarketOS, Linux 7.1.2 Telekom Deutschland GER
The Fairphone (Gen. 6) postmarketOS, Linux 7.1.2 Phonero
The Fairphone (Gen. 6) postmarketOS, Linux 7.1.2 Telia Norge

What it needs

imsd talks to the modem through ModemManager plus qmicli over QRTR, installs its security associations with ip xfrm and plays audio through PipeWire, so in practice: a Qualcomm modem on a mainline kernel with working WWAN/ModemManager integration, kernel ESP, PipeWire, systemd and polkit — and your carrier's P-CSCF address.

Install and configure

On a Catcrafts Fairphone 6 image imsd is installed from the project's apk registry and upgrades with apk upgrade (an upgrade restarts the daemon; a call in progress ends). Elsewhere, build it with make (below), run make install, and enable the units yourself — systemctl enable imsd, and systemctl enable --now imsd-ofonod for GNOME Calls: imsd's preset, the multi-user.target.wants link and the /etc/imsd.env condition below come with the apk, not with make install.

Configuration is environment variables in /etc/imsd.env; the one required setting is the P-CSCF:

# your carrier's P-CSCF address — from a stock-firmware capture or the
# carrier's IMS documentation
PCSCF=2001:db8::105

The apk's imsd.service is enabled by preset and starts at boot once that file exists (the condition is checked when the unit starts, so after writing the file run systemctl start imsd). It first brings the ims PDN up through ModemManager, then registers: resuming an existing registration when it can, registering fresh otherwise, and waiting out whatever the network tells it to wait out. Check on it with:

journalctl -u imsd -f
busctl --system call net.catcrafts.IMS1 /net/catcrafts/IMS1 net.catcrafts.IMS1 GetStatus

Every variable, what the daemon does when the registration breaks, and what the journal lines mean are in AGENTS.md (§10, §7.4, §7.8).

Dialers

imsd itself is desktop-agnostic: everything a dialer needs is on the system bus as net.catcrafts.IMS1. A small translation daemon per desktop speaks the backend protocol its dialer already consumes, so dialer, shell and the call-history/contacts stack run unmodified.

Desktop Daemon Package Owns Consumed by
Plasma Mobile imsd-dialerd imsd org.kde.telephony.* (session bus) plasma-dialer + kde-telephony-daemon, replacing modem-daemon
Phosh / GNOME Mobile imsd-ofonod imsd-ofono org.ofono (system bus) GNOME Calls' ofono provider plugin

Plasma Mobile needs nothing beyond the imsd package: imsd-dialerd is autostarted in the session in place of plasma-dialer's modem-daemon, whose own autostart must be off — the package hides it for accounts created after the install (/etc/skel); an existing account hides it itself with a Hidden=true copy of org.kde.modem.daemon.desktop in ~/.config/autostart.

For Phosh and GNOME Mobile, apk add imsd-ofono enables imsd-ofonod and points GNOME Calls at its ofono provider (a gschema override; the per-user equivalent is gsettings set org.gnome.Calls autoload-plugins "['ofono']"). Do not run it alongside a real ofonod. Known gaps — USSD, hold and multiparty, caller-ID withholding, the empty emergency-call screen, and why a restart of imsd-ofonod restarts GNOME Calls — are listed in AGENTS.md §8.3.

Call control is polkit's net.catcrafts.IMS1.Voice: the active local session and root may place, answer and end calls; an ssh login may not.

Building

With the Crafter Build Flatpak, which carries the compiler:

flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists catcrafts https://flatpak.catcrafts.net/catcrafts.flatpakrepo
flatpak install catcrafts net.catcrafts.CrafterBuild//stable
alias crafter-build='flatpak run net.catcrafts.CrafterBuild//stable'
crafter-build          # the daemon; -- --product=media|dialerd|ofonod for the others
crafter-build test     # the seven unit-test suites

Or without crafter-build (distro packaging), with clang/libc++/lld and the gio-2.0 headers:

make && make check

Cross-compiling for a phone, the Makefile's requirements, the package layout and the CI that publishes the apks are in AGENTS.md (§4, §9).

Documentation

AGENTS.md is the complete reference — for coding agents, and for anyone who wants to know how imsd works or change it: the build products and the rule that keeps the engine core free of GLib and I/O, the test suites, the lint rules, the registration and recovery behaviour, the ims PDN bring-up, upgrades, the frozen D-Bus ABI, packaging and release mechanics, the full configuration reference, and the carrier behaviours the code was shaped by.

License

GPL-3.0-only — see LICENSE.

Copyright (C) 2026 Catcrafts® catcrafts.net