Renames what users see. The app, the installers, the window title, the
docs and all 21 locales now say VoiceStudio, with "(previously
OmniVoice-Studio)" noted near the title of each doc surface so people
recognise it.
Deliberately NOT renamed, because renaming any of them silently breaks
an existing install — there is no legacy-path fallback anywhere in this
codebase:
- bundle identifier com.debpalash.omnivoice-studio (MSI UpgradeCode,
macOS TCC grants, managed venv, WebView localStorage, the
single-instance lock)
- data directories OmniVoice / .omnivoice and omnivoice.db
- the ~150 OMNIVOICE_* environment variables
- the X-OmniVoice-* HTTP headers (a wire protocol)
- the published Docker image paths
- the OmniVoice ENGINE, which is a model name and not this product
tests/test_identity_paths_survive_the_rename.py pins every one of those
so a future well-meaning sweep cannot orphan a user's library.
Linux .deb users install a new package name and should apt remove
omnivoice-studio; that note is in the changelog.
* 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>
* 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>