Normal setup: Jetson guide, Mac app, PC guide. This page: what the installer does, manual installs, the server command and custom integrations.
- Installs TensorRT if it is missing:
- Jetson: JetPack's
libnvinfer10,libnvonnxparsers10andlibnvinfer-plugin10from its package source (on JetPack 7.2 the newest there, at least 10.16.2.10; a 2.3 GB download). - PC and WSL2: no system package. NVIDIA's TensorRT 11.3.0.99 runtime wheel,
the build the server is compiled against (3.8 GB, checked against its
sha256), unpacked to
/opt/jetlink/tensorrt/11.3.0.99(2.7 GB). The service passes that folder to the server with--tensorrt-libs, and later server versions reuse it. Only the NVIDIA driver comes from the system.
- Jetson: JetPack's
- Installs libcurl (the server needs it),
curlandgitif missing, andunzipon a PC, from the distribution's package manager: apt, dnf, pacman or zypper. A PC with another installs when they are already there. - PC without NVIDIA driver 580: on Ubuntu's family (Mint, Pop!_OS), installs
it with
ubuntu-drivers(nvidia:580-open); on Arch's (EndeavourOS, CachyOS; not Manjaro),nvidia-openfor the running kernel (nvidia-open-ltson the LTS kernel,nvidia-open-dkmsand the headers on another). Elsewhere it prints the distribution's documented steps and stops. Either way the install continues after a restart. - Unpacks the release's server to
/opt/jetlink/<version>, with/opt/jetlink/currentpointing at it andpreviousat the one before, and checks it can use the GPU before it replaces the running one. - Installs the
jetlink-serverservice (runs as root) and its udev rules from the server's tarball, so they always match the binary, and thejetlinkcommand. Settings:/etc/jetlink/server.env(keys); your answers:/etc/jetlink/install.conf. - Serves the web page on port 5600 (a question; 0 turns it
off), behind a password. A first install asks for one; Enter, or a run with
no terminal, has the server make one, shown at the end. Updates keep it, and
jetlink setupasks whether to. - Keeps models and prepared engines in
/mnt/data/jetlinkon a Jetson,/var/lib/jetlinkon a PC. - Jetson only: sets the fastest power mode (MAXN SUPER on an Orin Nano; may
need one restart) and runs
jetson_clocksat every boot (jetlink-clocks.service); adds 8 GB of swap for preparing the 1.7 GB models; stops boot waiting for a network (none in the car; about two minutes); caps the system log at 200 MB; and boots faster. If you answer yes to turning off the desktop (asked when the Jetson starts one), it startsmulti-user.targetinstead ofgraphical.targetfrom the next restart; uninstall, orjetlink setup, puts the desktop back.
An install from 0.6.0 or earlier runs the server in Docker. Its next
jetlink update moves it to the native server, keeping the answers, models,
engines and Jetson setup, and saves the Docker setup in
/etc/jetlink/docker-era. Jetlink's Docker images go once the new server runs;
Docker itself stays.
- Jetson: the host gets the JetPack TensorRT the Docker image had, so its prepared engines load as they are (checked on JetPack 7.2.1). A later TensorRT update from JetPack's package source prepares each model again, once.
- PC: the Docker image had the same TensorRT 11.3.0.99 build. An engine that does not load is prepared again, once, from the downloaded model.
The installer is the supported path. Its pieces, from a release tarball, on any systemd distribution with glibc 2.35 or newer:
-
libcurl (
libcurl4on apt and zypper,libcurlon dnf, incurlon pacman), and TensorRT:-
Jetson:
sudo apt install libnvinfer10 libnvonnxparsers10 libnvinfer-plugin10. -
PC: NVIDIA driver 580 or newer, and TensorRT 11.3.0.99's libraries from NVIDIA's wheel. Its sha256 must equal
PC_TRT_SHA256at the top ofinstall.sh:wheel=tensorrt_cu13_libs-11.3.0.99-py3-none-manylinux_2_28_x86_64.whl curl -fLO "https://pypi.nvidia.com/tensorrt-cu13-libs/$wheel" sha256sum "$wheel" sudo unzip -j "$wheel" 'tensorrt_libs/lib*.so*' '*/LICENSE.txt' -x '*_win_*' \ -d /opt/jetlink/tensorrt/11.3.0.99
-
-
The tarball in
/opt/jetlink/<version>, andcurrentpointing at it:sudo mkdir -p /opt/jetlink/<version> sudo tar -xzf jetlink-server-<version>-linux-<arch>.tar.gz --strip-components 1 -C /opt/jetlink/<version> sudo ln -sfn /opt/jetlink/<version> /opt/jetlink/current
-
/etc/jetlink/server.env, which the service needs, with at least the cache folder, and on a PC the TensorRT folder (keys):sudo mkdir -p /etc/jetlink echo JETLINK_CACHE_DIR=/var/lib/jetlink | sudo tee /etc/jetlink/server.env # a PC only echo 'JETLINK_TENSORRT="--tensorrt-libs /opt/jetlink/tensorrt/11.3.0.99"' | sudo tee -a /etc/jetlink/server.env
-
share/jetlink/systemd/jetlink-server.servicefrom the tarball in/etc/systemd/system, thensudo systemctl daemon-reload && sudo systemctl enable --now jetlink-server. -
Jetson: a oneshot unit, enabled, that runs
/usr/bin/jetson_clockswithAfter=nvpmodel.serviceandWantedBy=multi-user.target. Do not order the server after nvpmodel: that waits for the desktop's login screen. Have the server wait for/dev/nvhost-ctrl-gpuinstead, in anExecStartPre(the installer's is injetlink-server.service.d/20-jetson-gpu.conf). -
Always-on supply only (lets the comma wake the Jetson):
share/jetlink/udev/99-jetlink-usb-wakeup.rulesin/etc/udev/rules.d. -
A password for the web page, or it shows the status only. This makes one and prints it:
sudo /opt/jetlink/current/bin/jetlink-server web-password --file /etc/jetlink/web-auth.json --generate.
The jetlink command needs the installer's files, so a hand install manages
the service with systemctl and reads its log with
journalctl -u jetlink-server.
jetlink-server.service runs
/opt/jetlink/current/bin/jetlink-server --usb --backend trt --web-auth /etc/jetlink/web-auth.json
as root, and starts it again 2 seconds after it exits. Its settings come from
/etc/jetlink/server.env:
| Key | Passed as | The installer sets it to |
|---|---|---|
JETLINK_CACHE_DIR |
--cache |
/mnt/data/jetlink on a Jetson, /var/lib/jetlink on a PC |
JETLINK_SLEEP_AFTER |
--sleep-after (0 when unset) |
120 for Always on with deep sleep, else 0 |
JETLINK_STATUS_PORT |
--status-port (0 when unset) |
the web page's port, 5600 by default |
JETLINK_POWEROFF |
--poweroff, or nothing |
--poweroff on a Jetson whose battery answer was Yes |
JETLINK_TENSORRT |
--tensorrt-libs DIR, or nothing |
the TensorRT folder on a PC; empty on a Jetson |
- The installer also writes
JETLINK_JETSON,JETLINK_FLAVORandJETLINK_SERVER_VERSIONthere, for thejetlinkcommand. - It rewrites the file on every run. Change the answers with
jetlink setup(or one at a time); put anything else, such asJETLINK_USB_LPM=1, in a drop-in (sudo systemctl edit jetlink-server, thenEnvironment=JETLINK_USB_LPM=1under[Service]). - Its own drop-ins wait for the cache's mount and, on a Jetson, for the GPU's
driver.
jetlink-clocks.servicerunsjetson_clocksat boot once nvpmodel has set the power mode; the server does not wait for it.
jetlink run runs the service's command line, with those settings, in the
terminal instead (it stops the service first; Ctrl-C stops it). Extra flags go
on the end: jetlink run --listen for a TCP bench, jetlink run --log-level debug.
jetlink setup --set KEY=VALUE changes the answers it is given and keeps the
rest. It asks nothing, so it runs without a terminal; the web page's settings
use it. It keeps the installed server, its source and TensorRT, so it needs no
network. Then it applies the answers as jetlink setup does and restarts the
server. Give --set once for each answer; install.sh --set is the same.
| Key | Values | Applies to |
|---|---|---|
power |
always or switched |
a Jetson |
comma_poweroff |
yes or no; only with always |
a Jetson |
desktop |
on or off, from the next restart |
a Jetson with a desktop |
autostart |
yes or no |
a PC |
- An answer the install already has changes nothing.
- An unknown key, a bad value, or a key for the other kind of computer stops it before anything changes.
- It needs an install to change, and does not go with
--update,--uninstall,--refor--binary.
The server serves it on JETLINK_STATUS_PORT, for a phone or computer on the
same network as the Jetson or PC: the status, the setup and the models.
- It asks for a password, kept in
/etc/jetlink/web-auth.json(owned by root, mode 0600) as a salted hash, with the key that signs the sign-in cookies. Without the file the page shows the status only. sudo jetlink passwordsets a new one: typed twice, or made with--generateand printed. Every device signed in is signed out. It is also the way back from a forgotten password.- The server writes the file (
jetlink-server web-password) and reads it again whenever it changes, so a new password needs no restart. - It is plain HTTP: the password and the page cross the network unencrypted. Use it on networks you trust, such as the comma's hotspot or your home Wi-Fi.
jetlink uninstallremoves the file with the rest of/etc/jetlink.
jetlink-server is the one server on every platform: the service on a Jetson
or PC, a Mac's server in a terminal (from a terminal),
and the server inside the Mac, iPhone and Android apps.
jetlink-server <command> --help lists each command's options.
| Command | Does |
|---|---|
serve (the default) |
Serves the comma until SIGINT or SIGTERM. |
build ONNX |
Builds this backend's engine for a model before a drive, and loads it once. The next serve on that cache preloads it. |
models ... |
The model catalog and the cache: model command reference. |
web-password --file FILE [--generate] |
Writes the web page's password file, mode 0600: the password from the first line of standard input (8 to 128 characters), or with --generate a new one, printed. Exits 1 with the reason otherwise. |
bench |
Runs a built engine at the comma's pace (20 frames a second) with no comma and no link, and prints its times. |
backends |
Lists the backends and why each can or cannot run here. Exits 0 when one can. |
spec ONNX |
Prints the spec the comma is sent for a model, as JSON (the file scripts/bench_link.py --spec reads). |
build, bench and models prepare open the GPU and the cache on their own:
stop any server using the same cache first.
| Option | Default | Does |
|---|---|---|
--usb |
off | Be the USB host for the comma's gadget (usbfs on Linux, IOKit on a Mac). No TCP listener then, unless --listen too. |
--listen |
on without --usb |
Listen on TCP for bench tools. No authentication: trusted networks only. |
--host ADDR |
0.0.0.0 |
The address to listen on. |
--port N |
5599 |
The TCP port. |
--dial HOST[:PORT] |
none | Also dial this address and serve it, as the phone apps dial the comma (192.168.60.1:5599) over its USB network interface. |
--cache DIR |
see below | Models and prepared engines. |
--backend B |
auto |
auto takes TensorRT where it loads, else ONNX Runtime; trt or ort that cannot run here is an error. The service passes trt, so a machine without TensorRT fails loudly instead of serving from the CPU. |
--device D |
per backend | trt: the CUDA device index (0). ort: ane (default), ane-whole, coreml or cpu on a Mac; cpu on Linux. |
--tensorrt-libs DIR |
lib/tensorrt beside bin/ if it exists, else the loader path |
Where TensorRT's libraries are: a PC's own copy. A Jetson's are JetPack's, on the loader path. |
--gpu-timing |
off | TensorRT: time each launch with CUDA events and log their spread every 1,200 frames. |
--sleep-after S |
0 (never) | Linux, with --usb only: suspend after S seconds with no comma on the bus. |
--status-port P |
0 (off) | Serve the web page on port P. |
--web-auth FILE |
none | The web page's password file. When it is there, the page asks for the password and has the setup and the models; without the flag or the file, it shows the status only. |
--poweroff |
off | Linux: power the machine off when the comma asks (its battery-protection shutdown). Without it the comma is told yes and the machine stays up. |
--no-preload |
off | Do not load the engine loaded last until a comma asks for it. |
--no-keepalive |
off | Mac ONNX Runtime: no GPU keep-alive between frames. |
--no-cpu-keepwarm |
off | Mac ONNX Runtime: no busy CPU core between Neural Engine frames. |
--log-level L |
info |
debug, info, warning or error, on standard error (the service's go to the journal: jetlink logs). |
Exit status: 0 after SIGINT or SIGTERM; 1 when it cannot serve (no usable backend, a bad option, a cache it cannot create); 3 after a CUDA error that only a new process recovers from (the service starts one, which loads the engine again).
build ONNX [--frame-skip N]: N model frames per camera frame, 4 by default. Copies the model into the cache first.bench [--seconds S] [--sha256 SHA] [--frame-skip N]: S up to 3600, 60 by default; the model loaded last unless--sha256names another one built here. The report goes to standard output.backends:--backend trttries TensorRT alone, so it exits 0 only with a usable GPU. The installer runs it before it replaces a server.- All three take
--backend,--device,--tensorrt-libsand--gpu-timing;buildandbenchalso--cacheand--log-level.
The cache is --cache DIR, else $JETLINK_CACHE, else:
| Where | Cache |
|---|---|
| Jetson | /mnt/data/jetlink |
| Linux, as root | /var/lib/jetlink |
| Linux, as a user | ${XDG_CACHE_HOME:-~/.cache}/jetlink |
| Mac | ~/Library/Caches/jetlink |
| Variable | Does |
|---|---|
JETLINK_CACHE |
The cache folder when --cache is not given. |
JETLINK_USB_LPM=1 |
Linux: leaves USB 3 link power management on during a session, for comparing (why it is off). |
JETLINK_FAULT_CUDA_AFTER=N |
Tests only: every frame after the first N fails as a CUDA error the process cannot recover from, to exercise the exit and restart. |
- comma 3X (AGNOS kernel 4.9.103) has FunctionFS and USB gadget support.
- The server is always the USB host, through usbfs (IOKit on a Mac): no driver and no gadget kernel modules on the host.
- On Linux the server turns off USB 3 link power management (U1/U2) on the
comma's port while it serves the comma, and puts the kernel's default back
when the session ends, when the comma has sent nothing for 30 s (parked with
its gadget still up; off again at its next message), or when the server
stops. On, waking the link cost 2.2 ms a frame on the bench Jetson (4.2
against 2.0 ms of transport, p50); off, the idle link drew 0.18 W more, so it
is not off for the whole park. Deep sleep and USB wake are unaffected.
JETLINK_USB_LPM=1leaves it on. - How the link carries a frame: link protocol.
- Nothing on the comma runs by hand. The owner builds the gadget on its first step, USB or iOS per the comma's Jetlink setting, and rebuilds it when the setting changes with the car off (the setting is locked while driving).
scripts/comma/jetlink-root.sh is every root action Jetlink takes on the comma
(comma four and 3X); the owner runs it under sudo -n.
| Subcommand | Does |
|---|---|
gadget |
Creates the gadget configuration: the vendor interface alone. The owner then opens ep0, writes the FunctionFS descriptors and binds the USB device controller (binding needs the descriptors first). |
gadget --ios |
Jetlink iOS: composite, adds a network interface for an iPhone. |
net |
Run by the owner after each iOS bind (the interface exists only from the first bind). |
port hold, port off |
Keeps the USB-C port the device end of a USB link. |
port device, port reset |
Asks the far end over USB PD to take the host role (a data role swap), or resets USB PD. |
udc apply, udc restore |
While the link is on, keeps the USB device side on whenever a host powers the port; restore is stock. udc start, udc stop turn it on or off now. |
vm apply, vm restore |
Sets and undoes the VM tuning the link needs while the comma records. |
check |
By hand, sudo scripts/comma/jetlink-root.sh check: what is built and the negotiated bus speed. |
teardown |
Removes the gadget. |
The owner holds the gadget while the link is on; openpilot lists it as
jetlinkd. Standard library only, about 10 MB. Code in jetlink/comma/:
| File | Role |
|---|---|
gadget.py |
the gadget, and what carries the link |
owner.py |
the owner |
lending.py |
the lease modeld borrows the endpoints, or a phone's dial, on |
port.py |
the USB-C port |
root.py |
runs jetlink-root.sh under sudo -n, on AGNOS only |
The zoompilot fork's manager runs it as its adapter module,
openpilot/sunnypilot/jetlink_adapter, whose main() hands
jetlink.openpilot.owner the params directory and keys, the chestnut's USB
ids and the adapter's name; the owner starts python -m jetlink.openpilot.provision --adapter openpilot.sunnypilot.jetlink_adapter
when there is provisioning to do.
| Descriptor | Value |
|---|---|
| idVendor:idProduct | 1209:0001 (pid.codes test allocation) |
| bcdDevice | 0x0100; 0x0101 for iOS, so hosts refetch cached descriptors |
| bDeviceClass/SubClass/Protocol | 0x00/0x00/0x00, class per interface; for iOS 0xEF/0x02/0x01, Miscellaneous with interface association (composite) |
| Interface 0 | 0xFF/0xFF/0xFF vendor specific, one bulk IN and one bulk OUT endpoint: the Jetlink link |
| Interfaces 1 and 2 (iOS only) | CDC-NCM control and data: the network for an iPhone |
| Network (iOS only) | comma 192.168.60.1/24; DHCP 192.168.60.2 to .254 with 10 minute leases from dnsmasq on the gadget's interface (usb1 or later, since the modem holds usb0), no router or DNS options |
Custom distributions need their own USB product ID.
- The installer installs the newest release;
jetlink updatemoves to the next.jetlink update --ref mainswitches to development builds (theedgeprerelease). - The zoompilot fork pins the Jetlink it was tested with as its
jetlink_reposubmodule:develop,danger-unstableandjetson-trtpin a commit onmain, which may lie between releases. - The comma and the server have to speak the same protocol version; a release that changes it says so, and across it the two update together. A comma on the older protocol stays on its small model.
To pin a release, pass it to the installer (replace v0.7.0);
jetlink update --ref latest follows releases again:
curl -fsSL https://raw.githubusercontent.com/zoompilot/jetlink/v0.7.0/install.sh | bash -s -- --ref v0.7.0-
Back one update from 0.7.0 or later: point
currentatpreviousand restart.jetlink updatemoves forward again.sudo ln -sfn "$(readlink -f /opt/jetlink/previous)" /opt/jetlink/current jetlink restart -
Back to 0.6.0, the last Docker release:
jetlink update --ref v0.6.0. Its own installer takes over with the same answers. 0.6.0'sjetlink updatecannot install a native release, so to come forward again run:curl -fsSL https://raw.githubusercontent.com/zoompilot/jetlink/main/install.sh | bash -s -- --update --ref latest
A Jetson on Switched power boots at every start of the car, and the big model drives only once the server is up. On JetPack 6.2 and 7.2 the installer makes three changes that took a reboot to the model loaded from 38.5 s to 26.1 s (Orin Nano, JetPack 7.2.1):
- The firmware's boot menu waits 1 s instead of JetPack's 5
(
efibootmgr -t 1; the installer addsefibootmgrif it is missing). A wait of 1 s or less stays as it is. To reach the firmware menu, give it the 5 s back first:sudo efibootmgr -t 5. quieton the kernel command line, at the end of theAPPENDline of the entry that boots in/boot/extlinux/extlinux.conf. Kernel messages still go to the journal anddmesg, just not to the serial console, which runs at 115200 baud. The file as it was stays asextlinux.conf.jetlink-bak. If a JetPack update rewrites the file, the nextjetlink updateaddsquietagain.- The server starts once the GPU's driver is up, not after nvpmodel,
which waits for the desktop's login screen. A drop-in
(
20-jetson-gpu.conf) waits up to 30 s for the GPU's control node, since CUDA fails for good in a process that starts before it.jetlink-clocks.servicepins the clocks once nvpmodel has set the power mode.
jetlink uninstall puts the firmware's wait and extlinux.conf back as they
were, unless you changed them since.
Always on in the installer installs a udev rule that lets the USB hubs wake
the Jetson, sets --sleep-after 120, and checks for deep in
/sys/power/mem_sleep. The server arms the hubs again before every suspend.
- Ignition off: the comma releases USB once the engine is ready and at least one minute has passed.
- The Jetson sleeps after 120 s without a USB connection. USB connect or disconnect wakes it; with no new connection it sleeps again after 120 s.
- A 30-minute RTC alarm also wakes it, in case a USB wake failed. With no comma it sleeps again after 15 s.
jetlink caffeinatekeeps an awake Jetson awake, like the Mac'scaffeinate: until Ctrl-C, for-t SECONDS, or whileCOMMANDruns. No sudo needed. Updates hold it awake on their own.- Failed sleep retries after 10 s, doubling up to 5 minutes. Check the logs and USB wake on the root and onboard hubs.
- With
--sleep-after 0, the link stays up while the comma is awake.
- The comma requests shutdown at 11.8 V or after 30 hours parked.
- The Jetson powers off (
systemctl poweroff) only if you answered Yes to the installer's battery question: it then writesJETLINK_POWEROFF=--poweroffin/etc/jetlink/server.env. Otherwise it stays up; so does a PC.jetlink setupchanges the answer. - Restart after full shutdown needs hardware that cycles DC power or triggers the J14 power-button input; the devkit boots when DC power returns.