Open Questions
Living document — add freely, close by linking the ADR/spec section that answered. Nothing here blocks the current milestone unless marked ⚠.
| # | Question | Surfaced | Closes via | Notes |
|---|---|---|---|---|
| 1 | Is fjarr.com worth acquiring (currently registered)? | M0 naming | business call | fjarr.io is primary; revisit if traction |
| 2 | nvcodec in M2.6 (hardware verified working in our dev image), nvv4l2 when a board exists; each family owes the nightly a runner or is published untested | camera-streamer heritage | roadmap insert post-M6 | design partner demand decides. Hardware now exists (2026-09-24): the self-hosted runner is an RTX 2080 Ti, and libgstnvcodec.so is already in the dev image, so its elements register wherever the NVIDIA driver is. Still three things away from usable: the NVIDIA Container Toolkit (we map /dev/dri, which is not how NVIDIA reaches a container), an EncoderAdapter and a media.encoder = "nvenc" value (docs/23 knows only auto|vaapi|software), and a docs/14 row. Note a desktop card’s NVENC is not a robot’s path either — Jetson encodes through nvv4l2h264enc, not nvcodec — so this box would develop the adapter, not validate a budget |
| 3 | params.listen); talking needs params.talk | vision | docs/06 fjarr.audio | closed |
| 4 | Session recording/replay (audit + support)? | docs/10 | capability spec | storage + consent design needed |
| 5 | MQTT signaling adapter — real demand? | camera-streamer seam | ADR if requested | seam exists; don’t build speculatively |
| 6 | ADR-0016 (superseded) | — | — | |
| 7 | Metrics retention policy + storage engine (M7) | observability | M7 spec revision | Cloud cost model input |
| 8 | M8 | — | — | |
| 9 | TPM-backed device keys — require or recommend? | docs/10 | M5 security review | NUC TPM2 present; portability vs strength. 2026-10-15: the lab NUC’s TPM went into dictionary-attack lockout (TPM_RC_LOCKOUT, 0x921) during 60 power cuts (docs/32); a TPM-held key must survive power cuts, so the answer also covers lockout settings and recovery |
| 10 | TURNS / TCP-443 TURN fallback priority | docs/04 | M5 | hospital/enterprise networks |
| 11 | Separate PeerConnection for bulk transfers? | docs/16 | M4 measurement | only if isolation budget fails |
| 12 | @fjarr/core for non-React frameworks (Vue/Svelte wrappers)? | delivery model | demand-driven | core is framework-agnostic by design |
| 13 | Widened 2026-10-15: the licence and the business model are under reconsideration, with the repository going private meanwhile (docs/17 M0.6); legal review of the chosen licence; a CLA only if dual licensing stays | ADR-0011 | before anything is published to npm or crates.io, and before the first external contribution | nothing new is published until it is answered; a superseding ADR records the decision |
| 14 | Answered 2026-09-28: main as the development version plus a frozen /vX.Y/ snapshot per release (docs/19). Docs site versioning scheme (per release vs latest+next) | docs/19 | M1 release process | Starlight supports versions via plugin |
| 15 | @fjarr/core TS types to the golden fixturesweb/packages/core/test/fixtures.test.ts replays every fixture through the runtime guards | slice-1 review | docs/21 testing | all three implementations are now conformance-checked against protocol/fixtures |
| 16 | Point clouds / depth: stream class (ADR-0018) vs depth-as-video per sensor type? | docs/21 review | first sensor-streaming capability (M4+) | decision matrix in docs/06; the spike runs in M4; the ADR-0018 chunker is built only when a capability picks the stream class |
| 17 | talk grant: a separate open-mic grant was complication for no value. The agent plays the uplink only between the audited start and stop | fjarr.audio planning | docs/06 fjarr.audio | closed |
| 18 | fjarr.logs milestone | docs/06 | roadmap | — |
| 21 | In-place ICE restart on the agent — source-verified absent in webrtcbin 1.24, 1.26 and 1.28 (ADR-0022); remaining question: contribute it upstream (offer option + nice_agent_restart), or check whether webrtcsink does it | spike, docs/23 | ADR-0007 spike (slice 6) | rung 2 stays the session-close{retry:true} fallback: a media restart instead of an in-place restart |
| 20 | Session resume across a signaling blip (hello with session_id) — so a WSS hiccup does not tear down a healthy media path | slice-2 review, docs/23 | after slice 5 (measured need) | needs a protocol addition on all three tiers plus server-side session retention; the operator’s ladder already covers the blip with a new round, at the cost of a media restart |
| 22 | IPv6 inside the tunnel — worth it, or is IPv4-only honest for the lifetime of the feature? | docs/27 | M4.5 | ROS 2 and ssh are fine on v4; the cost is a second address scheme and a second policy path |
| 23 | scp costs the camera nothing measurable — 31-33 fps either way, no lost frames (largest stamp step 1), longest gap 55-70 ms against a 48-51 ms idle baseline — and costs itself about 35 % of its throughput (338 Mbps alone, 190-236 Mbps beside video). No separate PeerConnection is needed for this reason | docs/27 | closed | the stall found while measuring it is #28, and is independent of video |
| 28 | link-stats now counts as abandoned | docs/27, ADR-0027, M4.5 gate review | closed | a usrsctp bug report upstream with this data, since the fragment-stranding is plausibly theirs |
| 24 | Windows support for fjarr-connect (WSL2 only, or native Wintun)? | ADR-0024 | demand-driven | Linux and macOS are the committed matrix; WSL2 is the free answer if it suffices |
| 25 | Is a forwarded-socket mode ever worth adding for hosts that cannot provide a TUN device? | ADR-0023 | demand-driven | deliberately not built (ADR-0023); it cannot carry DDS, so it would only serve ssh/scp on a locked-down host |
| 26 | Default tunnel range vs cellular carriers that hand out 100.64.0.0/10 WAN addresses | docs/27 | M4.5 field data | mitigated by only ever adding /32 routes plus an overlap check in --check; revisit if a design partner’s carrier collides |
| 29 | fjarr-connect, relay-only, direct path removed, at 1184 and 1280, daemon warm, cold or started with the link down; discovery 0.4 s warm, 1.2 s cold. None of 4.5d’s failure was Cyclone’s: the check asked ros2 topic list once as the link came up, and that command returns the daemon’s current view instead of waiting — the “fast miss” — and the link ran at #28’s 1280 MTU. Measured along the way: fact A is a Fast DDS property — a Cyclone participant, told its interface and peers, binds fjarr0 with or without a carrier and is visible once the agent attaches, so the ordering rule does not bind Cyclone; the operator-side daemon ordering docs/27 had only argued is now measured and harmless for both. Two lab defects fixed: the ordering script recreated the participant without the tunnel addresses Cyclone requires, and the DDS isolation blocked the introspection port, so every ROS gate hung to a 240 s timeout and make tunnel-ros exited non-zero on a green link. Still open, not blocking: Cyclone on the tunnel alone (unmeasured over the real link), and a one-off burst of relay-only links failing to come up before DDS was involved, not reproduced in 11 later runs | docs/27 | closed | net setup writing the file with the robot’s real LAN interface name; nightly runs both implementations |
| 31 | Answered 2026-09-28: no. Viewers never claim; control is per domain (desktop, motion), claimed on engagement, and terminal, tunnel and files are never exclusive (docs/10). The original question: Should a session that only views take the input lease? Today the first session an operator opens takes it whatever it claims (docs/10), so a dashboard user watching a camera under one identity makes fjarr.net open under another identity fail with capability-denied: input is owned by another operator. Found in slice 4.5e’s follow-up when a viewer beside a tunnel had to be the same operator to run at all. One operator at a time may be exactly the intent; if so docs/10 should say a viewer counts, and the CLI should say who holds it | docs/10, docs/27 | 4.5f, where the CLI meets real operators | the lease is fail-open on staleness, so the cost of the current rule is a refusal with a clear message, not a hang |
| 32 | net setup exists. Its drop-in ordered the customer’s units after fjarr-net.service (the device), but docs/27’s measured fact is that a Fast DDS participant created before the agent attaches never sees the interface, and After=fjarr-agent.service waited for READY = the first hello-ack, i.e. the WAN.READY once the capabilities are configured and the loop runs, STATUS= carries the connection, and the drop-in orders after both units. Found on the way: with READY on hello-ack, an offline robot’s agent was killed and restarted by the 90 s start timeout for as long as the WAN was down | docs/26, docs/27, surfaced 2026-09-28 building net setup | closed | proven on the spike machine: a unit ordered by the drop-in started with fjarr0 carrying LOWER_UP, with the server unreachable |
| 33 | fjarr-connect’s open once went unanswered when the device’s control channel opened about 2 s lateon-open handler, so a request sent as soon as the client’s end opened met no handler while the loop was busy, and was dropped. Not a network race and not the client’s: reproduced every time by blocking the loop during DTLS/SCTP setup | docs/23 | closed | handlers are connected at channel creation; Session.aRequestThatArrivesWhileTheLoopIsBusyAtChannelOpenIsAnswered is the regression |
| 34 | Found 2026-09-30: the agent offers ICE candidates on its own tunnel address (fjarr0, e.g. tcp4 host 100.77.176.22) — a path that exists only while the link it would carry is up. Harmless on the mini-PC trial, but ICE could choose it and lose the connection it rides on (an ICE restart is the likely trigger) | docs/23, docs/27 | closed 2026-09-30 | a tunnel interface never carries ICE on either end: the agent gives libnice an explicit address list without it, fjarr-connect binds explicit addresses; opsim’s tunnel scenario asserts no offered candidate carries the tunnel address |
| 35 | Seen once 2026-09-30: fjarr-opsim died of SIGSEGV in CI’s ice-restart scenario right after the robot’s session-close{reason: ice-restart}, every check already passed (commit c902dd4). Not reproduced in 20 local runs. A crash in the test tool, not the agent, but a race in opsim’s teardown can hide one in the code it drives | docs/23 | open | make opsim-all capture a backtrace when a scenario dies of a signal (core dump plus gdb in CI), then fix from the trace |
| 36 | Seen once 2026-10-01: the first make desktop-e2e after rebuilding the fixture image had the desktop track’s tier fail with pipeline refused PLAYING, and while it did the agent’s introspection endpoint timed out (GET /stats: Socket I/O timed out). That suggests a state change blocked the agent’s loop. Cold starts and recreating the fixture and the robot in that order both passed afterwards, in three tries | docs/23 | open | if it recurs: the robot’s log around the tier start, a backtrace of the agent’s threads while /stats hangs (docs/12#debugging-a-hung-process), and the fixture’s pipewire.log |
| 37 | display list and --check tell that a real monitor is plugged into a ghost connector?/sys/class/drm/card*-<connector>/ddc → /dev/i2c-N, address 0x50). With a DELL U2422H on the ghost’s HDMI-A-1 it answered “DELL U2422H”; with nothing plugged in, an I/O error. The connector’s status and EDID say “Fjarr Ghost 1” either way, and amdgpu’s HDCP probe kept reporting a sink after the unplug | docs/26 | closed | display list and --check read DDC on each ghost connector; fjarr-desktop-wayland loads i2c-dev |
| 38 | pausing after gst_pad_push() = flushing. stop_tier unlinked a branch and set it to NULL while the tee’s streaming thread could still be pushing into it. FLUSHING went back through the tee, and the source paused its task for good, freezing every tier. A robot under load would freeze its video when a viewer’s tier changed | docs/23 | closed | the branch is unlinked from an IDLE probe on the tee’s pad, so never mid-push (a 2 s deadline keeps a stuck pad from holding the loop); 0 of 25 under the same load afterwards |
| 39 | Seen twice in CI (ba93066, a0ad487): make opsim-tunnel was told robot-offline right after make tun-up said the robot was online. The server saw the agent connect after tun-up’s deliberate restart, then disconnect about 10 s later with nothing in the job causing it, and reconnect 0.7 s after that (the supervisor’s restart). opsim’s hello landed in that gap. Not reproduced locally (one restart, then stable). The CI dump had lost the supervisor’s exit line | docs/27 | open | CI now dumps the robot’s exit lines and the server’s view right after a failed tunnel step; fix from the exit code (a crash, a deliberate exit, or something in the namespace) |
| 40 | fjarr-opsim on the harness’s side, and skipped). On a workstation with VA-API it failed 2 runs in 3: a viewer behind a bad link held the shared active encoder, and both clean viewers, at band_low (2 Mbps) for about 7 s — docs/23 promises at most 2 s. Not #38, and not a regression since 2026-09-26 (that agent failed the same way). Two causes: a rate tick without new TWCC feedback (four ticks in five) counted as “untested” and reset the demotion timer, so leaving took about 4 s; and a demoted viewer’s last share in the active tier stayed until it aged out after 3 s. The software encoder hid both: the clean viewers there never dipped | docs/23 | closed | ticks without feedback hold the clocks (TierPolicy::Evidence::none); a viewer’s report for a tier withdraws its share from the track’s other tiers, and a tier change forgets it at once. Regression tests for both; afterwards 7 of 8 VA-API runs and 3 of 3 software runs passed, the clean viewers at 3.9–4.2 Mbps throughout. The one VA-API failure, the first run after the robot started, had a clean viewer at 1.55 Mbps for one sample — watch for it on the GPU nightly |
| 30 | Should Fjarr Cloud offer a hosted operator API for customers unwilling to build even two endpoints? | docs/27 | M5 Cloud alpha | it would mean Cloud holding a notion of individual developers, which ADR-0015 deliberately kept out of Fjarr; the grant_command escape hatch may be enough |
| 19 | docs/22 | M3 spike | portal is the written primary (one grant, one lease, no cross-window input proxy) but relies on same-origin popups sharing the opener’s agent cluster for srcObject; the spike measures it on Chrome/Edge/Firefox and under Cross-Origin-Opener-Policy: same-origin, which many host apps set | |
| 41 | Clipboard sync on focus: when the desktop view gains focus, read the operator’s clipboard (navigator.clipboard.read(), Chrome asks for permission once) and put it on the robot’s clipboard ahead of time, so a paste on the robot (right-click → Paste, a menu) gets it too. Today the browser hands the page its clipboard only on a Ctrl+V inside the view (docs/22) | mini-PC, 2026-10-04 (an image pasted with the robot’s file manager’s right-click Paste never reached the robot) | an option in @fjarr/react, off by default | Unscheduled: in no milestone, by the user’s decision (2026-10-04). Costs to weigh: whatever is on the operator’s clipboard (a password) lands on the robot on every focus, and it replaces what the robot copied last unless the operator copied something since |
| 42 | The terminal and the files as another account. The agent runs as its own unprivileged account (ADR-0009, ADR-0028) and cannot switch users, so fjarr.terminal gives a shell as fjarr, which can barely do anything, and fjarr.files would read and write as it too. Options: A run the agent as robot (undoes the separation; on a robot with sudo, the agent becomes root); B systemd’s own path, run0 --user=robot or machinectl shell robot@, allowed by a packaged polkit rule for the agent’s account and the configured target only (no root code of ours; a real login session); C a small root helper of our own that checks the caller’s uid and hands back a pty (full control, root code to keep secure); D ssh through fjarr.net (works today, not a dashboard terminal). Leaning B | mini-PC, 2026-10-04 (a terminal as fjarr “can barely do anything”) | spike on the mini-PC + ADR, M4 (roadmap) | the spike settles whether polkit can restrict the target account of run0/machinectl; setup would offer “terminal as robot” and write the rule |
| 43 | M8 critique | — | — | |
| 44 | INCOMPATIBLE_LICENSE = "GPL-3.0*"); whether a robot is a “user product” is the robot company’s question (docs/32) | M8 critique | — | — |
| 45 | Can the ZED Box Mini’s carrier configuration (device tree, pinmux) and the ZED Link drivers be packaged in an open board package, or only fetched from Stereolabs at build time? Approach decided 2026-10-13 (Erik): slice 8.0 reads the licences of Stereolabs’ flash scripts and stereolabs-zedbox-mini package and asks Stereolabs; if they may not be redistributed, the board package fetches them at build time, as meta-tegra does with NVIDIA’s. Read 2026-10-13/14 (8.0): the package and the device-tree tarballs state no licence of their own (all rights reserved by default), and the kernel modules are GPL-2.0 with no published source (it sits behind an NDA), so none of it can be redistributed: the board package fetches the driver package, its overlays and daemons at build time, pinned by SHA-256, and never runs its install script on an A/B robot (it edits the boot configuration and NVIDIA’s modules in place). The bootloader changes (two pinmux and GPIO files, no carrier EEPROM) are small edits to NVIDIA’s files, and the kernel device tree for the Orin NX 16 GB on L4T r36.4.4 differs from NVIDIA’s only in the USB port mapping, a micro-USB connector, a GPS pulse-per-second input and a suspend key on GPIOs: Fjarr writes both as its own patch and overlay. Pinned 2026-10-17 (8.0, L4T 36.5.2 by Q1 in docs/32): Fjarr’s Jetson images use stereolabs-zedbox-mini_1.4.4-SL-MAX9296-ZEDBOX-MINI-L4T36.5.2_arm64.deb from https://download.stereolabs.com/drivers/zedx/1.4.4/R36.5.2/, SHA-256 044be09281731c1eea75dc9eb4487ed4f6379fa3124f711cbbe77fe6787c3f51. Its kernel side is seven Stereolabs modules (sl_max9296, sl_max9295, sl_zedx, sl_zedxone_uhd, sl_zedxhdr, bmi088, bmi_spsc, GPL-2.0 by modinfo) built for NVIDIA’s own kernel (5.15.199-tegra), plus replacement builds of two of NVIDIA’s modules, host1x.ko and tegra-drm.ko, which its postinst moves over NVIDIA’s in /lib/modules/$(uname -r)/updates. The replacements carry two small fixes to NVIDIA’s 36.5.2 source, which the package’s postinst names in a comment (a host1x syncpoint alloc/free race and a tegra-drm comm-rename race) and which are visible in the functions they call: host1x drops a syncpoint’s last reference under the syncpoint mutex (its host1x_syncpt_put calls refcount_dec_and_mutex_lock, which upstream Linux commit c7d393267c49 “gpu: host1x: Fix race in syncpt alloc/free” brings in through kref_put_mutex; NVIDIA’s source still calls kref_put), and tegra-drm’s syncpoint-allocate ioctl copies the task’s name (__get_task_comm) where NVIDIA’s passes current->comm. Fjarr’s images carry equivalent nvidia-oot patches, the first backported from upstream, the second Fjarr’s own. The same 1.4.4 package built for 36.4 also replaces NVIDIA’s capture-ivc, host1x-fence and nvhost-vi5 modules and, on 36.4.0 and 36.4.3, NVIDIA’s ISP library libnvisppg.so; the 36.5.2 package replaces no ISP library. NVIDIA’s nvidia-oot builds a bmi088.ko of its own for the same device-tree compatible (bmi,bmi088), and Fjarr’s images keep NVIDIA’s out (meta-tegra packages each of NVIDIA’s modules on its own; the image’s TEGRA_OOT_CAMERA_DRIVERS leaves out bmi088). No Stereolabs flash script targets 36.5.x: the published ones, for JetPack 6.2 and 7.2, fetch device-tree tarballs for L4T 36.4.4 and 39.2.1. Stereolabs’ bootloader changes carry over to 36.5.2 (A1, 2026-10-17): the three NVIDIA files they change are byte-identical in L4T 36.4.4 and 36.5.2, so the changes apply unchanged, and Stereolabs’ download server also holds a zedbox_mini_device_trees_365.tar (dated 2026-06-02), which no published script fetches, with exactly those three files, identical to the 36.4.4 ones, and no kernel device tree. The rest of the board’s bootloader input differs between the two releases: 36.5.2 adds NVIDIA’s critical QSPI fix for Orin modules (trimmer2-val in the MB1 device configuration, published for earlier 36.x releases with a field bulletin), which Stereolabs’ 36.4.4 flash script does not apply, so the factory firmware lacks it (measured 2026-10-20 from the lab box’s QSPI dump: trimmer2-val 0x0c in both MB1 BCT copies, docs/32); moves a display block from MB2’s common configuration to MB1’s; and updates the firewall and flashing configurations, most firmware binaries, and nodes of NVIDIA’s kernel device tree other than those Stereolabs’ carrier changes. Only the pinmux and GPIO files are BSD-3: NVIDIA’s MB2 file states no licence and includes NVIDIA’s proprietary MB2 common file, so Fjarr writes an MB2 file of its own instead of patching NVIDIA’s (docs/32). Fjarr’s images never run the maintainer scripts (the root is read-only); instead the modules are built with the kernel (#47) and loaded by a Fjarr unit with modprobe, passing sl_max9296’s sync_mode module parameter (listed by modinfo); the camera overlay is merged into the device tree at build time; nvargus-daemon gets enableCamInfiniteTimeout=1 from a drop-in; and the ISP files, the module blacklist, the IMU udev rule and the imu group ship in the image. Open: Stereolabs’ answer to Erik’s email of 2026-10-17 (licences, redistribution by partners, the modules’ source: #47); the other modules’ device trees | M8 design, 2026-10-12 | 8.0 | closed with 8.0’s answer |
| 46 | M8 design review | — | — | |
| 47 | Found in 8.0 (2026-10-16/17): Stereolabs’ prebuilt ZED X kernel modules do not load on the kernels Fjarr’s Jetson images run. They are built for NVIDIA’s own kernel (5.15.199-tegra) with symbol checksums (modversions): the kernel then ignores a module’s release string but refuses it if any checksum differs from its own. meta-tegra and jetpack-nixos build NVIDIA’s 5.15.199 source under their own release strings (5.15.199-l4t-r36.5-1050.50+g<commit>, plain 5.15.199) and configurations. meta-tegra’s configuration switches module signing off, which NVIDIA’s defconfig has on, and that alone changes the module_layout checksum every module carries, so none loads there; jetpack-nixos’s kernel, built with NixOS’s configuration on top of NVIDIA’s, is expected to refuse them too, not yet checked. So Fjarr’s Jetson images must build the seven modules from source (#45). The GPL source of the 1.4.4 modules, with their host1x and tegra-drm changes, was requested from Stereolabs on 2026-10-17 | docs/32, 8.0 research | Stereolabs’ answer, then a module recipe (8.4) | until the source arrives, the cameras are blocked on Fjarr’s Jetson images; 8.0’s OTA tests do not need them |
| 48 | ⚠ The early-hang watchdog on Jetson. On L4T R36 nothing arms a watchdog that outlives the firmware before Linux userspace opens one: MB1 and MB2 run with theirs off by default, UEFI’s is a boot-services timer that stops when the kernel starts, and the kernel’s driver waits for userspace. L4TLauncher’s retry counter is reset by a cold boot, so a slot whose kernel hangs early hangs until a power cut, then boots and hangs again, forever. On x86 only EFI Boot Guard’s loader-armed watchdog recovered from this case, which supports ADR-0040, chosen for one engine across the recipes. Expected from NVIDIA’s sources and documentation, not yet measured | docs/32, 8.0 research, 2026-10-16 | measured in 8.0 (D2b, D4); a gate for 8.4 by Q8 (Erik, 2026-10-17) | options under study: a watchdog armed by L4TLauncher before the kernel starts, as EFI Boot Guard arms one on x86 (tried in 8.0 only as a second loader through BootNext); the kernel arming a watchdog at probe (upstream commit 46fb093b87a0), which narrows the gap without closing it, since the driver’s own interrupt keeps it alive; the applier’s trial counter and the lab dead-man, which act only once userspace runs |
| 49 | Mutter’s shared-memory monitor screencasts record nothing while a window is scanned out directly (found on the mini-PC 2026-10-17, ADR-0041): mutter 50.1’s before_stage_painted returns early for streams that are not DMA-BUF, so every SHM consumer freezes behind a fullscreen GPU window. Fixed upstream by mutter merge request !5276 (closes issue #3903), released in mutter 50.5 (2026-09-15) and 51.0; Ubuntu 26.04 ships 50.1 without it (50.1-0ubuntu2.4 in resolute-updates, checked 2026-10-17). A Launchpad SRU request to backport it to 26.04 is drafted in spikes/capture-scanout; Erik files it. Separately, mutter’s hardware-cursor inhibitor takes effect only at the next cursor update (still so on mutter main) | M3 gate review, 2026-10-17 | the 26.04 SRU | Fjarr does not depend on it: module E captures physical monitors as DMA-BUF (ADR-0041) |