Skip to content

Add a view-only live view member profile - #493

Merged
masnwilliams merged 3 commits into
mainfrom
hypeship/live-view-viewer-profile
Nov 6, 2026
Merged

masnwilliams merged 3 commits into
mainfrom
hypeship/live-view-viewer-profile

Conversation

@masnwilliams

@masnwilliams masnwilliams commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a view-only Neko member profile for read-only live view links.

  • Neko base: bumps to ghcr.io/kernel/neko/base:3.0.8-v1.6.4, which includes Refuse input from sessions without host permission neko#30 (members without host permission don't send input, clipboard or keyboard changes) and Generate screen modes at the exact requested size and refresh rate neko#31 (screen modes generated at the exact requested size and refresh rate).
  • neko.yaml: the multiuser user member becomes a view-only profile (can_host, can_share_media, can_access_clipboard, sends_inactive_cursor and is_admin all false) with its own password, used by the live-view proxy for read-only links.
  • Client: when the page sets window.KERNEL_LIVE_VIEW_READ_ONLY = true, the viewer starts read-only and answers KERNEL_SET_READ_ONLY unlock requests with readOnly: true, so it doesn't offer controls the member can't use. Without the flag, behavior is unchanged.

The admin member and the default live view behavior are unchanged.

Before merging

kernel/neko#30 and kernel/neko#31 are merged, and v3.0.8-v1.6.4 is published from neko master. This PR now uses that final tag.

Testing


Note

Medium Risk
Touches live-view input policy, NeKo credentials, and display sizing semantics via a base-image bump; misconfiguration could affect who can control sessions or odd-width resize behavior.

Overview
View-only live view pairs a dedicated Neko member profile with client logic so read-only links stay read-only even when parents ask to unlock.

The Docker/NeKo stack bumps the NeKo base image to 3.0.8-v1.6.4 and reshapes neko.yaml: the non-admin member uses password live-view-read-only and has hosting, media, clipboard, and inactive-cursor sending disabled—matching a view-only account the live-view proxy can use.

The viewer declares window.KERNEL_LIVE_VIEW_READ_ONLY, adds resolveReadOnly() so URL params and KERNEL_SET_READ_ONLY postMessages cannot leave read-only mode when that flag is true, and documents the contract in the README. Tests cover resolveReadOnly and the e2e odd-width display test is renamed and updated to expect the X root and API response match the requested width (1365×768), reflecting NeKo’s exact-size modes instead of libxcvt rounding.

Reviewed by Cursor Bugbot for commit b265c4b. Bugbot is set up for automated code reviews on this repo. Configure here.

@masnwilliams
masnwilliams force-pushed the hypeship/live-view-viewer-profile branch from 26dda8e to b6cca85 Compare October 27, 2026 21:47
@masnwilliams masnwilliams changed the title Add a view-only live view member Add a view-only live view member profile Oct 27, 2026

@tnsardesai tnsardesai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi, remember to merge kernel/neko#30 first and update this version (images/chromium-headful/Dockerfile:L165, currently 3.0.8-v1.6.4-rc.1) before merging.

Follow-ups

  • Follow-up bug — read-only links will never connect on GPU browsers — PR-level (kernel-images-private shared/gpu/neko.yaml)
    • Problem: Dockerfile.gpu in kernel-images-private overwrites /etc/neko/neko.yaml with shared/gpu/neko.yaml. That file still has user_password: "neko" and the user profile that can take control, and syncing this PR doesn't touch it.
    • Impact: once kernel/kernel#5042 ships, read URLs for GPU browsers load the page, but the stream never connects.
    • Fix: apply the same user_password and user_profile change to shared/gpu/neko.yaml during the private sync, and add that step to #5042's merge order before #5042 merges.

masnwilliams added a commit to kernel/neko that referenced this pull request Nov 6, 2026
## Summary

Refuse input from sessions whose member profile doesn't have host
permission (`can_host: false`), so a view-only member can watch without
controlling the desktop.

- `LegacyIsHost()` returns `false` when the profile can't host.
Otherwise it keeps its existing behavior: true under implicit hosting,
or when the session is the host.
- `clipboardSet`, `keyboardMap` and `keyboardModifiers` require
`can_host` (clipboard set also requires `can_access_clipboard`).
- `controlRelease` requires `can_host`.

The checks use the profile's `can_host` rather than `IsHost()`, because
with legacy implicit hosting a session that may control isn't
necessarily registered as the host.

Sessions that can host behave as before, including the default admin and
user profiles.

## Tests

New unit tests:

- `internal/session`: `LegacyIsHost` with and without implicit hosting,
for profiles that can and can't host.
- `internal/websocket/handler`: a session without host permission is
refused for control request/release, move, key press, clipboard set,
keyboard map and keyboard modifiers, with no desktop calls and no host
change. A host-capable session still reaches the desktop for each.
- `internal/webrtc`: legacy data-channel move, key down and button down
are dropped for a session without host permission and applied for a
host-capable one.

`go vet ./internal/...` and `go test ./...` pass in the server build
image.

## After merging

Publish `v3.0.8-v1.6.4` from `master`. kernel/kernel-images#493 is on
the `v3.0.8-v1.6.4-rc.1` release candidate built from this branch and
will be bumped to the final tag.
@masnwilliams
masnwilliams merged commit 6d4d1a6 into main Nov 6, 2026
12 checks passed
@masnwilliams
masnwilliams deleted the hypeship/live-view-viewer-profile branch November 6, 2026 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants