Skip to content

Dependencies

Policy (ADR-0011): shipped artifacts may depend on permissive or LGPL (dynamically linked) components only — no GPL. Dev-only tools are unrestricted. Adding a dependency requires adding its row here in the same PR. The devcontainer Dockerfile mirrors these groups with comments.

DependencyVersion (Ubuntu 26.04 LTS, ADR-0022; re-verified in slice 2.9)PurposeLicenseShips
GStreamer core + base/good/bad plugins1.28.2pipelines, RTP, WebRTC; gstreamer1.0-plugins-base-apps for gst-device-monitor-1.0, which fjarr-agent setup and drivers detect run to find cameras (docs/26), a package dependency since 2026-09-28LGPL-2.1 (dynamic)yes
libx265 (pulled by Ubuntu’s gstreamer1.0-plugins-bad for its x265enc)26.04nothing: present on disk, never loaded by the agent. x265enc is an encoder, encoders are never autoplugged, and the agent’s encoder is explicit (VA-API, openh264). Recorded 2026-09-28, when the image’s GPL guard was sharpenedGPL-2.0present, not a dependency: the rule (ADR-0011) is about what enters the agent’s process. The image guard fails on x264enc, avdec_*, libx264 and libxvidcore instead, which are what decodebin or a GPL encoder would load
gstreamer1.0-nice (libnice)0.1.23ICE for webrtcbin; the agent also links libnice directly (libnice-dev at build) to give it the local addresses to gather from, without the tunnel interface (docs/23, #34)LGPL-2.1/MPLyes
gstreamer1.0-alsa1.28.2alsasrc: fjarr.audio reads a robot’s microphones through ALSA, as a system service with no desktop session (docs/23#audio, M3.5); Opus itself is gstreamer1.0-plugins-base’s opusenc, over libopus (BSD-3-Clause)LGPL-2.1 (dynamic)yes
gstreamer1.0-pipewire1.6.2Wayland capture (pipewiresrc)MITyes
libva + intel-media-driver (iHD; amd64 only — arm64 uses mesa’s VA drivers)2.22 / 26.1VA-API H.264 encodeMITyes (driver from distro)
libx11 / libxtst / libxfixes / libxrandr / libxi26.04X11 backendMIT/X11yes — in fjarr-desktop-x11 only (ADR-0021)
libei1.5.0Wayland input injectionMITyes — in fjarr-desktop-wayland only
libxkbcommon1.13.1keymap lookup for typing text through the robot’s layout (docs/23)MITyes — in fjarr-desktop-wayland only
libpipewire1.6.2capture: backend module E reads each capture’s stream itself, frames and the cursor’s metadata (slice 3.5, docs/23); fjarr-desktop-session narrows each handed connection to its capture’s node (docs/23). gstreamer1.0-pipewire is no longer used by the agent. The modules load their own client configuration (/usr/share/fjarr/pipewire-client.conf, in fjarr-agent so both desktop packages can share it) with libpipewire-0.3-modules’ client modules, not pipewire-bin’s client.conf: that package brings GPL-3 readline (2026-10-06). The desktop’s sound (M3.5 3.5.2): both modules read the speakers through it, and fjarr-x11-session narrows the X11 kiosk’s connection as the GNOME helper does (ADR-0034)MITyes — in fjarr-desktop-wayland, fjarr-desktop-session and fjarr-desktop-x11
libevdev1.13.6uinput helperMIT-ish (X11)yes (in fjarr-inputd)
libdbus / sd-bus26.04portal negotiationAFL-2.1/GPL dual → use LGPL path; verify at M2yes — in fjarr-desktop-wayland only
nlohmann-json3.11envelopes, configMITyes
GStreamer nvcodec plugin (nvh264enc, plugins-bad)1.28the nvcodec encoder family for discrete NVIDIA cards (ADR-0025, M2.6)LGPL-2.1 (dynamic)yes (optional)
NVIDIA driver libraries (libnvidia-encode, libcuda)the host’s driverwhat nvcodec loads at runtime; never linked or redistributed by us — the machine’s own driver install provides them, exactly as the Intel VA driver isproprietary, not shippedno
NVIDIA L4T GStreamer (nvv4l2h264enc)JetPackthe nvv4l2 encoder family on Jetson (M2.6); from NVIDIA’s own repository on the board, arm64 onlyNVIDIA SDK terms, not shippedno
openh264 (openh264enc, plugins-bad)2.4the explicit encoder = "software" path (CI, no-GPU dev, portable robots) — never a silent fallback (docs/23)BSD-2yes (optional)
libsoup-3 (+ glib-networking)3.6WS/HTTP signaling client (ADR-0017) and the introspection server (docs/24)LGPL-2.1 (dynamic)yes
toml++ (tomlplusplus)3.4 (header-only)fjarr.toml config (docs/23)MITyes
nlohmann json-schema-validator2.3 (no Ubuntu package: pinned via CMake FetchContent, built into libfjarr)capability config validation against config_schemaMITyes
GStreamer gstreamer.supp (valgrind suppressions, vendored at agent/tests/valgrind/)1.28.2 source treemake agent-memcheck (docs/23 memory ladder, nightly)LGPL-2.1-or-later (a data file; never linked)no — dev only
python3-gi + gir1.2-gst-rtsp-server-1.0 + gstreamer1.0-rtsp26.04the lab’s RTSP camera simulator (docker/lab/rtsp-sim.py, the rtsp-sim compose service) for fjarr.camera’s rtsp trackLGPL-2.1 (PyGObject, gst-rtsp-server)no — dev image and CI only
gstreamer1.0-libav (avdec_h264)1.28receive-side H.264 decode in fjarr-opsim (docs/23) — the dev image and CI only. It had crept into the shipped agent image, where decodebin would autoplug it for RTSP; removed 2026-09-28, and the image guard now fails on itLGPL-2.1 (Ubuntu’s ffmpeg build enables GPL parts)no — a test tool; never linked into libfjarr, fjarr-agent or a demo
libsystemd (sd_notify)26.04READY/WATCHDOG supervision — required by the packaged agent (ADR-0019 addendum)LGPL-2.1 (dynamic)yes
GoogleTest1.17C++ unit/loop testsBSD-3dev-only
mutter, PipeWire, WirePlumber, GTK 4, libei (Ubuntu 26.04 packages)50.1 / 1.6 / 0.5 / 4.22 / 1.5the desktop fixture’s headless GNOME session (docker/desktop-fixture, M3 slice 3.0): the compositor under test, its screencast plumbing, the test window, EISGPL-2.0+ (mutter), MIT (PipeWire, WirePlumber, libei), LGPL-2.1+ (GTK)no — a test image only; never in a shipped artifact
wl-clipboard (Ubuntu 26.04 package)2.2the desktop fixture’s robot side of the clipboard tests (M3 slice 3.5): wl-copy copies on the robot, wl-paste pastes there, behind the fixture’s oracleGPL-3.0no — a test image only; never in a shipped artifact
python3-xlib, x11-xserver-utils (xrandr, xhost), xauth (Ubuntu 26.04 packages)0.33 / 7.7 / 1.1the X11 fixture’s robot side (robot-sim with SIM_FIXTURE=1, M3 slice 3.7): the test window that logs input, carving and removing RandR monitors, the xhost grant, an enforced access controlLGPL-2.1+ (python-xlib), MIT (the X utilities)no — a test image only; never in a shipped artifact
PipeWire, WirePlumber, dbus (Ubuntu 26.04 packages, in robot-sim)1.6 / 0.5 / 1.16the X11 fixture’s kiosk sound (M3.5 slice 3.5.2, ADR-0034): the kiosk account’s session bus and sound server, a null sink as its speakers, pw-play as the application that playsMIT (PipeWire, WirePlumber), GPL-2.0+ or AFL-2.1 (dbus)no — a test image only; never in a shipped artifact
xclip (Ubuntu 26.04 package)0.13the X11 fixture’s robot side of the clipboard tests (M3 slice 3.7c): copies text and a PNG on the robot and pastes them there, through plugd_x11.pyGPL-2.0no — a test image only; never in a shipped artifact
FFmpeg dev packages, with libx264 and libxvidcore (pulled by Ubuntu’s libgstreamer-plugins-base1.0-dev and -bad1.0-dev)26.04nothing of Fjarr’s: they arrive on any machine that builds against GStreamer, so on an embedder’s build machine with libfjarr-dev. libfjarr links GStreamer, never FFmpeg, and the robot runtime packages (fjarr-agent, fjarr-tools) do not bring them (make deb-install-test checks)GPL-2.0no — build machine only
debhelper, devscripts, equivs, fakeroot (the package builder, docker/deb-builder)26.04building the .debs (docs/26#releases, ADR-0031)GPL-2.0+ (build tools; nothing of theirs is in a package)no — build only
Vendor camera SDKs (ZED, RealSense, Jetson multimedia, GigE vendors…)per vendornever a libfjarr dependency — each ships as its own GStreamer plugin package fjarr-gst-<vendor> per architecture (ADR-0020)per vendor (checked per package)optional, separate packages
Forbidden: gstreamer1.0-plugins-ugly (x264enc)——GPLnever (doctor-enforced)

Setup tool (fjarr-setup) — ships in fjarr-agent

Section titled “Setup tool (fjarr-setup) — ships in fjarr-agent”

The installer commands fjarr-agent hands over (docs/26): a Rust binary at /usr/lib/fjarr/fjarr-setup, no GStreamer, nothing of it in the agent’s process.

DependencyVersionPurposeLicenseShips
cliclack0.5the prompts (every one has a flag); the one prompt library once fjarr-connect moves off dialoguerMITyes
toml_edit0.22/etc/fjarr/fjarr.toml edited in place, comments and order kept — it is the customer’s fileMIT/Apache-2.0yes
drm0.15fjarr-agent display: the DRM connectors, their state, and the PATH property that says a connector hangs off a DisplayPort chain, so a chain’s root, which reads as disconnected, is never taken for a free connector (docs/26)MITyes
clap, anyhow, libc, serde, serde_json, tokioas the operator clientthe CLI, errors, getpwnam/getuid/isatty, the change record, the netlink runtimeMIT/Apache-2.0yes
fjarr-netdev (rtnetlink)in-treethe same device code fjarr-connect uses: the persistent tun, its address, the routing table for the range checkMIT (rtnetlink)yes

Signaling (fjarr-signaling / fjarr-server) — ships as sidecar/Cloud

Section titled “Signaling (fjarr-signaling / fjarr-server) — ships as sidecar/Cloud”
DependencyVersionPurposeLicenseShips
Rust toolchain1.98.0 (pinned; bumped from 1.89.0 in slice 4.5e because webrtc-rs 0.21 uses Ipv4Addr::from_octets, stabilised after 1.89)buildMIT/Apache-2.0build-only
tokio1.xasync runtimeMITyes
axum (+ ws)0.8HTTP + WebSocketMITyes
tracing / tracing-subscriber0.1/0.3structured logsMITyes
serde / serde_json1.xenvelopesMIT/Apache-2.0yes
futures-util0.3WS stream/sink combinatorsMIT/Apache-2.0yes
jsonwebtoken9session-grant verification (HS256 at M1)MITyes
hmac + sha1 + sha20.12/0.10TURN ephemeral creds (SHA1, coturn format), webhook signatures (SHA256)MIT/Apache-2.0yes
base640.22TURN credential encodingMIT/Apache-2.0yes
subtle2constant-time token comparisonBSD-3yes
uuid (v7)1.xevent/session idsMIT/Apache-2.0yes

Operator client (fjarr-connect) — ships in fjarr-tools (planned, M4.5)

Section titled “Operator client (fjarr-connect) — ships in fjarr-tools (planned, M4.5)”

Versions are pinned when the binary is implemented; these rows exist so the licence question is settled before the code is written (ADR-0024). The robot side of the tunnel adds no dependency at all — a TUN device is the kernel plus two ioctls.

DependencyVersionPurposeLicenseShips
webrtc-rspinned at implementationpeer connection + data channels, no mediaMIT/Apache-2.0yes
tokio1.xasync runtimeMITyes
libc0.2the virtual interface, with no wrapper crate: TUNSETIFF plus one unbuffered read and write per packet is three calls, and a TUN crate would bring its own I/O model to sit beside tokio’s. Interface, address and route setup shells out to ip, which is on every host that has a TUN device (slice 4.5e; the macOS utun path is the same three calls against a different socket family)MIT/Apache-2.0yes
rtnetlink (with netlink-packet-core / -route / -utils, netlink-proto, netlink-sys, nix)0.23the interface’s address, MTU and per-robot routes, in process. A file capability is not inherited by a child, so shelling out to ip cannot work under the setcap install docs/27 promises — and speaking netlink here also means the binary needs no iproute2 on the host (slice 4.5e)MITyes
clap4.xthe CLI surfaceMIT/Apache-2.0yes
getrandom0.3the 128-bit state of the loopback login (docs/27#logging-in), from the OS on every platform; it replaced a Linux-only libc::getrandom so the binary builds where the shell runs. Already in the tree under randMIT/Apache-2.0yes
windows-sys0.61Windows only: the console modes and size behind fjarr-connect shell’s raw mode (docs/27#shell); on Unix that is termios through libc. Already in the tree under tokioMIT/Apache-2.0yes
dialoguer0.11the robot picker — type to filter, enter to connect (docs/27#what-it-feels-like) — rather than a hand-rolled terminal UI (slice 4.5f). To be replaced by cliclack, the setup tool’s prompt library, once its filter mode is confirmed to serve the picker (docs/26#the-setup-tool)MITyes
toml, dirs0.8, 6~/.config/fjarr/config.toml and the 0600 credential cache beside it (slice 4.5f)MIT/Apache-2.0yes
reqwest (rustls)0.12the operator API calls — robots, grants, login codes (docs/09#operator-api); already in the tree for the server’s webhooksMIT/Apache-2.0yes
fjarr-protocol (this repo)workspaceshared signaling message types — the reason wire drift is a compile error. Its own crate since slice 4.5e, so an operator client links the types without the server’s axum/hyper/HMAC stack (ADR-0024)AGPL-3.0yes
reqwest (rustls)0.12webhook deliveryMIT/Apache-2.0yes
tokio-tungstenite0.24the operator’s signaling socket (fjarr-connect, slice 4.5e), wss:// through its rustls-tls-webpki-roots feature: the rustls (ring) and webpki-roots reqwest already brings; also the e2e test WS clientMITyes (it was dev-only until fjarr-connect shipped one)
futures-util0.3stream/sink combinators on that socketMIT/Apache-2.0yes
anyhow1.xerror context in the operator client, where a failure is a message to a developer rather than a value to match onMIT/Apache-2.0yes
tracing, tracing-subscriber0.1 / 0.3the operator client’s loggingMITyes

Web (@fjarr/core, @fjarr/react) — ships to customer bundles

Section titled “Web (@fjarr/core, @fjarr/react) — ships to customer bundles”
DependencyVersionPurposeLicenseShips
TypeScript^5.9buildApache-2.0build-only
React (peer dep)≥ 19@fjarr/react onlyMITpeer
(M2+) xterm.js—terminal viewMITyes
@fjarr/core runtime depsnone (design goal)keep the core dependency-free——
vitest^5unit tests (core, react)MITdev-only
happy-dom^20DOM for @fjarr/react hook testsMITdev-only
@testing-library/react^16hook/component testsMITdev-only
@xterm/xterm + @xterm/addon-fit^5.5 / ^0.10the terminal renderer in <TerminalView> (docs/06 fjarr.terminal)MITyes (@fjarr/react/terminal entry with optional peer dependencies, imported only when the component mounts — a dashboard with no terminal pays nothing)
d3-graphviz (+ its d3-selection, d3-transition peers) + @hpcc-js/wasm^5 (^3) / ^2DOT rendering in <PipelineGraph> and the on-robot viewer (docs/24)BSD-3 (ISC) / Apache-2.0yes (@fjarr/react/pipelines entry with optional peer dependencies; the viewer app’s static files served from introspect.viewer_dir, docs/24)
@playwright/test (+ pinned Chromium image)1.63.0 (pinned)browser lab harness and e2e (docs/25; rows below)Apache-2.0dev-only
Vite + @vitejs/plugin-react^8 / ^5serves the lab page (web/e2e/app)MITdev-only
DependencyVersionPurpose
Vite^8demo-dashboard dev/build
Astro + Starlight^7 / ^0.42website + docs rendering
pnpm (via corepack)10.xJS workspace
markdownlint-cli2, lycheelatestdocs gates
ajv^8protocol schema conformance gate (make protocol-check)
CMake/Ninja/ccache, clang-21 suite26.04C++ build/lint
valgrind, heaptrack3.26 / 1.5nightly memcheck and allocation profiling of the agent (docs/15 memory safety; GPL tools, dev-only, never linked)
@playwright/test (@fjarr/e2e, never published)1.63.0 (pinned; the browser image tag follows it)browser lab harness and fjarr-lab (Apache-2.0)
mcr.microsoft.com/playwright:v1.63.0-noble (container)1.63.0the lab’s headless Chromium with CDP (docs/25)
openssh-client26.04the operator end of the tunnel lab: make tunnel-ssh / tunnel-scp run through it over fjarr0 (docs/27 gate, slice 4.5c). BSD-2/ISC, dev image only — nothing in a shipped artifact links or invokes it
openssh-server (fjarr-robot-services container)26.04the ssh daemon the tunnel gate reaches, in a sidecar on the robot’s network namespace. BSD-2/ISC, dev-only fixture: on a real robot this is the integrator’s own package and the agent never knows about it
docker-cli, docker-compose-v2 (dev image)26.04docker-outside-of-docker: the lab drives the compose stack from dev (Apache-2.0)
iproute2 (tc, dev image)26.04netem media-path profiles inside the robot container (GPL-2.0 tool, dev-only, never linked)
coturn (container)4.6dev/self-host TURN (BSD-3)
mkosi, systemd-ukify, systemd-repart, systemd-boot-efi, OVMF, qemu-system-x86 (docker/ota-build)Ubuntu 26.04 (mkosi 26, systemd 259)building the x86 test images and rehearsing them in a VM (M8 slice 8.0, docs/32). LGPL-2.1+ (mkosi, systemd), BSD-2 (OVMF), GPL-2.0 (QEMU, a tool); what the images contain is the recipe’s own list
SWUpdate, EFI Boot Guard (efibootguard, its 0.22 loader pinned by hash)2025.12 / 0.19 tools (Ubuntu 26.04), 0.22 loaderthe x86 Ubuntu A/B recipe’s slot writer and boot loader (ADR-0040). SWUpdate 2025.12 is also the Jetson Yocto test image’s slot writer (docs/32), built by meta-swupdate with the spike layer’s defconfig (-B none, CMS signatures, raw handler, zstd; no Lua, web server or downloader). GPL-2.0-only, libebgenv included: run as tools by the applier, never linked into Fjarr; they are in images built from the recipes, not in Fjarr’s packages
desync, xdelta3, bsdiff, zchunk, diffoscope (spikes/ota-delta)1.1.4 (upstream binary, checksum verified) / 26.04the 8.0 delta harness, chunks against binary diffs. BSD-3 (desync, bsdiff, zchunk), GPL-2.0+ (Ubuntu’s xdelta3), GPL-3.0+ (diffoscope); harness only — a diff decoder on robots would be upstream xdelta 3.1+ (Apache-2.0) or zstd (BSD)
kas, kas-container (image ghcr.io/siemens/kas/kas:5.5, the script pinned by SHA-256)5.5builds the Jetson Yocto test image (M8 slice 8.0, docs/32, spikes/ota-jetson/yocto). MIT; a build tool, never shipped
Yocto layers: poky (OE-core, bitbake), meta-openembedded (meta-oe), meta-tegra (OE4T), meta-swupdatescarthgap: poky 3a3d07f, meta-oe 0f00f8b, meta-tegra 6d7cc8f (L4T 36.5.2), meta-swupdate 25c9be8the Jetson Yocto recipe’s base (Q1, Q2). Metadata MIT (OE-core, meta-oe, meta-tegra, meta-swupdate), bitbake GPL-2.0; what an image contains carries each recipe’s own licence (kernel GPL-2.0, systemd LGPL-2.1+, SWUpdate GPL-2.0). Recipes, not images: Fjarr ships no image
NVIDIA Jetson Linux (L4T) BSP and public sources: flash, backup and BCT tools (l4t_initrd_flash.sh, l4t_backup_restore.sh, tegrabct_v2), nvbootctrl, edk2-nvidia UEFI and L4TLauncher, the NVMe A/B partition XML36.4.4 and 36.5.2 (SHA-256 in spikes/ota-jetson/board/checks.lock)flashing, backing up and building the ZED Box Mini’s firmware; nvbootctrl is run by the Jetson applier. NVIDIA proprietary (tools, MB2 common file, partition XML), BSD-3-Clause (MB1 pinmux/GPIO files), BSD-2-Clause-Patent (edk2-nvidia), GPL-2.0 (kernel). Fetched by hash and never redistributed: the layer carries only Fjarr’s changes
Stereolabs stereolabs-zedbox-mini driver package, ZED Box Mini device-tree tarballs (36.4.4, 36.5)1.4.4 for L4T 36.5.2 (SHA-256 in spikes/ota-jetson/board/stereolabs.lock, checks.lock)the board package’s reference and, later, the camera drivers (docs/18 #45, #47). Stereolabs’ terms (no licence stated); fetched by hash at build time, maintainer scripts never run, never redistributed
Fjarr’s timer-tegra186 watchdog backport (a patch in spikes/ota-jetson/yocto)for NVIDIA’s linux-jammy l4t-r36.5.2 (e6b364044e43)the Yocto image’s kernel, compared in D2b. GPL-2.0-only, like the file it changes; applied by the recipe to a kernel Fjarr does not ship
Board check tools image (spikes/ota-jetson/board/Dockerfile: device-tree-compiler, cpp, patch, dpkg, python3, shellcheck)Ubuntu 26.04the board package’s checks (BCT and device-tree diffs, the driver pin). GPL-2.0+/BSD-2 (dtc), GPL-3.0+ (cpp, patch, dpkg, shellcheck), PSF (Python); tools only
L4T flash container fjarr-l4t-flash (ubuntu:22.04 by digest with L4T’s l4t_flash_prerequisites.sh packages: qemu-user-static, nfs-kernel-server, sshpass, abootimg, xmlstarlet, device-tree-compiler, …) and its minimal rootfs (63 Ubuntu 22.04 arm64 packages, flash/rootfs-debs.lock)22.04running NVIDIA’s flash, backup and restore tools from the lab mini-PC (Q6, phase C). GPL-2.0 (QEMU, nfs-utils, sshpass, abootimg), MIT (xmlstarlet), mixed (the rootfs packages); lab only, never shipped
nfs-kernel-server, rpcbind, nfs-common on the lab mini-PC (spikes/ota-jetson/flash/host-setup.sh, undone by --undo)Ubuntu 26.04the box’s flash initrd mounts the L4T tree over NFS. GPL-2.0; a lab host change, never shipped
device-tree-compiler, bmaptool on the lab mini-PC (spikes/ota-jetson/flash/host-setup.sh, undone by --undo)Ubuntu 26.04meta-tegra’s initrd-flash, run natively for D1 (it mounts the box’s disks through the host’s udisks): its tegraflash calls dtc, its make-sdcard writes the slot images with bmaptool. GPL-2.0+/BSD-2 (dtc), GPL-2.0 (bmaptool); a lab host change, never shipped
Jetson network rescue: NVIDIA’s L4T kernel and modules, systemd-stub and ukify, BusyBox, OpenSSH, gdisk, e2fsprogs, nvme-cli, efibootmgr, dosfstools, util-linux, zstd; dnsmasq on the mini-PC; QEMU and AAVMF for the bench5.15.148-tegra, 259.5, 1.30.1, 8.9p1 (Ubuntu 22.04 arm64); dnsmasq 2.92; QEMU 10.2.1, AAVMF 2025.11 (the full list in spikes/ota-jetson/rescue/README.md, Licences)the lab’s network rescue UKI and its off-box bench (docs/32, “Three rescue layers”). GPL-2.0 (kernel, BusyBox, gdisk, e2fsprogs, nvme-cli, efibootmgr, QEMU), GPL-2.0/3.0 (dnsmasq), GPL-3.0+ (dosfstools), LGPL-2.1+ (systemd-stub, ukify), BSD (OpenSSH, zstd), BSD-2-Clause-Patent (AAVMF); lab only, never shipped
shellcheck (koalaman/shellcheck:stable container; Ubuntu’s in the spike’s tool images)0.11.0lint for the spikes’ shell scripts. GPL-3.0-or-later; a check, never shipped
Lab script host tools: curl, jq, GnuPG (gpg, AES-256 for the backup’s second copy), rsync, util-linux flock, python3, OpenSSH client; nmap optional (probe-host.sh)Ubuntu 26.04spikes/ota-jetson lab scripts on the workstation and the mini-PC (the lock, the plug’s RPC, the backup and its encrypted copy). curl (MIT-style), jq (MIT), GPL-3.0+ (GnuPG, rsync), GPL-2.0 (util-linux), PSF (Python), BSD (OpenSSH), NPSL (nmap); tools only
Xvfb/openbox/x11vnc/noVNC (robot-sim)26.04fake robot desktop
Docker + Compose v2≥ 24the environment itself

Toolchains are pinned (rust-toolchain.toml, packageManager field, Dockerfile base images by tag). Distro libraries float within Ubuntu 26.04 LTS. Cargo.lock and pnpm-lock.yaml are committed. Upgrades are ordinary PRs with a dependencies-row diff; base-image bumps get a changelog note.