Commit Graph
6 Commits
Author SHA1 Message Date
debpalash 794942faf4 Merge current main into GHAS empty-except fixes 2026-08-10 12:17:43 +00:00
Palash Debnath 27a8f477b7 fix(security): keep private diagnostics out of API responses (#1454)
* fix(security): keep private diagnostics out of API responses

* docs: reference response-safety PR

* fix(security): preserve constant recovery guidance

* fix(security): keep recovery and logs data-independent

* fix(security): close remaining response sinks

* test(security): keep SOCKS diagnostics private

* fix: keep Tailscale exceptions local

* fix: keep Tailscale CLI output private
2026-08-10 12:01:12 +00:00
debpalash 7a7dc422a6 fix: keep degraded state truthful 2026-08-10 08:22:37 +00:00
Palash DebnathandClaude Opus 4.8 c2869e73d5 feat(network): user-configurable backend / LAN-share / UI ports (#163)
* feat(ports): make backend/share/UI ports configurable via env vars

Single-source the backend port from OMNIVOICE_PORT and derive the LAN-share
base from it (OMNIVOICE_SHARE_PORT override). Previously network_share.py
hardcoded BACKEND_PORT=3900, so a user running the backend on a custom port
got LAN-share/Tailscale pointed at the wrong port.

- network_share: replace BACKEND_PORT constant with backend_port() /
  share_port_base() helpers (env-driven, never-throw fallback to defaults);
  enable() probes from share_port_base().
- tailscale: serve_enable(port=None) defaults to network_share.backend_port().
- main.py: direct-run + --health-check ports and HEALTH_URL read OMNIVOICE_PORT;
  CORS default origins use OMNIVOICE_UI_PORT (default 3901).
- /system/info + SystemInfoResponse: expose backend_port, share_port_base,
  ui_port (both success and never-throw except branches).
- set-env: persist OMNIVOICE_PORT/SHARE_PORT/UI_PORT; validate numeric and
  1024-65535, reject otherwise with 400.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ports): pin child OMNIVOICE_PORT in backend spawn

Push OMNIVOICE_PORT=backend_port() onto the spawned Python child's env so
network_share.backend_port() always agrees with the uvicorn --port Rust
passes. Without this, a user-set OMNIVOICE_PORT would move the LAN-share /
Tailscale target while the listener stayed on the Rust-resolved port.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ports): Ports subsection in Sharing settings + env-driven Vite port

- vite.config.js: dev-server port reads OMNIVOICE_UI_PORT (default 3901).
- SharingPanel: new Ports subsection reads /system/info and displays
  backend_port / ui_port (with their env-var names + restart-to-apply note)
  and makes the LAN-share port editable — Save POSTs OMNIVOICE_SHARE_PORT to
  /system/set-env (persisted), "applies next time you enable sharing".
- Tests: extend SharingPanel.test.jsx for the ports subsection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 12:52:51 +05:30
Palash DebnathandClaude Opus 4.8 4af5d69111 fix(tailscale): HTTP serve fallback when tailnet lacks HTTPS certs + parallel dev launch (#161)
* fix(tailscale): serve over HTTP when tailnet has no HTTPS certs

Real-world failure: 'tailscale serve --https=443' on a tailnet without the
HTTPS Certificates feature (CertDomains: None) fails with 'error enabling
https feature: 404'. Detect cert availability from status --json and use
--https only when certs exist; otherwise serve over --http (the WireGuard
tunnel encrypts transport anyway). Also surface a clear note/error instead
of the raw 404, and a 'run tailscale up' hint when not running. Verified the
--http path live on a real tailnet. SharingPanel now shows the returned note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(dev): launch app in parallel with backend (drop wait:api gate)

dev/desktop no longer block the Tauri/vite launch on the API being HTTP-ready
— the window appears immediately and the frontend's setup-status check
already retries (30x1s) until the API answers. Matches prod, where the window
shows BootstrapSplash while the sidecar boots. Dev-only; no shipped change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 12:25:38 +05:30
Palash DebnathandClaude Opus 4.8 fa1503c4eb feat: network sharing (PIN-gated LAN + QR) & Tailscale remote access (#125) (#159)
* docs(spec): network sharing + Tailscale remote access design

Same-state LAN sharing via a second in-process uvicorn listener on a
dedicated share port (no restart, model/jobs preserved), PIN-gated for
non-loopback clients, with QR + all-LAN-addresses panel. Tailscale serve
for private remote access. Supersedes the raw 0.0.0.0 default-flip in #125.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(spec): control endpoints reuse existing require_loopback gate

Security review of #157 confirmed the /system router is already loopback-gated
via Depends(require_loopback) (non-spoofable request.client.host). The network
control endpoints inherit it and /system/set-env is auto-protected from the
LAN listener — no new guard needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(network): share-listener module — LAN enumeration + PIN + lifecycle

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(network): loopback-only control endpoints + /system/info sharing fields

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(cjk): scan git-tracked files only, not untracked vendored dirs

The no-hardcoded-CJK guard walked the filesystem, so local untracked
vendored experiments (research/voice-pro etc. with JP issue templates)
caused false local failures while CI (committed files) passed. Scan via
git ls-files so local-only and CI behavior match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(network): PIN middleware — gate non-loopback API access when sharing on

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(network): inject X-OmniVoice-Pin globally + capture ?pin= from QR URL

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(network): remote PIN gate on 401

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(network): add qrcode dep for share QR

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(network): footer Local/Network toggle with LAN addresses, QR, copy/open

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(tailscale): CLI status + serve enable/disable + endpoints

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(network): Settings → Sharing & Remote Access panel (LAN + Tailscale)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(network): sharing & remote access guide (LAN PIN/QR + Tailscale)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(network): enable() tears down and raises if the share listener never binds

Defensive guard (spec §7): if the second uvicorn server doesn't reach
'started' (e.g. the share port was taken in the race after the free-port
probe), cancel the task, reset state, and raise — so the API surfaces the
failure and the UI stays Local rather than reporting a dead 'Network' state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(network): use globalThis (not Node global) in client.test.ts for tsc

CI runs 'tsc --noEmit --checkJs false', which type-checks .ts files; Node's
'global' isn't typed there (TS2304). vitest (esbuild) tolerated it locally.
Use globalThis (standard, typed) + cast the mock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(network): apiFetch leaves opts untouched when no PIN set

The unconditional headers merge changed the request shape for callers with
no headers (e.g. FormData posts), breaking the legacy 'apiPost passes
FormData without Content-Type override' node test. Only spread opts +
inject X-OmniVoice-Pin when a PIN is actually present; otherwise pass opts
through unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 11:26:16 +05:30