This branch may be ahead of the latest release.
maincarries work that has not been built, signed or published yet, and its version numbers name the release being prepared rather than one you can download. What is released is on the releases page and at sovatela.eu; a version tagged here and then withdrawn is burned rather than reused, so a gap in the numbers is deliberate.
A small, friendly desktop chat client for Z.ai's GLM-5.2, served from Europe on Scaleway's Generative APIs. Built with Tauri v2 + Svelte 5.
Each user brings their own keys ("BYO-key"), stored in the OS keychain. The app has no backend of its own, and the publisher receives no telemetry, no conversations, no keys and no crash reports.
The client is local; the inference is not. The interface, your history and
memory, document generation and artifact rendering all run on your machine. The
model does not: chat, vision, search and image generation are calls to providers
you configure. Every network call goes to one of those providers, to
sovatela.eu when you press Check for updates, or to a page the model reads
while web search is on — and one of them is automatic, a connection check at
launch. Security lists all of them, with what is sent and when.
The project is built to keep the conversation data path in Europe and out of US big-tech, and to be self-reliant — no shared server to depend on. Every provider is brought by the user and billed to the user:
- Chat → GLM-5.2 (Z.ai) on Scaleway's EU infrastructure.
- Image understanding → a Mistral vision model (French) on Scaleway — GLM-5.2 is text-only.
- Image generation → OVHcloud (SDXL, French and EU-hosted) as the sovereign option, Black Forest Labs (German FLUX) for quality, or your own endpoint. BFL is the trade-off worth naming: better images, but a German-American company with a US entity that trains on your inputs by default. The app says so where you choose.
- Web search → Linkup (French search API), Qwant Staan (EU search API), or your own SearXNG. No US search APIs, no shared default.
- History & memory → stored on your device (or a folder you choose, e.g. your own cloud drive). Nothing is uploaded to the app's developer.
- Artifacts → generated by GLM-5.2, rendered locally in a sandbox.
Where that claim stops, named rather than implied. The data path above is the conversation. Around it: this project's site and its downloads are served by GitHub Pages (Microsoft), which sees a visitor's IP; Linkup lists Microsoft Azure among its subprocessors; Black Forest Labs has a US entity and trains on inputs by default; and terminal access runs an Anthropic agent, which is outside all of this and says so where it is offered. The model itself is Chinese — Z.ai's GLM-5.2, hosted in Paris. "European model" would be wrong; "European-hosted, under EU law" is the claim.
Ollama lists glm-5.2, which makes it look like you could run this model on
your own machine for free. You can't: the only tag is glm-5.2:cloud.
There are no weights to pull — GLM-5.2 is far too large for a laptop, so Ollama
runs it on Ollama's own servers, and you need an Ollama account and key
billed by Ollama.
That is a perfectly good product; it is just not a sovereign one. Ollama Inc. is a US company, and its privacy policy says data "may be transferred to and processed in the United States" and names its inference providers only generically. It does state that prompts are processed transiently and never trained on. So the choice for GLM-5.2 isn't local vs. hosted — both are hosted — it's whose cloud, under whose law:
glm-5.2:cloud in Ollama |
Sovatela | |
|---|---|---|
| Runs on | Ollama Inc.'s infrastructure | Scaleway, France |
| Jurisdiction | US; data may be processed in the US | EU |
| Inference sub-processor | Not disclosed | Named, single |
| Key & billing | Ollama account, billed by Ollama | Your Scaleway key, billed to you |
Where Ollama genuinely wins is its local models: a smaller open model running fully offline on your hardware is more private than anything that touches a network, costs nothing per token, and works on a plane. That's a different trade — a weaker model and real hardware requirements — and it's a good reason to use both tools. This app doesn't currently support a local endpoint for chat; see the Roadmap.
-
Streaming chat with GLM-5.2.
-
File uploads — attach documents (PDF,
.docx,.odt,.pptx,.xlsx— text extracted in Rust, in a separate process that is allowed to die so a hostile file cannot take the app with it), text/code (folded into context), and images (routed to the vision model). Nothing is uploaded to the developer; if chat recording is on, an attachment is saved beside the conversation like any other message content.Word and OpenDocument extraction covers the body and the headers, footers, footnotes and endnotes. Text deleted with Track Changes is left behind rather than sent. Comments are not read. A presentation is read in slide order; a spreadsheet keeps its rows and columns, and its sheet names.
-
Documents the assistant can write — ask for a Word document, a spreadsheet or a slide deck and it comes back as a real file you can open, built from Markdown by the app rather than written as a file format by the model. Save it from the conversation, or have it written into your workspace folder.
What "real file" means, and does not. These are genuine
.docx,.xlsxand.pptxfiles that open in Word, Excel and PowerPoint without a repair prompt — not renamed CSVs or HTML. They are also basic: a spreadsheet is one sheet with no formulas and no cell formatting, lists and tables are simplified, and only part of Markdown survives the conversion. If you are expecting a finished corporate deliverable, read what they carry and what they drop first — it is a first draft in the right file format, not a replacement for the application that opens it.Supply your own
.docx,.dotx,.pptxor.potxin Settings → Document templates and generated files come out in your fonts, colours, page size and page furniture; headings and lists use your template's own styles for them, where it defines any. Templates are checked when you choose one; those containing macros, linking to anything outside themselves, or carrying a field that fetches when the document is opened, are refused, and only pictures are taken from a template's media folder. What the conversion does not carry is listed in Technical specification § 7. -
Web search (toggle 🌐) — the model searches when you ask, streaming its answer. Four EU paths: a Linkup API key (French search API, self-serve sign-up with a free tier — the recommended default), a shared SearXNG server (paste URL + token), a local SearXNG (click-to-run, needs Docker), or a Qwant Staan key (note: Staan sign-up is currently business-only with a review process). Settings has a Save & test button that runs a real query.
-
Image generation (🎨) — OVHcloud (SDXL, the sovereign default), Black Forest Labs (FLUX, higher quality), or a custom OpenAI-images endpoint. No Docker or GPU needed. Off until you add a key. On FLUX you can attach pictures to generate from — a
flux-2-*model takes up to eight and holds their style across a new image (a matching icon set, one character in different scenes), while aflux-kontext-*model edits a single picture to your instruction. -
Artifacts — ask for a chart/diagram/visual/app and it renders live in a right-hand canvas panel (sandboxed). All code blocks open in the panel; a session-wide index lets you revisit any artifact.
-
Chat history — a sidebar (☰) of past conversations; reopen, delete, or start fresh. Toggle recording off, or choose where it's saved — including a folder your cloud drive syncs across devices.
-
Memory — tell it about you and how to respond once; it applies to every chat. Auto-memory proposes durable facts to remember when a chat wraps up, which you approve.
-
Projects — named containers with their own instructions and reference files, grouping related chats. The project's context is folded into each chat.
-
Quick start, Guide & Settings — Quick start is three numbered steps to a working setup, sharing its cards with the welcome screen so first-run and later wording can't drift; the Guide tours what the app can do; Settings is an accordion for the API key, memory, web search, image generation, chat history, appearance, terminal access, and privacy.
-
Accessibility — text scaling to 200% (Settings → Appearance), because a desktop app has no address bar to zoom with, and
prefers-reduced-motionis honoured throughout, including in scripted scrolling that CSS alone can't reach. Known gaps are documented rather than glossed.
- Rust backend (
src-tauri/src/lib.rs) holds the Scaleway key in the OS keychain (keyring) and makes HTTPS calls withreqwest— done in Rust to avoid browser CORS and keep the key out of the webview. - Streaming uses a Tauri
Channel: Rust parses Scaleway's SSE stream and pushes tokens (and search-status / artifact events) to the Svelte UI. - Model routing: text →
glm-5.2; any image in the conversation →mistral-small-3.2-24b-instruct-2506(vision). - Web search: when enabled, the model is forced to call a
web_searchtool; Rust queries Staan or SearXNG, feeds results back, and answers from them (multi-round, so it can search again). Today's date is injected so the model doesn't rely on stale training data. - Image generation: Rust submits to Black Forest Labs, polls until ready, and downloads the result into a data URL (BFL's URLs expire fast).
- Memory & projects: personalization, remembered facts, and the active project's instructions + files are assembled into a system prompt on every send — never stored inside the saved conversation, so they can't go stale.
- Artifacts render in
<iframe sandbox="allow-scripts">(no same-origin) + a strict CSP — generated code cannot reach Tauri, the keychain, or the network.
src/ Svelte 5 frontend
App.svelte routing: welcome / overview / quickstart / chat / settings
lib/KeyPage.svelte onboarding + Settings (key, memory, search, image, history)
lib/QuickStart.svelte three-step setup for chat, search and images
lib/Guide.svelte feature guide (shown in the Overview)
lib/Overview.svelte first-run overview tour (Settings links to it)
lib/Chat.svelte chat, uploads, web search, artifacts, history, projects
lib/History.svelte sidebar: projects + saved conversations
lib/ProjectPanel.svelte project editor (instructions + files)
lib/MemoryReview.svelte approve auto-captured memories
lib/Artifact.svelte sandboxed artifact renderer (auto-height)
src-tauri/ Rust backend (keychain, Scaleway, search, images, memory)
deploy/searxng-local/ click-to-run local SearXNG (private, per-user)
deploy/flux-litellm/ LiteLLM proxy → BFL EU FLUX (legacy; native BFL is built in)
.github/workflows/ CI: cross-platform release builds on version tags
Prerequisites: Node 20 (see .nvmrc; 22+ ships an experimental
global localStorage that collides with jsdom and fails the test suite), Rust (curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh), and platform build tools (macOS:
xcode-select --install; see https://tauri.app/start/prerequisites/).
npm install
npm run icon # generate app icons (first time only)
npm run tauri dev # compile Rust + launch (first build is slow)npm run test:rust # Rust: unit + send_chat mock-server scenarios
npm test # frontend: markup parsing + markdown sanitization (vitest)The backend scenario tests stand up a mock OpenAI-compatible server and never contact an external provider.
Cross-platform installers build via GitHub Actions on a version tag:
# bump version in package.json, src-tauri/tauri.conf.json, src-tauri/Cargo.toml
git tag vX.Y.Z && git push origin vX.Y.ZThis produces a draft release with a universal macOS .dmg (Intel + Apple
Silicon), Windows .msi/.exe, and Linux .deb/.AppImage/.rpm.
Development happens in a separate working repository, and this source tree is
published from it at each release. Builds are drafted there and are not
downloadable; publishing is a second, deliberate step. Signing is verified on
the drafted artifacts, and those same files — never a rebuild, which would
have different checksums — are uploaded to the public release on
jacobla1/sovatela, which is what the download page is built from. Full
procedure: deploy/web/README.md.
That split is why the checksums on the download page match the files on the release: nothing is compiled between verifying an artifact and publishing it.
The release workflow signs and notarizes the macOS build automatically when the GitHub secrets below are set. Until they are set the build is simply unsigned (users open via System Settings → "Open Anyway"), so the workflow is safe to run either way.
tauri-action degrades to unsigned rather than failing when the certificate
expires or a secret goes missing, and nothing in the CI log flags it — so a
release can ship unsigned while the docs still promise otherwise. Verify against
the published artifact, every time:
hdiutil attach Sovatela_X.Y.Z_universal.dmg
spctl -a -t exec -vv /Volumes/Sovatela/Sovatela.app # accepted / Notarized Developer ID
xcrun stapler validate /Volumes/Sovatela/Sovatela.app # ticket stapled — or it fails offline
hdiutil detach /Volumes/SovatelaOne-time setup:
-
Enrol in the Apple Developer Program (~$99/yr). Note your 10-character Team ID (Membership details).
-
Create a "Developer ID Application" certificate (Certificates →
+→ Developer ID Application). Download it, double-click to add it to your login keychain, then in Keychain Access export it (with the private key) as a.p12with an export password. -
Create an app-specific password at appleid.apple.com → Sign-In & Security → App-Specific Passwords (used for notarization).
-
Add these as repository secrets (Settings → Secrets and variables → Actions):
Secret Value APPLE_CERTIFICATEthe .p12, base64-encoded:base64 -i cert.p12 | pbcopyAPPLE_CERTIFICATE_PASSWORDthe .p12export password from step 2APPLE_SIGNING_IDENTITYDeveloper ID Application: Your Name (TEAMID)KEYCHAIN_PASSWORDany random string (a temp keychain is created in CI) APPLE_IDyour Apple account email APPLE_PASSWORDthe app-specific password from step 3 APPLE_TEAM_IDyour 10-character Team ID
Then push a tag as above — the macOS .dmg/.app is signed with the Developer
ID identity, notarized by Apple, and the ticket stapled, so Gatekeeper admits
it: the first launch shows the ordinary "downloaded from the internet" prompt
carrying "Apple checked it for malicious software", not the blocking
"developer cannot be verified" panel. (App Store Connect API-key notarization is an alternative
to the Apple ID method: swap APPLE_ID/APPLE_PASSWORD/APPLE_TEAM_ID for
APPLE_API_ISSUER/APPLE_API_KEY/APPLE_API_KEY_PATH.)
Windows builds are unsigned, and that is a decision rather than a backlog
item — SECURITY.md § Release integrity records it. Authenticode needs a
purchased OV/EV certificate or
Azure Trusted Signing, a
recurring cost the publisher is not willing to carry for a free application, so
the SmartScreen warning is permanent. If that ever changes it would be wired in
via tauri.conf.json → bundle.windows.
Linux packages don't use OS-level code signing either. What both platforms
do carry, from the first release built in the public repository, is a minisign
signature over SHA256SUMS.txt and a build attestation binding each installer
to the commit that produced it — neither of which is code signing, and neither
of which stops SmartScreen.
Nothing is required to launch except a Scaleway key for chat; the rest are optional, per-feature, and each brought by the user:
- Chat / image understanding / artifacts → a Scaleway account + API key.
- Image generation → an OVHcloud key (SDXL — French, EU-hosted, the sovereign choice), a Black Forest Labs key (FLUX — better quality, US entity, trains on inputs by default), or your own endpoint.
- Web search → one of, in increasing order of setup:
- a Linkup key — self-serve sign-up at app.linkup.so with a free tier, so it's the one option non-technical users can set up entirely alone.
- a shared SearXNG server someone technical runs for you — a token-gated instance behind nginx. Users just paste the URL + token; nothing to install. The server side is arranged with the maintainer directly, so the app offers the fields but never instructions for standing one up.
deploy/searxng-local/— double-clickstart.command(macOS) /start.bat(Windows) after installing Docker Desktop; the app defaults tohttp://localhost:8888with a blank token.- a Qwant Staan key — best UX once you have one, but sign-up currently goes through a business-only review, so personal users may not get access.
Full index: docs/README.md.
- Users — Install · Quick-start · FAQ · Troubleshooting · Uninstall & data deletion · Support
- Policies — Security · Accessibility · Privacy · Terms (published 2026-09-01; written by the publisher, not reviewed by a lawyer — the sections that needed one were removed rather than published unreviewed, and TERMS.md records what was dropped and why)
- Product — Product spec · UX spec · Technical spec
- Release — Announcement · Release notes · QA checklist · Legal checklist
- All keys — Scaleway, Linkup, Staan, BFL, and endpoint tokens — live in the OS keychain (plaintext copies from older versions are migrated on first launch) and are sent only to their own provider. Secrets are never echoed back to the UI.
- Chat history, memory, and projects are stored on the user's device (or a folder they choose); nothing goes to the app's developer. Settings can reveal the history folder and delete all stored data in one step.
- Artifacts run in an opaque-origin sandbox with a CSP that denies network, storage and navigation: generated code cannot reach your files, this app, or anything online. It is not isolated from the window's CPU and memory, so code that loops forever can still make the app unresponsive — which is why nothing runs until you press run on the artifact.
- A local/OpenAI-compatible chat endpoint (Ollama, llama.cpp, LM Studio) as an
alternative provider — the fully-offline end of the sovereignty scale.
ChatOptions.endpointis already a field, so this is a settings surface more than a rewrite.
Accessibility is deliberately not a roadmap item. Text scaling to 200%, keyboard shortcuts, focus management and landmark roles have shipped; screen-reader verification on each platform remains open and is not scheduled — the accessibility statement says so and records what is unknown, rather than promising a date. This section used to list that testing as if a pass were coming, which the statement itself had already stopped implying.
Terminal/agent use of GLM-5.2 is handled by pointing Claude Code at Scaleway through a local LiteLLM proxy (a
claude-glmlauncher), rather than a bespoke CLI — the former in-repoglm-chatCLI was removed in favour of it. Set it up from Settings → Advanced → Terminal access, or run the installers directly:deploy/claude-glm/. It reads the Scaleway key from the same keychain item as the desktop app — set the key once, both use it.Unofficial, and disclosed as such in the app: Anthropic's gateway documentation states it "doesn't support routing Claude Code to non-Claude models through any gateway". Sovereignty is also narrower here than in the desktop app, because Claude Code is an agent that runs commands which can reach hosts outside Europe.
Released under the MIT License — © 2026 Jacob Bergmann Larsen.
The MIT license covers this project's own source code. Bundled third-party components (the Inter font, the Svelte runtime, marked, DOMPurify, the Tauri framework and Rust crates, and the Lucide-derived icons) are redistributed under their own permissive licenses — see THIRD-PARTY-LICENSES.md for the full notices and an audited license summary. External services this app integrates with or configures (GLM-5.2, Scaleway, Black Forest Labs, SearXNG, Qwant Staan, Linkup, and others) are not distributed here and remain governed by their own licenses and terms.
This is an independent, unofficial project and is not affiliated with, sponsored by, or endorsed by Anthropic, Z.ai, Scaleway, Qwant, Black Forest Labs, Linkup, or any other company named here. "Claude", "Claude Code", "GLM", "Scaleway", "SearXNG", "Staan", "Linkup", and all other product and company names are trademarks of their respective owners; they are used here only descriptively, to identify the tools and services this app interoperates with.