What is the first-run UX of the RemoteDesktop portal permission prompt under niri: when does it appear, what must the user click, and what happens on deny or re-prompt? Ground in portal docs and the actual injection code path.
Part of #1
## Question
What is the first-run UX of the RemoteDesktop portal permission prompt under niri: when does it appear, what must the user click, and what happens on deny or re-prompt? Ground in portal docs and the actual injection code path.
Decision (agent-made under the user's explicit delegation for this run): there is no portal permission prompt on niri, by design of the desktop stack — and the spec should say so plainly. On niri, first dictation goes straight to the wtype fallback with no permission UI at all. The RemoteDesktop permission dialog exists only on desktops whose portal backend implements org.freedesktop.portal.RemoteDesktop (GNOME, KDE). Consequences for the spec and the port are listed at the end.
When the prompt appears (desktops that have one). The probe at startup never prompts: RemoteDesktopPortal.probe() only reads the interface version property (src/vocalinux/text_injection/remote_desktop_portal.py, probe), and TextInjector._portal_probe() documents that "the session handshake (and its one-time permission prompt) is deferred to the first actual injection" (src/vocalinux/text_injection/text_injector.py). The dialog is presented by the compositor's portal backend during the CreateSession → SelectDevices → Start handshake in _ensure_session(), which runs lazily on the first inject_text / tap_keysym / send_shortcut. Start() carries a 180 s budget (_START_TIMEOUT_S) precisely because it blocks on the user clicking through that dialog.
What the user clicks (GNOME/KDE). Per the upstream portal spec (RemoteDesktop docs), Start() "will typically result in the portal presenting a dialog letting the user select what to share, including devices". Voculinux requests keyboard only (_DEVICE_TYPE_KEYBOARD), so the user approves keyboard access for the session. On success with interface v2, Start returns a single-use restore_token, which the client persists owner-only (0600) at $XDG_DATA_HOME/vocalinux/remote_desktop_restore_token (_save_restore_token); the next session offers that token in SelectDevices (persist_mode 2, until revoked) and the prompt is skipped. A stale token is harmless: the spec says an invalid one is ignored and the user gets the normal prompt. On interface v1 there is no persistence and the prompt recurs on every app start.
Why niri never prompts. No portal backend implements RemoteDesktop on niri. xdg-desktop-portal-wlr implements only Screenshot and ScreenCast; the GNOME backend's RemoteDesktop path requires Mutter's private RemoteDesktop service, which niri does not run. Our own user guide already records this: "portal needs a desktop implementing the org.freedesktop.portal.RemoteDesktop interface (GNOME and KDE Plasma do; most wlroots compositors do not)" (docs/USER_GUIDE.md). Independent third-party matrices agree (wlroots-family compositors including niri ship no RemoteDesktop backend; wtype is the working route there). With no backend, the version read fails, probe() returns False, the portal backend is never selected, and dictation falls through to wtype — silently, with no dialog, nothing to click, and nothing to deny.
Deny and re-prompt (where a prompt exists). Dismissing the dialog returns response code 1, raised as "Portal request was cancelled by the user" (_await_response); the client closes the unfinished session so no half-grant lingers, never retries Start on that session, and keeps the saved restore token untouched — all pinned by tests/test_remote_desktop_portal.py (test_cancelled_start_is_not_retried, test_failed_session_is_closed). The injector then demotes the portal for the run (_demote_portal_backend) and types the remaining text through wtype/ydotool/XWayland, so a deny costs a fallback, not the dictation. Nothing negative is cached: the next app start builds a fresh session and the dialog appears again, until the user grants once (then the restore token takes over on v2).
Provenance. Resolved by agent decision under the user's explicit delegation for this wayfinder run ("answer all the wayfinder questions ... continue till all wayfinder tickets are closed"). Grounded in the portal client and injector code paths above, the pinned portal tests, the upstream org.freedesktop.portal.RemoteDesktop spec, and third-party compositor matrices for the niri-negative.
Consequences for later work.
The Wayland terminal injection choice (issue #6) stands unchanged, but its "portal first" rung is a GNOME/KDE rung: on niri the effective order starts at wtype. The handoff spec should state the niri first-run experience as "no prompt; wtype types directly".
The voculinux rename's one-time import (issue #8) copies config plus dictionary; it does not mention remote_desktop_restore_token. GNOME/KDE migrants will see one extra permission prompt after the rename unless the import carries that file too — a one-line addition for the implementation phase, moot on niri.
No new tickets: this answer closes the last in-scope question and graduates nothing new. Findings live in this comment; no research branch was pushed.
## Resolution: Portal first-run prompt UX under niri
**Decision (agent-made under the user's explicit delegation for this run): there is no portal permission prompt on niri, by design of the desktop stack — and the spec should say so plainly.** On niri, first dictation goes straight to the `wtype` fallback with no permission UI at all. The RemoteDesktop permission dialog exists only on desktops whose portal backend implements `org.freedesktop.portal.RemoteDesktop` (GNOME, KDE). Consequences for the spec and the port are listed at the end.
**When the prompt appears (desktops that have one).** The probe at startup never prompts: `RemoteDesktopPortal.probe()` only reads the interface `version` property (`src/vocalinux/text_injection/remote_desktop_portal.py`, `probe`), and `TextInjector._portal_probe()` documents that "the session handshake (and its one-time permission prompt) is deferred to the first actual injection" (`src/vocalinux/text_injection/text_injector.py`). The dialog is presented by the compositor's portal backend during the `CreateSession` → `SelectDevices` → `Start` handshake in `_ensure_session()`, which runs lazily on the first `inject_text` / `tap_keysym` / `send_shortcut`. `Start()` carries a 180 s budget (`_START_TIMEOUT_S`) precisely because it blocks on the user clicking through that dialog.
**What the user clicks (GNOME/KDE).** Per the upstream portal spec ([RemoteDesktop docs](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.RemoteDesktop.html)), `Start()` "will typically result in the portal presenting a dialog letting the user select what to share, including devices". Voculinux requests keyboard only (`_DEVICE_TYPE_KEYBOARD`), so the user approves keyboard access for the session. On success with interface v2, `Start` returns a single-use `restore_token`, which the client persists owner-only (`0600`) at `$XDG_DATA_HOME/vocalinux/remote_desktop_restore_token` (`_save_restore_token`); the next session offers that token in `SelectDevices` (`persist_mode` 2, until revoked) and the prompt is skipped. A stale token is harmless: the spec says an invalid one is ignored and the user gets the normal prompt. On interface v1 there is no persistence and the prompt recurs on every app start.
**Why niri never prompts.** No portal backend implements RemoteDesktop on niri. `xdg-desktop-portal-wlr` implements only Screenshot and ScreenCast; the GNOME backend's RemoteDesktop path requires Mutter's private RemoteDesktop service, which niri does not run. Our own user guide already records this: "`portal` needs a desktop implementing the `org.freedesktop.portal.RemoteDesktop` interface (GNOME and KDE Plasma do; most wlroots compositors do not)" (`docs/USER_GUIDE.md`). Independent third-party matrices agree (wlroots-family compositors including niri ship no RemoteDesktop backend; `wtype` is the working route there). With no backend, the version read fails, `probe()` returns False, the portal backend is never selected, and dictation falls through to `wtype` — silently, with no dialog, nothing to click, and nothing to deny.
**Deny and re-prompt (where a prompt exists).** Dismissing the dialog returns response code 1, raised as "Portal request was cancelled by the user" (`_await_response`); the client closes the unfinished session so no half-grant lingers, never retries `Start` on that session, and keeps the saved restore token untouched — all pinned by `tests/test_remote_desktop_portal.py` (`test_cancelled_start_is_not_retried`, `test_failed_session_is_closed`). The injector then demotes the portal for the run (`_demote_portal_backend`) and types the remaining text through `wtype`/`ydotool`/XWayland, so a deny costs a fallback, not the dictation. Nothing negative is cached: the next app start builds a fresh session and the dialog appears again, until the user grants once (then the restore token takes over on v2).
**Provenance.** Resolved by agent decision under the user's explicit delegation for this wayfinder run ("answer all the wayfinder questions ... continue till all wayfinder tickets are closed"). Grounded in the portal client and injector code paths above, the pinned portal tests, the upstream `org.freedesktop.portal.RemoteDesktop` spec, and third-party compositor matrices for the niri-negative.
**Consequences for later work.**
- The Wayland terminal injection choice (issue #6) stands unchanged, but its "portal first" rung is a GNOME/KDE rung: on niri the effective order starts at `wtype`. The handoff spec should state the niri first-run experience as "no prompt; wtype types directly".
- The voculinux rename's one-time import (issue #8) copies config plus dictionary; it does not mention `remote_desktop_restore_token`. GNOME/KDE migrants will see one extra permission prompt after the rename unless the import carries that file too — a one-line addition for the implementation phase, moot on niri.
- No new tickets: this answer closes the last in-scope question and graduates nothing new. Findings live in this comment; no research branch was pushed.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of #1
Question
What is the first-run UX of the RemoteDesktop portal permission prompt under niri: when does it appear, what must the user click, and what happens on deny or re-prompt? Ground in portal docs and the actual injection code path.
Resolution: Portal first-run prompt UX under niri
Decision (agent-made under the user's explicit delegation for this run): there is no portal permission prompt on niri, by design of the desktop stack — and the spec should say so plainly. On niri, first dictation goes straight to the
wtypefallback with no permission UI at all. The RemoteDesktop permission dialog exists only on desktops whose portal backend implementsorg.freedesktop.portal.RemoteDesktop(GNOME, KDE). Consequences for the spec and the port are listed at the end.When the prompt appears (desktops that have one). The probe at startup never prompts:
RemoteDesktopPortal.probe()only reads the interfaceversionproperty (src/vocalinux/text_injection/remote_desktop_portal.py,probe), andTextInjector._portal_probe()documents that "the session handshake (and its one-time permission prompt) is deferred to the first actual injection" (src/vocalinux/text_injection/text_injector.py). The dialog is presented by the compositor's portal backend during theCreateSession→SelectDevices→Starthandshake in_ensure_session(), which runs lazily on the firstinject_text/tap_keysym/send_shortcut.Start()carries a 180 s budget (_START_TIMEOUT_S) precisely because it blocks on the user clicking through that dialog.What the user clicks (GNOME/KDE). Per the upstream portal spec (RemoteDesktop docs),
Start()"will typically result in the portal presenting a dialog letting the user select what to share, including devices". Voculinux requests keyboard only (_DEVICE_TYPE_KEYBOARD), so the user approves keyboard access for the session. On success with interface v2,Startreturns a single-userestore_token, which the client persists owner-only (0600) at$XDG_DATA_HOME/vocalinux/remote_desktop_restore_token(_save_restore_token); the next session offers that token inSelectDevices(persist_mode2, until revoked) and the prompt is skipped. A stale token is harmless: the spec says an invalid one is ignored and the user gets the normal prompt. On interface v1 there is no persistence and the prompt recurs on every app start.Why niri never prompts. No portal backend implements RemoteDesktop on niri.
xdg-desktop-portal-wlrimplements only Screenshot and ScreenCast; the GNOME backend's RemoteDesktop path requires Mutter's private RemoteDesktop service, which niri does not run. Our own user guide already records this: "portalneeds a desktop implementing theorg.freedesktop.portal.RemoteDesktopinterface (GNOME and KDE Plasma do; most wlroots compositors do not)" (docs/USER_GUIDE.md). Independent third-party matrices agree (wlroots-family compositors including niri ship no RemoteDesktop backend;wtypeis the working route there). With no backend, the version read fails,probe()returns False, the portal backend is never selected, and dictation falls through towtype— silently, with no dialog, nothing to click, and nothing to deny.Deny and re-prompt (where a prompt exists). Dismissing the dialog returns response code 1, raised as "Portal request was cancelled by the user" (
_await_response); the client closes the unfinished session so no half-grant lingers, never retriesStarton that session, and keeps the saved restore token untouched — all pinned bytests/test_remote_desktop_portal.py(test_cancelled_start_is_not_retried,test_failed_session_is_closed). The injector then demotes the portal for the run (_demote_portal_backend) and types the remaining text throughwtype/ydotool/XWayland, so a deny costs a fallback, not the dictation. Nothing negative is cached: the next app start builds a fresh session and the dialog appears again, until the user grants once (then the restore token takes over on v2).Provenance. Resolved by agent decision under the user's explicit delegation for this wayfinder run ("answer all the wayfinder questions ... continue till all wayfinder tickets are closed"). Grounded in the portal client and injector code paths above, the pinned portal tests, the upstream
org.freedesktop.portal.RemoteDesktopspec, and third-party compositor matrices for the niri-negative.Consequences for later work.
wtype. The handoff spec should state the niri first-run experience as "no prompt; wtype types directly".remote_desktop_restore_token. GNOME/KDE migrants will see one extra permission prompt after the rename unless the import carries that file too — a one-line addition for the implementation phase, moot on niri.