gui-remote-controll 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gui_remote_controll-0.1.1/CHANGELOG.md +15 -0
- gui_remote_controll-0.1.1/LICENSE +21 -0
- gui_remote_controll-0.1.1/PKG-INFO +104 -0
- gui_remote_controll-0.1.1/README.md +64 -0
- gui_remote_controll-0.1.1/README.zh-CN.md +60 -0
- gui_remote_controll-0.1.1/SECURITY.md +32 -0
- gui_remote_controll-0.1.1/docs/README.md +20 -0
- gui_remote_controll-0.1.1/docs/architecture-and-algorithms.md +334 -0
- gui_remote_controll-0.1.1/docs/cli-reference.md +146 -0
- gui_remote_controll-0.1.1/docs/development.md +158 -0
- gui_remote_controll-0.1.1/docs/platform-support.md +168 -0
- gui_remote_controll-0.1.1/docs/user-guide.md +286 -0
- gui_remote_controll-0.1.1/gui_remote_controll/__init__.py +6 -0
- gui_remote_controll-0.1.1/gui_remote_controll/__main__.py +191 -0
- gui_remote_controll-0.1.1/gui_remote_controll/app.py +439 -0
- gui_remote_controll-0.1.1/gui_remote_controll/auth.py +95 -0
- gui_remote_controll-0.1.1/gui_remote_controll/config.py +49 -0
- gui_remote_controll-0.1.1/gui_remote_controll/desktop.py +310 -0
- gui_remote_controll-0.1.1/gui_remote_controll/protocol.py +113 -0
- gui_remote_controll-0.1.1/gui_remote_controll/static/app.css +492 -0
- gui_remote_controll-0.1.1/gui_remote_controll/static/app.js +701 -0
- gui_remote_controll-0.1.1/gui_remote_controll/static/auth.css +120 -0
- gui_remote_controll-0.1.1/gui_remote_controll/static/auth.html +26 -0
- gui_remote_controll-0.1.1/gui_remote_controll/static/auth.js +15 -0
- gui_remote_controll-0.1.1/gui_remote_controll/static/index.html +79 -0
- gui_remote_controll-0.1.1/pyproject.toml +76 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
- Add opt-in bidirectional automatic clipboard synchronization.
|
|
6
|
+
- Request browser clipboard access when the remote UI opens and retry from the switch gesture.
|
|
7
|
+
- Prefer `clipboardchange` events with a focused-page polling fallback.
|
|
8
|
+
- Establish independent initial baselines and suppress synchronization feedback loops.
|
|
9
|
+
- Add digest-aware server polling and request-correlated clipboard write acknowledgements.
|
|
10
|
+
- Redirect stale authenticated pages to PIN login after a server restart.
|
|
11
|
+
- Document secure-context requirements and clipboard privacy considerations.
|
|
12
|
+
|
|
13
|
+
## 0.1.0
|
|
14
|
+
|
|
15
|
+
- Initial cross-platform browser remote desktop server.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GGN_2015
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gui-remote-controll
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Cross-platform remote desktop control from a web browser.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: remote-desktop,screen-sharing,websocket,remote-control
|
|
8
|
+
Author: GGN_2015
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: MacOS X
|
|
12
|
+
Classifier: Environment :: Win32 (MS Windows)
|
|
13
|
+
Classifier: Environment :: X11 Applications
|
|
14
|
+
Classifier: Framework :: FastAPI
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
27
|
+
Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture
|
|
28
|
+
Requires-Dist: fastapi (>=0.115)
|
|
29
|
+
Requires-Dist: mss (>=10.0)
|
|
30
|
+
Requires-Dist: pillow (>=10.4)
|
|
31
|
+
Requires-Dist: py-admin-launch (>=0.1.3)
|
|
32
|
+
Requires-Dist: pynput (>=1.8)
|
|
33
|
+
Requires-Dist: pyperclip (>=1.9)
|
|
34
|
+
Requires-Dist: uvicorn[standard] (>=0.30)
|
|
35
|
+
Project-URL: Documentation, https://github.com/GGN-2015/gui-remote-controll#readme
|
|
36
|
+
Project-URL: Homepage, https://github.com/GGN-2015/gui-remote-controll
|
|
37
|
+
Project-URL: Repository, https://github.com/GGN-2015/gui-remote-controll
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# GUI Remote Controll
|
|
41
|
+
|
|
42
|
+
[中文](https://github.com/GGN-2015/gui-remote-controll/blob/main/README.zh-CN.md)
|
|
43
|
+
|
|
44
|
+
GUI Remote Controll shares a Windows, Linux, or macOS desktop with a web browser. The
|
|
45
|
+
browser receives screen frames and can redirect mouse, touch, wheel, keyboard, text input,
|
|
46
|
+
and plain-text clipboard events to the server computer.
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
Python 3.10 or newer is required.
|
|
51
|
+
|
|
52
|
+
1. Install the package:
|
|
53
|
+
|
|
54
|
+
```console
|
|
55
|
+
pip install gui-remote-controll
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
2. Start a PIN-protected server:
|
|
59
|
+
|
|
60
|
+
```console
|
|
61
|
+
gui-remote-controll --pin 123456
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Startup requests administrator/root privileges once. Use `--no-elevate` only when the
|
|
65
|
+
desktop permissions have already been configured or elevation is inappropriate.
|
|
66
|
+
|
|
67
|
+
3. Open the control page:
|
|
68
|
+
|
|
69
|
+
- On the server computer: `http://127.0.0.1:8000`
|
|
70
|
+
- On the local network: `http://SERVER_LAN_IP:8000`
|
|
71
|
+
|
|
72
|
+
The default bind address is `0.0.0.0`. Always set `--pin` when another device can reach the
|
|
73
|
+
server. Use TLS or an encrypted tunnel outside a trusted LAN, and never expose the default
|
|
74
|
+
HTTP service directly to the public internet.
|
|
75
|
+
|
|
76
|
+
### First-run permissions
|
|
77
|
+
|
|
78
|
+
- **Windows:** accept the UAC prompt. Run the server from the signed-in interactive session.
|
|
79
|
+
- **Linux:** use an X11 desktop session. Native Wayland does not permit portable global
|
|
80
|
+
capture and input injection.
|
|
81
|
+
- **macOS:** grant Screen Recording and Accessibility permissions to the terminal or Python
|
|
82
|
+
executable, then restart the server. Root access does not replace these permissions.
|
|
83
|
+
|
|
84
|
+
See the [platform guide](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/platform-support.md)
|
|
85
|
+
for setup and troubleshooting details.
|
|
86
|
+
|
|
87
|
+
## Documentation
|
|
88
|
+
|
|
89
|
+
- [Documentation index](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/README.md)
|
|
90
|
+
- [Complete user guide](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/user-guide.md)
|
|
91
|
+
- [CLI reference](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/cli-reference.md)
|
|
92
|
+
- [Architecture and algorithms](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/architecture-and-algorithms.md)
|
|
93
|
+
- [Platform support](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/platform-support.md)
|
|
94
|
+
- [Development and release guide](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/development.md)
|
|
95
|
+
- [Changelog](https://github.com/GGN-2015/gui-remote-controll/blob/main/CHANGELOG.md)
|
|
96
|
+
- [Security policy](https://github.com/GGN-2015/gui-remote-controll/blob/main/SECURITY.md)
|
|
97
|
+
|
|
98
|
+
The Python import package is `gui_remote_controll`. The PyPI distribution and command name
|
|
99
|
+
intentionally follow the repository spelling, `gui-remote-controll`.
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
[MIT](https://github.com/GGN-2015/gui-remote-controll/blob/main/LICENSE)
|
|
104
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# GUI Remote Controll
|
|
2
|
+
|
|
3
|
+
[中文](https://github.com/GGN-2015/gui-remote-controll/blob/main/README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
GUI Remote Controll shares a Windows, Linux, or macOS desktop with a web browser. The
|
|
6
|
+
browser receives screen frames and can redirect mouse, touch, wheel, keyboard, text input,
|
|
7
|
+
and plain-text clipboard events to the server computer.
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
Python 3.10 or newer is required.
|
|
12
|
+
|
|
13
|
+
1. Install the package:
|
|
14
|
+
|
|
15
|
+
```console
|
|
16
|
+
pip install gui-remote-controll
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
2. Start a PIN-protected server:
|
|
20
|
+
|
|
21
|
+
```console
|
|
22
|
+
gui-remote-controll --pin 123456
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Startup requests administrator/root privileges once. Use `--no-elevate` only when the
|
|
26
|
+
desktop permissions have already been configured or elevation is inappropriate.
|
|
27
|
+
|
|
28
|
+
3. Open the control page:
|
|
29
|
+
|
|
30
|
+
- On the server computer: `http://127.0.0.1:8000`
|
|
31
|
+
- On the local network: `http://SERVER_LAN_IP:8000`
|
|
32
|
+
|
|
33
|
+
The default bind address is `0.0.0.0`. Always set `--pin` when another device can reach the
|
|
34
|
+
server. Use TLS or an encrypted tunnel outside a trusted LAN, and never expose the default
|
|
35
|
+
HTTP service directly to the public internet.
|
|
36
|
+
|
|
37
|
+
### First-run permissions
|
|
38
|
+
|
|
39
|
+
- **Windows:** accept the UAC prompt. Run the server from the signed-in interactive session.
|
|
40
|
+
- **Linux:** use an X11 desktop session. Native Wayland does not permit portable global
|
|
41
|
+
capture and input injection.
|
|
42
|
+
- **macOS:** grant Screen Recording and Accessibility permissions to the terminal or Python
|
|
43
|
+
executable, then restart the server. Root access does not replace these permissions.
|
|
44
|
+
|
|
45
|
+
See the [platform guide](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/platform-support.md)
|
|
46
|
+
for setup and troubleshooting details.
|
|
47
|
+
|
|
48
|
+
## Documentation
|
|
49
|
+
|
|
50
|
+
- [Documentation index](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/README.md)
|
|
51
|
+
- [Complete user guide](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/user-guide.md)
|
|
52
|
+
- [CLI reference](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/cli-reference.md)
|
|
53
|
+
- [Architecture and algorithms](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/architecture-and-algorithms.md)
|
|
54
|
+
- [Platform support](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/platform-support.md)
|
|
55
|
+
- [Development and release guide](https://github.com/GGN-2015/gui-remote-controll/blob/main/docs/development.md)
|
|
56
|
+
- [Changelog](https://github.com/GGN-2015/gui-remote-controll/blob/main/CHANGELOG.md)
|
|
57
|
+
- [Security policy](https://github.com/GGN-2015/gui-remote-controll/blob/main/SECURITY.md)
|
|
58
|
+
|
|
59
|
+
The Python import package is `gui_remote_controll`. The PyPI distribution and command name
|
|
60
|
+
intentionally follow the repository spelling, `gui-remote-controll`.
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
[MIT](https://github.com/GGN-2015/gui-remote-controll/blob/main/LICENSE)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# GUI Remote Controll
|
|
2
|
+
|
|
3
|
+
[English](README.md)
|
|
4
|
+
|
|
5
|
+
GUI Remote Controll 可以把 Windows、Linux 或 macOS 桌面共享到浏览器,并把鼠标、触摸、
|
|
6
|
+
滚轮、键盘、输入法文本和纯文本剪贴板事件重定向到服务端电脑。
|
|
7
|
+
|
|
8
|
+
## 快速开始
|
|
9
|
+
|
|
10
|
+
需要 Python 3.10 或更高版本。
|
|
11
|
+
|
|
12
|
+
1. 安装:
|
|
13
|
+
|
|
14
|
+
```console
|
|
15
|
+
pip install gui-remote-controll
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
2. 启动带 PIN 保护的服务器:
|
|
19
|
+
|
|
20
|
+
```console
|
|
21
|
+
gui-remote-controll --pin 123456
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
程序默认只请求一次管理员/root 权限。只有在已经单独配置桌面权限,或当前环境不适合提权
|
|
25
|
+
时,才使用 `--no-elevate`。
|
|
26
|
+
|
|
27
|
+
3. 打开控制页面:
|
|
28
|
+
|
|
29
|
+
- 服务端电脑:`http://127.0.0.1:8000`
|
|
30
|
+
- 局域网设备:`http://服务端局域网IP:8000`
|
|
31
|
+
|
|
32
|
+
默认监听地址是 `0.0.0.0`。只要其他设备可以访问,就必须设置 `--pin`。离开可信局域网时,
|
|
33
|
+
应使用 TLS 或加密隧道,不要把默认 HTTP 服务直接暴露到公网。
|
|
34
|
+
|
|
35
|
+
### 首次运行权限
|
|
36
|
+
|
|
37
|
+
- **Windows:**接受 UAC 提示,并从已登录用户的交互式会话启动。
|
|
38
|
+
- **Linux:**使用 X11 桌面会话;原生 Wayland 不允许通用的全局画面抓取和输入注入。
|
|
39
|
+
- **macOS:**为终端或 Python 可执行文件授予“屏幕录制”和“辅助功能”权限,然后重启服务。
|
|
40
|
+
root 权限不能代替这些隐私授权。
|
|
41
|
+
|
|
42
|
+
详细设置与排错见[平台支持手册](docs/platform-support.md)。
|
|
43
|
+
|
|
44
|
+
## 完整手册
|
|
45
|
+
|
|
46
|
+
- [手册索引](docs/README.md)
|
|
47
|
+
- [完整用户手册](docs/user-guide.md)
|
|
48
|
+
- [CLI 参数手册](docs/cli-reference.md)
|
|
49
|
+
- [架构、算法与协议手册](docs/architecture-and-algorithms.md)
|
|
50
|
+
- [平台支持手册](docs/platform-support.md)
|
|
51
|
+
- [开发与发布手册](docs/development.md)
|
|
52
|
+
- [版本变更记录](CHANGELOG.md)
|
|
53
|
+
- [安全策略](SECURITY.md)
|
|
54
|
+
|
|
55
|
+
Python 导入包名为 `gui_remote_controll`;PyPI 分发包和命令名沿用仓库拼写
|
|
56
|
+
`gui-remote-controll`。
|
|
57
|
+
|
|
58
|
+
## 许可证
|
|
59
|
+
|
|
60
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes are applied to the latest released version.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Do not open a public issue for an undisclosed vulnerability. Use GitHub's private security
|
|
10
|
+
advisory feature for this repository, or contact the repository owner privately.
|
|
11
|
+
|
|
12
|
+
Include the affected version, platform, configuration, reproduction steps, impact, and any
|
|
13
|
+
suggested mitigation. Please allow a reasonable time for investigation before disclosure.
|
|
14
|
+
|
|
15
|
+
## Security model
|
|
16
|
+
|
|
17
|
+
GUI Remote Controll gives authenticated clients the ability to observe and operate the signed-in
|
|
18
|
+
desktop. Its PIN protects the web boundary; it does not provide end-to-end encryption. Use TLS
|
|
19
|
+
or an encrypted tunnel whenever traffic leaves a trusted network.
|
|
20
|
+
|
|
21
|
+
The server intentionally does not persist the PIN, authentication cookies, frames, keystrokes,
|
|
22
|
+
or clipboard text. Authentication tokens are signed with a random process-local secret and
|
|
23
|
+
become invalid after restart. Login failures are rate-limited in memory.
|
|
24
|
+
|
|
25
|
+
Automatic clipboard synchronization is disabled by default because clipboard text may contain
|
|
26
|
+
passwords, tokens, private messages, or other sensitive data. Enabling it authorizes plain-text
|
|
27
|
+
clipboard contents to move in both directions while the browser tab is active. Use HTTPS or an
|
|
28
|
+
encrypted tunnel so that clipboard data is not exposed in transit.
|
|
29
|
+
|
|
30
|
+
Administrator/root elevation increases impact if the service is exposed incorrectly. The server
|
|
31
|
+
requests elevation at most once per startup and supports `--no-elevate`, but operators remain
|
|
32
|
+
responsible for restricting network reachability and choosing a strong PIN.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Documentation
|
|
2
|
+
|
|
3
|
+
This directory contains the complete GUI Remote Controll manual. Start with the user guide
|
|
4
|
+
unless you are looking for a specific command-line option or implementation detail.
|
|
5
|
+
|
|
6
|
+
| Manual | Contents |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| [User guide](user-guide.md) | Browser controls, sessions, displays, input, clipboard, view-only mode, reconnection, TLS, and operational workflows. |
|
|
9
|
+
| [CLI reference](cli-reference.md) | Every public command-line option, defaults, validation rules, and deployment examples. |
|
|
10
|
+
| [Architecture and algorithms](architecture-and-algorithms.md) | Process lifecycle, authentication, capture pipeline, frame deduplication, coordinate mapping, input algorithms, HTTP routes, and WebSocket protocol. |
|
|
11
|
+
| [Platform support](platform-support.md) | Windows, Linux/X11 desktop environments, Wayland limitations, macOS permissions, and platform troubleshooting. |
|
|
12
|
+
| [Development and release](development.md) | Local setup, repository layout, tests, linting, CI, building, artifact inspection, and PyPI release steps. |
|
|
13
|
+
| [Security policy](../SECURITY.md) | Threat model, operational requirements, and vulnerability reporting. |
|
|
14
|
+
| [Changelog](../CHANGELOG.md) | User-visible changes by package version. |
|
|
15
|
+
|
|
16
|
+
## Support boundary
|
|
17
|
+
|
|
18
|
+
GUI Remote Controll controls the currently signed-in interactive desktop. It is not a service
|
|
19
|
+
for the Windows secure desktop, pre-login screens, native Wayland compositors, DRM-protected
|
|
20
|
+
content, system audio, file transfer, or unattended operating-system installation.
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# Architecture and Algorithms
|
|
2
|
+
|
|
3
|
+
## System model
|
|
4
|
+
|
|
5
|
+
GUI Remote Controll is a bitmap remote-control system. It does not mirror a native window tree
|
|
6
|
+
or expose remote DOM elements. The server captures desktop pixels, encodes them as JPEG, and
|
|
7
|
+
injects validated browser input into the signed-in operating-system session.
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
flowchart LR
|
|
11
|
+
Browser["Browser UI"] -->|"JSON control messages"| WS["FastAPI WebSocket"]
|
|
12
|
+
WS --> Validator["Protocol validator"]
|
|
13
|
+
Validator --> Desktop["Desktop backend"]
|
|
14
|
+
Desktop --> OS["Signed-in desktop"]
|
|
15
|
+
OS --> Capture["MSS capture"]
|
|
16
|
+
Capture --> JPEG["Pillow JPEG encoder"]
|
|
17
|
+
JPEG --> Dedup["BLAKE2b frame check"]
|
|
18
|
+
Dedup -->|"changed binary frames"| WS
|
|
19
|
+
WS --> Browser
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Package layout
|
|
23
|
+
|
|
24
|
+
| Module | Responsibility |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| `gui_remote_controll.__main__` | CLI parsing, validation, elevation, browser opening, and Uvicorn startup. |
|
|
27
|
+
| `gui_remote_controll.config` | Validated runtime settings and protocol size limits. |
|
|
28
|
+
| `gui_remote_controll.auth` | PIN comparison, signed Cookie tokens, and login attempt limiting. |
|
|
29
|
+
| `gui_remote_controll.protocol` | Strict normalization and validation of client JSON messages. |
|
|
30
|
+
| `gui_remote_controll.desktop` | Screen enumeration/capture, JPEG encoding, input injection, clipboard, DPI, and platform diagnostics. |
|
|
31
|
+
| `gui_remote_controll.app` | HTTP routes, security headers, backend lifecycle, connection gate, and WebSocket loops. |
|
|
32
|
+
| `gui_remote_controll.static` | Packaged browser UI with no CDN or runtime frontend dependency. |
|
|
33
|
+
|
|
34
|
+
## Process lifecycle
|
|
35
|
+
|
|
36
|
+
1. Parse arguments and validate the TLS file pair.
|
|
37
|
+
2. Unless disabled or already attempted, relaunch through `py-admin-launch`.
|
|
38
|
+
3. Convert parsed values to `Settings` and validate ranges.
|
|
39
|
+
4. Create the FastAPI application and Uvicorn server.
|
|
40
|
+
5. Serve the login UI, control UI, health endpoint, and static files without opening the desktop.
|
|
41
|
+
6. On the first accepted WebSocket, initialize the shared desktop backend in a worker thread.
|
|
42
|
+
7. Enumerate displays and start one receive loop and one capture loop for the client.
|
|
43
|
+
8. Cancel the peer loop and release held inputs when either loop finishes.
|
|
44
|
+
|
|
45
|
+
Lazy desktop initialization keeps CLI help, health checks, packaging, and PIN authentication
|
|
46
|
+
usable even when the process is temporarily outside a graphical session.
|
|
47
|
+
|
|
48
|
+
## Elevation algorithm
|
|
49
|
+
|
|
50
|
+
The public CLI implements a single-relaunch invariant:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
if no_elevate or elevation_attempted:
|
|
54
|
+
start server
|
|
55
|
+
else:
|
|
56
|
+
launch([python, -m, gui_remote_controll,
|
|
57
|
+
--elevation-attempted, *original_arguments], wait=True)
|
|
58
|
+
exit parent
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The marker is inserted by the program rather than stored in process environment, so it survives
|
|
62
|
+
the platform launcher without requiring environment mutation support. Even when the first
|
|
63
|
+
process is already administrator/root, `py-admin-launch` directly launches the marked child and
|
|
64
|
+
the invariant remains at most one launch call.
|
|
65
|
+
|
|
66
|
+
## Authentication algorithm
|
|
67
|
+
|
|
68
|
+
### PIN comparison
|
|
69
|
+
|
|
70
|
+
PIN values are compared with `hmac.compare_digest` over UTF-8 bytes. This avoids ordinary
|
|
71
|
+
short-circuit string comparison behavior.
|
|
72
|
+
|
|
73
|
+
### Cookie token
|
|
74
|
+
|
|
75
|
+
At application creation, the server generates a random 32-byte secret. With a configured PIN:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
token = HMAC-SHA256(process_secret, pin_utf8)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The hexadecimal token is stored in `gui_remote_auth` with:
|
|
82
|
+
|
|
83
|
+
- `HttpOnly` enabled;
|
|
84
|
+
- `SameSite=Strict`;
|
|
85
|
+
- path `/`;
|
|
86
|
+
- a maximum age of seven days;
|
|
87
|
+
- `Secure` enabled when direct TLS is configured.
|
|
88
|
+
|
|
89
|
+
The secret is never persisted, so all authentication cookies become invalid after restart.
|
|
90
|
+
Without a configured PIN, request and WebSocket authentication checks allow access.
|
|
91
|
+
|
|
92
|
+
### Failed-login limiter
|
|
93
|
+
|
|
94
|
+
Failures are stored in an in-memory deque per client IP. Before each attempt, timestamps at or
|
|
95
|
+
before `now - 60 seconds` are discarded. A client with five remaining timestamps is rejected
|
|
96
|
+
until one expires. Successful authentication clears that client's deque.
|
|
97
|
+
|
|
98
|
+
This is a process-local rolling-window defense, not a replacement for firewall or proxy rate
|
|
99
|
+
limiting on an internet-facing deployment.
|
|
100
|
+
|
|
101
|
+
## HTTP security boundary
|
|
102
|
+
|
|
103
|
+
Every HTTP response receives:
|
|
104
|
+
|
|
105
|
+
- a Content Security Policy restricted to packaged same-origin resources plus WebSocket
|
|
106
|
+
connections and blob/data images;
|
|
107
|
+
- `Cross-Origin-Opener-Policy: same-origin`;
|
|
108
|
+
- `Referrer-Policy: no-referrer`;
|
|
109
|
+
- `X-Content-Type-Options: nosniff`;
|
|
110
|
+
- `X-Frame-Options: DENY`.
|
|
111
|
+
|
|
112
|
+
The main and authentication pages use `Cache-Control: no-store`. Static file access uses a fixed
|
|
113
|
+
filename allowlist rather than arbitrary path resolution.
|
|
114
|
+
|
|
115
|
+
Authentication redirect targets must start with one `/`, must not start with `//`, and are
|
|
116
|
+
truncated to 2,048 characters.
|
|
117
|
+
|
|
118
|
+
## WebSocket admission
|
|
119
|
+
|
|
120
|
+
Admission checks run before the socket is accepted:
|
|
121
|
+
|
|
122
|
+
1. Validate the PIN Cookie, closing with `4401` on failure.
|
|
123
|
+
2. Validate Origin, closing with `4403` on failure.
|
|
124
|
+
3. Reserve a connection slot, closing with `4429` when full.
|
|
125
|
+
4. Accept the socket.
|
|
126
|
+
5. Initialize the desktop and close with `4500` if it is unavailable.
|
|
127
|
+
|
|
128
|
+
An absent Origin is allowed for non-browser clients. A browser Origin is accepted when its
|
|
129
|
+
network location matches the HTTP Host header or when the complete normalized origin appears in
|
|
130
|
+
the configured trusted-origin set.
|
|
131
|
+
|
|
132
|
+
## Connection and concurrency model
|
|
133
|
+
|
|
134
|
+
`BackendManager` creates one desktop backend per FastAPI application and protects creation with
|
|
135
|
+
an `asyncio.Lock`. Blocking desktop operations run through `asyncio.to_thread`, so capture,
|
|
136
|
+
clipboard, and native input calls do not block the event loop.
|
|
137
|
+
|
|
138
|
+
Each accepted client has:
|
|
139
|
+
|
|
140
|
+
- one `ClientState` with its display selection and held keys/buttons;
|
|
141
|
+
- one frame streaming task;
|
|
142
|
+
- one message receiving task;
|
|
143
|
+
- one lock serializing JSON and binary WebSocket sends.
|
|
144
|
+
|
|
145
|
+
The first completed task cancels the other. Cleanup decrements the connection counter and asks
|
|
146
|
+
the backend to release all inputs still held by that client.
|
|
147
|
+
|
|
148
|
+
## Display model
|
|
149
|
+
|
|
150
|
+
MSS returns an ordered monitor collection:
|
|
151
|
+
|
|
152
|
+
- monitor `0` is the bounding rectangle of all monitors;
|
|
153
|
+
- monitor `N > 0` is a physical display.
|
|
154
|
+
|
|
155
|
+
A `Screen` contains `index`, `left`, `top`, `width`, `height`, and `name`. Negative `left` or
|
|
156
|
+
`top` values are valid when a monitor is positioned to the left or above the primary display.
|
|
157
|
+
|
|
158
|
+
On Windows, per-monitor DPI awareness is requested before importing input libraries. MSS is
|
|
159
|
+
imported before `pynput` to keep capture and input coordinate systems aligned.
|
|
160
|
+
|
|
161
|
+
## Capture and frame algorithm
|
|
162
|
+
|
|
163
|
+
The capture loop runs once per WebSocket:
|
|
164
|
+
|
|
165
|
+
1. Select the current monitor from the client's state.
|
|
166
|
+
2. Acquire BGRA pixels through a thread-local MSS instance.
|
|
167
|
+
3. Convert MSS RGB bytes to a Pillow RGB image.
|
|
168
|
+
4. Encode JPEG with the configured quality and chroma subsampling.
|
|
169
|
+
5. Compute `BLAKE2b(frame_bytes, digest_size=8)`.
|
|
170
|
+
6. Send display metadata when the selected monitor changes.
|
|
171
|
+
7. Send the JPEG as a binary WebSocket message only when its digest differs from the last sent
|
|
172
|
+
frame.
|
|
173
|
+
8. Sleep for `max(0.001, 1/fps - capture_elapsed)`.
|
|
174
|
+
|
|
175
|
+
Hashing encoded bytes means the comparison reflects exactly what the client would receive. The
|
|
176
|
+
64-bit digest is used as a fast change detector rather than a security primitive. A theoretical
|
|
177
|
+
collision would skip one changed frame; a later differing frame would resume transmission.
|
|
178
|
+
|
|
179
|
+
Thread-local MSS instances avoid sharing a platform capture object across worker threads. The
|
|
180
|
+
instance remains associated with that worker thread for reuse.
|
|
181
|
+
|
|
182
|
+
## Pointer coordinate mapping
|
|
183
|
+
|
|
184
|
+
The browser measures the rendered image rectangle and converts a pointer event into normalized
|
|
185
|
+
coordinates:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
nx = clamp((client_x - image_left) / image_width, 0, 1)
|
|
189
|
+
ny = clamp((client_y - image_top) / image_height, 0, 1)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The server converts them to absolute desktop coordinates:
|
|
193
|
+
|
|
194
|
+
```text
|
|
195
|
+
x = screen.left + round(nx * max(0, screen.width - 1))
|
|
196
|
+
y = screen.top + round(ny * max(0, screen.height - 1))
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Using normalized values makes Fit and 1:1 modes equivalent and supports negative multi-monitor
|
|
200
|
+
origins. Subtracting one keeps `1.0` inside the final pixel rather than one pixel beyond the
|
|
201
|
+
display.
|
|
202
|
+
|
|
203
|
+
## Pointer movement and buttons
|
|
204
|
+
|
|
205
|
+
The browser sends at most one pending pointer movement per animation frame. Down/up events carry
|
|
206
|
+
`left`, `middle`, or `right`. The server first moves the native pointer to the mapped coordinate,
|
|
207
|
+
then applies the button operation.
|
|
208
|
+
|
|
209
|
+
Both browser and server track held buttons. Browser blur, pointer cancellation, WebSocket
|
|
210
|
+
disconnect, and server cleanup release them so a lost connection does not leave a native button
|
|
211
|
+
pressed.
|
|
212
|
+
|
|
213
|
+
## Wheel accumulation
|
|
214
|
+
|
|
215
|
+
Browser deltas are normalized to a bounded range of -20 to 20. The backend keeps two fractional
|
|
216
|
+
remainders:
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
remainder_x -= incoming_dx
|
|
220
|
+
remainder_y -= incoming_dy
|
|
221
|
+
step_x = trunc(remainder_x)
|
|
222
|
+
step_y = trunc(remainder_y)
|
|
223
|
+
remainder_x -= step_x
|
|
224
|
+
remainder_y -= step_y
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Only nonzero integer steps are passed to `pynput`. The sign inversion converts browser-positive
|
|
228
|
+
down/right deltas to the controller's native convention.
|
|
229
|
+
|
|
230
|
+
## Keyboard and text algorithms
|
|
231
|
+
|
|
232
|
+
The browser does not send ordinary printable characters as key events. Instead, a hidden text
|
|
233
|
+
input collects regular and composed text and sends it through `text`. Named keys and modified
|
|
234
|
+
shortcuts use `key` down/up messages.
|
|
235
|
+
|
|
236
|
+
This avoids duplicate input from the common sequence of keydown, composition events, and input.
|
|
237
|
+
Ctrl/Cmd+V is handled as local pasted text instead of forwarding both a remote shortcut and the
|
|
238
|
+
local clipboard contents.
|
|
239
|
+
|
|
240
|
+
The backend maps browser named keys to `pynput.keyboard.Key` and single characters to
|
|
241
|
+
`KeyCode.from_char`. Repeated keydown messages are not injected again. The native operating
|
|
242
|
+
system provides repeat behavior while the key remains held.
|
|
243
|
+
|
|
244
|
+
## Clipboard algorithm
|
|
245
|
+
|
|
246
|
+
Clipboard get/set operations run in worker threads through `pyperclip`. Text is limited to
|
|
247
|
+
1,000,000 characters. A get truncates an unexpectedly longer native clipboard value; the
|
|
248
|
+
protocol validator rejects an oversized set before calling the backend.
|
|
249
|
+
|
|
250
|
+
Clipboard operations are independent of view-only input control. They are completely rejected
|
|
251
|
+
only when clipboard synchronization is disabled.
|
|
252
|
+
|
|
253
|
+
### Automatic client state machine
|
|
254
|
+
|
|
255
|
+
Automatic synchronization begins with independent client and server baselines, so enabling it
|
|
256
|
+
does not immediately overwrite either clipboard. The client then tracks the last observed text
|
|
257
|
+
from both sides. A new local value is sent to the server and optimistically becomes the expected
|
|
258
|
+
server value. A new server value is written locally while an `applyingServerClipboard` guard
|
|
259
|
+
suppresses the resulting browser change event.
|
|
260
|
+
|
|
261
|
+
If the client changes before the first server baseline response arrives, a baseline-pending flag
|
|
262
|
+
causes that stale response to be ignored. This makes active local input win the only interval in
|
|
263
|
+
which ordering is otherwise unknowable.
|
|
264
|
+
|
|
265
|
+
The client prefers the standard `clipboardchange` event. When it is unavailable, a one-second
|
|
266
|
+
poll reads the client clipboard. Server polling continues in either case, but only while the page
|
|
267
|
+
is visible and focused. Browser permission loss disables the switch instead of silently running
|
|
268
|
+
one-way synchronization.
|
|
269
|
+
|
|
270
|
+
### Digest negotiation
|
|
271
|
+
|
|
272
|
+
Server clipboard text is hashed as `BLAKE2b(UTF8(text), digest_size=8)`. Automatic
|
|
273
|
+
`clipboard_get` messages include the last known hexadecimal digest:
|
|
274
|
+
|
|
275
|
+
- a match returns `clipboard_unchanged` with only the digest;
|
|
276
|
+
- a mismatch or missing digest returns `clipboard` with text and digest.
|
|
277
|
+
|
|
278
|
+
This digest is a bandwidth optimization, not an authentication primitive. `clipboard_set`
|
|
279
|
+
requests carry a short `requestId`; acknowledgements echo it so the UI can distinguish automatic
|
|
280
|
+
writes from explicit manual writes.
|
|
281
|
+
|
|
282
|
+
## Client-to-server protocol
|
|
283
|
+
|
|
284
|
+
All client messages are JSON objects. The maximum encoded message size is 1,100,000 bytes.
|
|
285
|
+
Numbers must be finite; booleans are not accepted as numeric values.
|
|
286
|
+
|
|
287
|
+
| Type | Fields | Meaning |
|
|
288
|
+
| --- | --- | --- |
|
|
289
|
+
| `pointer` | `event`: `move/down/up`, `x`, `y`, and `button` for down/up | Absolute normalized pointer event. |
|
|
290
|
+
| `wheel` | `dx`, `dy` in `[-20, 20]` | Normalized scroll delta. |
|
|
291
|
+
| `key` | `event`: `down/up`, `key`, `code`, `repeat` | Named key or modified character event. |
|
|
292
|
+
| `text` | `text` | Ordinary or composed text to type. |
|
|
293
|
+
| `monitor` | nonnegative integer `index` | Change this connection's captured display. |
|
|
294
|
+
| `clipboard_get` | optional `knownDigest` | Read server clipboard text, or confirm that a known digest is current. |
|
|
295
|
+
| `clipboard_set` | `text`, optional `requestId` | Replace server clipboard text. |
|
|
296
|
+
| `ping` | none | Application-level liveness request. |
|
|
297
|
+
|
|
298
|
+
Unknown types, invalid ranges, excessive strings, NaN, infinity, and malformed JSON produce an
|
|
299
|
+
`error` response without terminating the connection.
|
|
300
|
+
|
|
301
|
+
## Server-to-client protocol
|
|
302
|
+
|
|
303
|
+
JPEG frames are binary messages. All other messages are JSON.
|
|
304
|
+
|
|
305
|
+
| Type | Important fields | Meaning |
|
|
306
|
+
| --- | --- | --- |
|
|
307
|
+
| `hello` | `protocol`, `platform`, `viewOnly`, `clipboard`, `screens`, `monitor` | Initial capabilities and display list. Current protocol is `1`. |
|
|
308
|
+
| `screen` | screen fields | Metadata for the binary frames that follow. |
|
|
309
|
+
| `clipboard` | `text`, `digest` | Changed or explicitly requested server clipboard text. |
|
|
310
|
+
| `clipboard_unchanged` | `digest` | The supplied server clipboard digest is still current. |
|
|
311
|
+
| `clipboard_saved` | `digest`, optional `requestId` | Confirmation of `clipboard_set`. |
|
|
312
|
+
| `pong` | none | Response to `ping`. |
|
|
313
|
+
| `error` | `message` | Recoverable protocol or desktop operation error. |
|
|
314
|
+
| `fatal` | `message` | Desktop capture cannot continue. |
|
|
315
|
+
|
|
316
|
+
## HTTP route contract
|
|
317
|
+
|
|
318
|
+
| Route | Authentication | Purpose |
|
|
319
|
+
| --- | --- | --- |
|
|
320
|
+
| `GET /healthz` | no | Returns `status` and package `version`. |
|
|
321
|
+
| `GET /` | PIN Cookie when enabled | Packaged control UI. |
|
|
322
|
+
| `GET /auth` | no | Packaged PIN form. |
|
|
323
|
+
| `POST /auth` | rate-limited PIN | Sets the authentication Cookie. |
|
|
324
|
+
| `POST /logout` | no | Deletes the authentication Cookie. |
|
|
325
|
+
| `GET /api/status` | PIN Cookie when enabled | Runtime version, platform, modes, and client counts. |
|
|
326
|
+
| `GET /static/{filename}` | no | Serves four allowlisted CSS/JavaScript assets. |
|
|
327
|
+
| `WS /ws` | PIN Cookie and Origin | Screen and control protocol. |
|
|
328
|
+
|
|
329
|
+
## Extension points
|
|
330
|
+
|
|
331
|
+
`create_app` accepts a desktop backend factory. A replacement backend implements initialize,
|
|
332
|
+
screen listing, capture, input execution, input release, and clipboard methods. Tests use this
|
|
333
|
+
boundary to run the full HTTP and WebSocket stack without capturing or controlling a real
|
|
334
|
+
desktop.
|