If you discover a security vulnerability in Utsuwa, please report it by opening a GitHub issue or contacting the maintainer directly.
Please include:
- A description of the vulnerability
- Steps to reproduce
- Potential impact
- Any suggested fixes (if applicable)
Utsuwa stores API keys for LLM and TTS providers locally on your device. This means:
- Keys are stored locally on your device only
- Keys are not sent to any server other than the respective API providers
- Keys persist until you remove them in Settings or clear the app's data
Recommendations:
- Use Utsuwa on trusted devices only
- Consider using API keys with usage limits when possible
- Remove API keys in Settings if using a shared device
Utsuwa stores all your data locally on your device. When self-hosting, chat, model-fetching, and Fish Audio speech requests are proxied through SvelteKit server-side API routes before reaching the provider. With ALLOW_LOCAL_PROVIDER_HOSTS=true set, speech requests to a local TTS server (Local TTS or OmniVoice) that the browser cannot reach directly are proxied the same way, only ever to /v1/audio/speech under the configured base URL. Before proxying to a client-supplied base URL, the server resolves its host and refuses loopback, private, link-local, and cloud metadata addresses, and it never follows redirects. ALLOW_LOCAL_PROVIDER_HOSTS=true opens loopback and private ranges for self-hosters; link-local and metadata addresses stay blocked. No data is stored server-side. The server acts only as a pass-through. When using the hosted version at utsuwa.ai, these requests pass through the deployment server in the same way, limited to 240 API requests a minute per IP address.
MCP is off unless a self-hosted deployment sets MCP_ENABLED=server, and the /api/mcp/* routes are unauthenticated, so put an authenticating reverse proxy in front of any shared deployment. stdio servers are fail-closed: each MCP_STDIO_ALLOWED_COMMANDS entry is a full command line that a request must match exactly (a trailing * allows extra arguments), env vars from the client are dropped unless named in MCP_STDIO_ENV_ALLOWLIST, and variables that change how code loads (PATH, NODE_OPTIONS, LD_PRELOAD, BASH_ENV, PYTHONPATH, NPM_CONFIG_*, and similar) are never passed through. By default the app asks before each tool call and tells the model that tool results are untrusted data.
The desktop app has no standing read access to your files. It reads only files you drag into the window (the file system plugin adds dropped paths to its scope), and writes only to your Downloads folder.
On Linux, WebKitGTK has no microphone prompt of its own, so the desktop app grants microphone access to its own windows without asking. Recording only starts when you press the mic button or the push-to-talk shortcut. Camera and other permissions keep the webview's defaults. On macOS and Windows the system prompt still applies.
Server responses carry Content-Security-Policy: frame-ancestors 'none', X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, and a Permissions-Policy that allows the microphone and WebXR on the app's own origin only.
When you configure API keys, Utsuwa communicates directly with:
- LLM providers (OpenAI, Anthropic, Google, etc.)
- TTS providers (ElevenLabs, OpenAI, etc.)
Please review the privacy policies and terms of service of any providers you choose to use.
| Version | Supported |
|---|---|
| 0.x.x | ✅ |
Security updates will be released as needed. Watch the repository for notifications about important updates.