Commit Graph
21 Commits
Author SHA1 Message Date
yuanyuanxianganddeepseek-flash a522424491 Feature: Security audit logging with admin web query page
Add a dedicated security audit trail to the Go server. Login attempts,
authorization denials, WebSocket lifecycle, remote screen and terminal
sessions, clipboard operations, device authentication and admin mutations
now emit stable JSON-line events (audit=true, event=<name>) to a separate
logs/audit.log, mirrored to server.log. The path is configurable through
YAMA_AUDIT_LOG and the separate file can be switched off. Records never
contain passcodes, password hashes, tokens or clipboard contents, and
untrusted string fields are stripped of control characters and truncated
so they cannot forge log lines.

Expose the trail to administrators through a query_audit WebSocket command
and an Audit Log page in the web UI. The page filters by time range, event
(exact or prefix), actor, IP, device id, keyword and warning level, pages
newest-first, and stays hidden unless the server advertises the auditLog
capability, so older C++ servers degrade cleanly. Each query scans at most
the newest 32 MB of the current file to bound latency, records itself as
admin.audit.query, and tolerates torn or non-audit lines.

Add unit tests for event emission and redaction, query filtering, paging,
malformed lines, missing or disabled audit files, and handler
authorization.

Co-Authored-By: deepseek-flash
2026-10-18 18:44:17 +02:00
yuanyuanxianganddeepseek-flash b1ddec1b8a Feature: Offline device history with delete and remote uninstall
Persist every device that has ever logged in to the Go server in a small
JSON store (devices.json, overridable with YAMA_DEVICES_FILE and capped by
YAMA_DEVICES_MAX, default 1000 newest). The store follows the users.json
convention: atomic temp-file write with fsync and rename, a 500 ms debounce
for bursts of reconnects, first_seen preserved across logins, and pruning
that evicts offline records before devices that are still connected.
Devices connected at shutdown are stamped offline before the final flush so
last_seen stays accurate across restarts.

The server advertises an offlineDevices capability and device_list now
appends historical devices that are not online, marked with online:false and
last_seen. The web UI shows an Offline checkbox after Total/Showing (state
kept in localStorage), renders offline cards dimmed and dashed, and always
lists them in the screen-wall sidebar. Older servers without the capability
keep the online-only list.

Grid and List cards gain a delete button after the terminal button, admin
only, behind a confirmation dialog. Deleting an offline device removes its
history record; deleting an online device first sends the single-byte
TOKEN_UNINSTALL command (matching the MFC OnOnlineUninstall flow) and then
removes the record too, so the device does not come back as a dead offline
entry. The agent's own disconnect then runs the existing cleanup path:
screen and terminal sub-connections are closed, the hub entry is removed,
and browsers receive device_offline to release their watch sets, wall
sessions, and stream references. Wall mode exposes no delete action.

Tests cover history persistence/pruning/recovery, offline ordering, the
device_list merge, and all three delete outcomes (offline removal, online
uninstall with a single 0x3F byte, and viewer rejection).

Co-Authored-By: deepseek-flash
2026-10-18 17:54:30 +02:00
yuanyuanxianganddeepseek-flash 1922f87dd2 Feature: Multi-device screen wall on the Go server
Bring the Go web backend up to parity with the MFC server's screen wall.
A browser tab can now stream several devices over one WebSocket: the
single `watching` field becomes a per-connection watch set guarded by the
hub mutex, capped at 32 devices per socket. The challenge advertises
`caps.multiWatch` and `caps.keyframeRequest`, binary frames stay keyed by
the low 32 bits of the device ID, and connect/disconnect replies echo the
device id and request id so the wall can route concurrent responses.

Input commands (mouse, key, clipboard, rdp_reset) resolve their target
from the watch set: an explicit id must be a current subscription, and an
absent id is accepted only while exactly one device is watched. Legacy
frontends that do not send `multiWatch` keep replace-all semantics, so a
repeated device switch cannot grow the set without bound. Offline
devices are removed from every watch set, and teardown is computed from a
snapshot taken outside the hub lock to avoid re-entrant locking.

Add the `keyframe` command: it replays the cached IDR, then asks the
device to emit a fresh one, rate-limited to one request per device per
second. This exposed a Go-only corruption bug: the client answers the
request with TOKEN_KEYFRAME (134), which the dispatcher treated as a
no-op. That token only appears on forced IDRs because the client's GOP is
effectively infinite, so the dropped IDR left the browser decoding
P-frames against a reference chain it never received, producing
persistent garbling. Relay it as a keyframe instead. Also drop the
cached IDR when the resolution changes, mirroring the C++ server, so a
late joiner cannot receive an old-geometry keyframe.

Add tests for the watch-set transitions and for relaying TOKEN_KEYFRAME.

Co-Authored-By: deepseek-flash
2026-10-18 11:51:28 +02:00
yuanyuanxianganddeepseek-v4-pro 0c04f7516b Feature: Web remote desktop bidirectional text clipboard sync
Add keyboard-shortcut (Ctrl+C / Ctrl+X / Ctrl+V) text clipboard transfer
between the browser and the remote desktop, reusing the clipboard protocol
that already exists in the native viewer (COMMAND_SCREEN_GET_CLIPBOARD=24,
COMMAND_SCREEN_SET_CLIPBOARD=25, TOKEN_CLIPBOARD_TEXT=118) so the client
needs no change.

Local -> remote: pressing Ctrl+V reads the browser clipboard, pushes it to
the remote clipboard (UTF-8 -> GBK, matching the client's CF_TEXT/ANSI
handling) and then synthesizes a remote Ctrl+V so the pasted text is the
newly delivered one.

Remote -> local: Ctrl+C or Ctrl+X is forwarded to the remote as usual and,
after a short delay, the remote clipboard is pulled back and written to the
browser clipboard for a later local Ctrl+V.

Both the MFC and Go servers get the same three clipboard signaling paths
(clipboard_set, clipboard_get, clipboard_text) so the shared single-page
frontend behaves identically on either backend.

Co-Authored-By: deepseek-v4-pro
2026-10-17 19:52:38 +02:00
yuanyuanxianganddeepseek-v4-pro a447452521 Fix: Silence SOCKET_DLLLOADER log flood and tighten device-port logging
SOCKET_DLLLOADER (210) is the shellcode / memory-DLL loader asking the
server for the full DLL payload before it can load the client. The MFC
master console serves it via SendServerDll; the Go web device port has no
payload to hand out, so every memory-DLL client startup/reconnect fell
into the default dispatch branch and logged "Unhandled command 210",
flooding the log. Add a protocol constant and drop the command silently.

While auditing the device-port logging surface:

- Downgrade wrapTLS's per-connection hex-dump lines and the screen-spy
  "[timing]" cold-start line from Info to Debug (they fire once per TCP
  connection / screen view and are diagnostics, not operational events).

- Distinguish a sub-connection hijack attempt from a plain orphan: when a
  screen/terminal sub-conn claims a MasterID from a source IP other than
  the device's login conn, log a dedicated Warn instead of mislabeling it
  as "orphan ... unknown device".

Co-Authored-By: deepseek-v4-pro
2026-10-16 12:26:35 +02:00
yuanyuanxianganddeepseek-v4-pro 4ff965a2b4 Feat: White-label web UI branding for subpath deployments
When the web remote desktop is mounted under another domain's subpath
(YAMA_WEB_BASE_PATH non-empty), read the product title from
YAMA_WEB_BRAND_NAME, substitute it for the SimpleRemoter titles and
PWA manifest name, and strip the security notice and website links.
The brand name is JSON- and HTML-escaped so operator input can't
produce malformed output. Served from the domain root, the official
SimpleRemoter branding is left unchanged.

Co-Authored-By: deepseek-v4-pro
2026-10-15 19:43:58 +02:00
yuanyuanxianganddeepseek-v4-pro 8e0f1283c6 Security: Add per-IP connection and login rate limiting
The TCP device port is exposed directly to the public internet, so a single
host must not be able to exhaust the global connection budget, flood new
connection attempts, or brute-force the device authorization passcode.

- server: cap concurrent connections and the accept rate per source IP
  (defaults 256 concurrent / 120 per minute, overridable via
  YAMA_PERIP_MAX_CONNS and YAMA_PERIP_CONN_RATE). Rejections happen at
  accept time, before any buffer or goroutine is allocated, and a rejected
  device simply retries on its reconnect loop.
- cmd: throttle TOKEN_AUTH per source IP (120/min) and answer over-limit
  attempts with a normal failed response so clients get a definitive reply.
- web: periodically sweep the login rate-limit maps so the username-keyed
  one (which is attacker-controlled) cannot grow without bound.

Per-IP defaults are generous enough that a NAT office whose devices share
one source IP is unaffected.

Co-Authored-By: deepseek-v4-pro
2026-10-13 16:25:34 +02:00
yuanyuanxianganddeepseek-v4-pro 53b7151fb7 Feature: Add admin-only SSO bridge to web UI
Bridge an already-authenticated portal session into a YAMA web session via
a new /sso endpoint, so portal administrators can enter the remote-desktop
UI without a second login. Only the "admin" portal role is bridged; any
other role (viewer, unknown) still falls back to the built-in login form,
so a low-privileged portal account cannot bypass authentication.

- wsauth: extract IssueToken from VerifyLogin so an out-of-band-authenticated
  identity (SSO) can mint a session token without the challenge/response.
- web: add the SSODetector interface, a fail-closed JWT detector (HMAC-only),
  and the /sso handler gated to the admin role.
- cmd: enable the JWT detector via YAMA_SSO_JWT_SECRET, the shared HS256
  secret matching the parent portal.
- web UI: attempt SSO before showing the login form, falling back to login
  when the server returns 401.
- docs: document SSO (admin-only) and license trial-vs-paid troubleshooting
  in README and the env example.

Co-Authored-By: deepseek-v4-pro
2026-10-13 15:46:04 +02:00
yuanyuanxianganddeepseek-v4-pro f3a05c14b1 Feature: Play remote audio on iPhone via WebAudio + self-hosted Opus WASM
iOS WebKit lacks Media Source Extensions with WebM/Opus, so remote audio
was silent on iPhone Safari/Chrome even though audio frames kept flowing.
Add a Web Audio API fallback path that decodes Opus packets with the
`opus-decoder` WASM library and schedules them onto an AudioContext.

The decoder bundle is now self-hosted — served at /static/opus-decoder.min.js
by both the Go and C++ servers — instead of pulled from a CDN, which was
unreachable from some networks.

Gotcha fixed during bring-up: the unscoped `opus-decoder` build's
`OpusDecoder.decodeFrame` is synchronous and returns {channelData,
samplesDecoded, sampleRate, errors} directly, not a Promise, so it must be
called in try/catch rather than via .then().

Also remove the on-screen diagnostic overlay (audio toast + frame counter)
used while debugging the phone without a console; diagnostics now go to
console.* only.

Co-Authored-By: deepseek-v4-pro
2026-10-03 18:23:10 +02:00
yuanyuanxianganddeepseek-v4-pro 42df128f62 Feature: Support subpath deployment for Go web UI
Add optional YAMA_WEB_BASE_PATH to mount the whole web UI (index, manifest,
static assets, /health, /api/devices, /ws) under a URL prefix such as
/remoter behind a reverse proxy. index.html now uses a __BASE_PATH__
placeholder substituted at serve time, the PWA manifest start_url is rooted
at the base path, and a mount wrapper redirects the bare prefix to prefix/
and strips it before routing. Include nginx, systemd, and env-file deployment
examples for the subpath setup.

The shared placeholder is also handled by the MFC server, which serves from
the domain root and strips __BASE_PATH__ (to "") so its web remote desktop
URLs stay rooted.

Co-Authored-By: deepseek-v4-pro
2026-09-11 12:48:21 +02:00
yuanyuanxiang f146af121a Fix: prevent mobile web remote desktop freeze on Safari app-switch
by forcing clean WS reconnect on foreground return;

add 30-second grace period on server to avoid cold-start on quick reconnects
2026-07-10 16:08:02 +02:00
yuanyuanxiang 498c7d15b3 Feature(web): Add toolbar audio toggle button 2026-06-02 20:52:20 +02:00
yuanyuanxiang 268a427172 Go:Add build pipeline for go server and fix web login bug 2026-05-27 08:47:21 +02:00
yuanyuanxiang 1c1bb3a5ff Fix(Go): notify browsers with device_offline so the webpage not frozen 2026-05-19 21:48:09 +02:00
yuanyuanxiang d757c33bcb Fix(Go): stable device list ordering + RDP-reset handler
Fix UTF-8 login text decode + stale screen sub-conn retirement
2026-05-19 16:28:32 +02:00
yuanyuanxiang 32a75f4670 Security(Go): Login rate limit + WS origin allowlist + REST bearer auth 2026-05-18 22:06:07 +00:00
yuanyuanxiang d7f38ecfdb Feature(Go): Web terminal relay with PTY mode and graceful close (Phase 6) 2026-05-18 22:06:07 +00:00
yuanyuanxiang 6485e800d6 Feature(Go): Mouse/keyboard input + user management with users.json (Phase 5 + 7) 2026-05-18 22:06:07 +00:00
yuanyuanxiang fba4143dd1 Feature(Go): Screen frame relay end-to-end with graceful client BYE (Phase 4) 2026-05-18 22:06:07 +00:00
yuanyuanxiang 4ea6ed252c Feature(Go): Web auth, WebSocket signaling and live device list (Phase 3) 2026-05-18 22:06:07 +00:00
yuanyuanxiang 534d3650c4 Feature(Go): Embed and serve web UI assets 2026-05-18 22:06:07 +00:00