⚡ Zyquo Cloud Web
Every cloud model. One beautiful chat. Entirely in your browser.
The browser edition of Zyquo Cloud — 12 AI providers, 170 models, zero backend. Bring your own keys; nothing ever leaves your device.
📚 Table of Contents
- ✨ Features
- 🛰 The Provider Fleet
- 🌐 Live Demo
- 📸 Screenshots
- 🔒 Privacy & Security Model
- 🧱 Tech Stack
- 🚀 Getting Started
- 📜 Available Scripts
- 🗂 Project Structure
- ☁️ Deployment
- ✅ Verification
- 🗺 Roadmap
- 🤝 Contributing
- 📄 License
- 👤 Author
✨ Features
Everything below is implemented and verified in this codebase — no vaporware.
💬 Chat engine
- Streaming & non-streaming chat against 12 providers through two faithfully
ported wire clients: one OpenAI
/chat/completionsclient (11 providers + custom endpoints, every quirk handled) and one native Anthropic Messages API client — overfetch+ReadableStreamwith a hand-rolled incremental SSE parser (event:/data:blocks, keep-alive comments,[DONE], trailing-event flush). - Token-by-token rendering with a blinking caret, live elapsed time +
tok/s, Stop (
AbortControllerteardown) and Continue. - Reasoning streams normalized from every dialect:
reasoning_content,reasoning, Mistral's array-of-thinking-chunks, DashScopeenable_thinking, Anthropicthinkingblocks — shown in a collapsible Thought process panel. - Perplexity citations (
citations+search_resultsmerged) rendered as numbered, clickable chips. - Auto-retry with backoff on 429/5xx (honors
Retry-After), typed human-readable errors that name the provider, and a CORS-shaped network error that explains the proxy/Zyquo Router fallback. - Auto-titling after the first exchange using the provider's cheapest non-reasoning model (native Zyquo Cloud behavior, ported verbatim).
🎛 The Model Menu
- Command-palette-style picker: fuzzy search across name / model ID /
provider, filter chips (Vision · Tools · Reasoning · JSON · Long context
· Cheapest), Favorites ⭐ / Recents / Aliases groups, keyed-providers
first, full keyboard navigation (
↑↓,↵use here,⌘↵set default). - Every row shows capability badges (👁 🧠 🛠), context window, and $/1M-token pricing straight from the catalog; un-keyed providers get an inline "Add key" affordance.
🧪 Power features
- Multi-model compare — broadcast one prompt to 2–4 columns, each streaming independently with its own stop/copy/regenerate, then promote the best answer into your thread.
- Variants & branching — regenerate keeps the previous answer as a
swipeable variant (
‹ 2/3 ›); branch a whole conversation from any message; edit & resend forks from an earlier user turn. - ⌘K command palette — actions (new chat, compare, focus mode, summarize, duplicate), jump-to-conversation, model switch, personas, templates.
/slash commands — quick tools (/summarize,/translate,/rewrite,/explain) + the full library with{{input}}substitution.- 8 personas and 56 prompt templates (8 categories × 7), ported verbatim from native Zyquo Cloud.
- Vision attachments (drag/drop/paste images → base64 parts) and text file attachments (injected as fenced context blocks), gated by each model's real capabilities.
- Read-aloud (Web Speech TTS) and dictation (SpeechRecognition).
- Token & cost HUD — per-message and per-conversation token counts and cost estimates from catalog pricing, plus a context-window usage bar with auto-trim of the oldest turns at 90% capacity (system prompt kept).
- Per-conversation parameters — temperature, top-p, max tokens, penalties, reasoning effort, extended thinking — the panel only shows controls the active model actually supports; unchecked = provider default, omitted from the request entirely.
📝 Rendering
- GitHub-flavored Markdown (tables with zebra rows, task lists, blockquotes with accent bar), syntax-highlighted code blocks with language label, copy and wrap toggles, KaTeX math, and Mermaid diagrams (the ~1 MB renderer lazy-loads only when a diagram appears).
💾 Local-first persistence
- Conversations in IndexedDB (schema-versioned, dependency-free wrapper),
keys and settings in namespaced
localStorage(zyquo.cloud.web.*) — surviving reloads and full browser restarts (gate-tested). - Export / import everything as JSON (keys strictly opt-in with a warning), per-conversation Markdown/JSON export, confirm-twice clear-data, undo for deletes.
- Optional passphrase lock: the key map encrypted at rest with AES-GCM (PBKDF2, 310k iterations) — honestly framed as at-rest protection, not a running-session vault.
🎨 Design
- The ZyquoTheme design system, ported token-for-token from the native
macOS app — light flagship theme (
#FAFBFD/ sky-indigo#4E6AF0), derived dark theme, 5 accent choices, adjustable chat font size and density, focus mode,prefers-reduced-motionrespected. - Installable PWA (app-shell precache, 1.15 MB) and fully responsive — the sidebar becomes a drawer on phones, code blocks scroll internally, 44 pt touch targets.
🛰 The Provider Fleet
| Provider | Models | Wire format | Streaming | Vision | Reasoning | Citations |
|---|---|---|---|---|---|---|
| 🟢 OpenAI | 27 | OpenAI compat | ✅ | ✅ | ✅ | — |
| 🟠 Anthropic | 11 | Messages API | ✅ | ✅ | ✅ | — |
| ⚫ xAI | 5 | OpenAI compat | ✅ | ✅ | ✅ | — |
| 🔵 Mistral | 10 | OpenAI compat | ✅ | ✅ | ✅ | — |
| 🔴 Google Gemini | 14 | OpenAI compat | ✅ | ✅ | ✅ | — |
| 🟣 Alibaba Qwen | 32 | OpenAI compat | ✅ | ✅ | ✅ | — |
| 🌀 DeepSeek | 2 | OpenAI compat | ✅ | — | ✅ | — |
| 🌙 Kimi (Moonshot) | 12 | OpenAI compat | ✅ | ✅ | ✅ | — |
| 🔍 Perplexity | 4 | OpenAI compat | ✅ | — | ✅ | ✅ |
| 🧩 Together AI | 16 | OpenAI compat | ✅ | — | ✅ | — |
| 📦 DeepInfra | 34 | OpenAI compat | ✅ | ✅ | ✅ | — |
| ⚡ Cerebras | 3 | OpenAI compat | ✅ | ✅ | ✅ | — |
All 12 call the provider APIs directly from your browser — CORS
feasibility was empirically probed per provider (preflight + authenticated
POST + streaming), documented with official sources in
docs/CORS-MATRIX.md. Anthropic requires its
documented anthropic-dangerous-direct-browser-access: true header, which the
client sends. A per-provider base-URL override (Settings → Providers &
Keys) lets you point any provider at your own proxy or a locally running
Zyquo Router if a policy ever changes.
🌐 Live Demo
| URL | Notes | |
|---|---|---|
| 🚀 Production | https://www.zyquo.cloud | Primary deployment — HTTPS, security headers, installable PWA |
| 🪞 GitHub Pages mirror | https://spboucher-ai.github.io/zyquo-cloud-web/ | Auto-deployed from main by CI |
Open it, add any provider API key in Settings → Providers & Keys (it stays in your browser), pick a model, chat. That's the whole onboarding.
📸 Screenshots
| Light (flagship) | Dark |
|---|---|
![]() |
![]() |
| The Model Menu | ⌘K Command Palette |
|---|---|
![]() |
![]() |
| 📱 Mobile |
|---|
![]() |
🔒 Privacy & Security Model
This app has no backend, no accounts, and no telemetry — by design.
- 🔑 Bring-your-own-key: your API keys live in
localStorageon your device and are sent only to the provider you call, directly. - 🧾 Honest first-run notice: browser storage is not a secure vault — anyone with your browser profile (or a script running on the page) could read it. The app says so instead of pretending otherwise.
- 🛡 Strict CSP:
connect-srcis locked to exactly the 12 provider origins +localhost(for Zyquo Router). No inline scripts, no third-party scripts,object-src 'none',frame-ancestors 'none'. - 🕵️ Audited: during the full 169-model verification run, the page contacted the 12 provider origins and localhost — nothing else.
- 🔐 Optional passphrase lock: AES-GCM (WebCrypto) encryption of the key map at rest, PBKDF2-derived key, wrong-passphrase rejection tested.
- 📦 Dependency-light on purpose: every dependency is key-theft surface — no icon packs, no CSS framework, no IndexedDB wrapper lib, no auth SDKs.
🧱 Tech Stack
| Layer | Choice | Why |
|---|---|---|
| ⚛️ UI | React 19 + TypeScript (strict) | noUncheckedIndexedAccess, project references, zero any leaks |
| ⚡ Build | Vite 7 | code-split (Mermaid/KaTeX lazy), hashed assets, 288 kB gzip core bundle |
| 🗃 State | Zustand 5 | one store: conversations, streaming, settings, UI |
| 🎨 Styling | CSS custom properties (ZyquoTheme tokens) | no Tailwind/framework — tokens ported 1:1 from the native macOS app |
| 📝 Markdown | react-markdown + remark-gfm + rehype-highlight + KaTeX + Mermaid (lazy) | GFM, math, diagrams, Zyquo-mapped syntax theme |
| 🌊 Networking | hand-rolled SSE parser over fetch/ReadableStream |
one parser for OpenAI-style and Anthropic named events |
| 💾 Storage | IndexedDB (own 80-line wrapper) + namespaced localStorage |
schema versioning, no dependency |
| 📲 PWA | vite-plugin-pwa (Workbox) | app-shell precache only — never API responses or keys |
| 🧪 Testing | Playwright (Chromium/WebKit/Firefox) | live gate scripts: streaming, persistence, full provider matrix |
🚀 Getting Started
Prerequisites
- Node.js ≥ 20 and npm
- At least one provider API key (OpenAI, Anthropic, xAI, Mistral, Gemini, DashScope, DeepSeek, Moonshot, Perplexity, Together, DeepInfra or Cerebras)
Install & run
git clone https://github.com/spboucher-ai/zyquo-cloud-web.git
cd zyquo-cloud-web
npm install
npm run dev # → http://localhost:5173Open the app, go to Settings → Providers & Keys, paste a key, hit Test, pick a model — done.
Environment variables
The app itself needs none — keys are entered in the UI and stored in the browser. Build-time only:
| Variable | Purpose | Default |
|---|---|---|
VITE_BASE |
Base path for subpath hosting (e.g. GitHub Pages sets /zyquo-cloud-web/) |
/ |
⚠️ Never put API keys in
.envfiles here — there is no server to hide them in. The.gitignoreblocks.env*and the local test-key file anyway.
📜 Available Scripts
| Script | What it does |
|---|---|
npm run dev |
Vite dev server on :5173 |
npm run build |
Type-check (project refs) + production build + PWA assets |
npm run preview |
Serve the production build locally |
npm run typecheck |
tsc -b --noEmit |
npm run lint |
ESLint (flat config) over src/ |
npm run check |
typecheck + lint + build — the CI gate |
node scripts/server.mjs 8080 dist |
Zero-dependency production server (CSP/HSTS headers, SPA fallback, /healthz) |
node scripts/gate-phase2.mjs |
Live streaming gate in headless Chromium |
node scripts/gate-phase3.mjs |
13-check persistence gate (reload + browser restart + vault) |
node scripts/verify-matrix.mjs |
Full provider/model verification matrix (needs real keys in .keys.local.json) |
node scripts/verify-browsers.mjs |
Chromium/WebKit/Firefox responsive smoke |
🗂 Project Structure
zyquo-cloud-web/
├── index.html # CSP meta, PWA/social meta
├── vite.config.ts # Vite + PWA (app-shell precache only)
├── src/
│ ├── App.tsx # Shell: sidebar + chat, global shortcuts
│ ├── types/ # Provider, AIModel, Conversation, Message, Settings
│ ├── providers/ # 🔌 ALL networking lives here
│ │ ├── registry.ts # 12-provider metadata + client resolution
│ │ ├── openaiCompatible.ts# one client, 11 providers + custom, every quirk
│ │ ├── anthropic.ts # native Messages API + browser CORS header
│ │ ├── sse.ts # fetch/ReadableStream SSE parser + retry
│ │ ├── catalog.ts # the complete 170-model typed catalog
│ │ └── testKey.ts # key validation (/models or min completion)
│ ├── storage/ # 💾 the ONLY place touching localStorage/IndexedDB
│ │ ├── keys.ts # namespaced key map (+ vault overlay)
│ │ ├── vault.ts # AES-GCM passphrase lock
│ │ ├── db.ts # dependency-free IndexedDB wrapper
│ │ ├── conversations.ts # CRUD + schema migration + search
│ │ ├── settings.ts # theme, default model, proxy overrides
│ │ └── backup.ts # export / import / clear-everything
│ ├── state/store.ts # Zustand: send/stream/stop/regenerate/branch/title
│ ├── features/ # personas, prompt library, compare, exporters, trim
│ ├── components/ # Sidebar, ChatView, ModelMenu, CompareView, …
│ └── design/ # ZyquoTheme tokens (CSS vars), components.css
├── scripts/ # prod server + live verification gates
├── docs/ # PLAN, PROVIDER-REUSE, CORS-MATRIX, VERIFICATION, NATIVE-UX
└── .github/workflows/ # CI + GitHub Pages deployTwo hard architectural invariants, enforced by sweep: all network I/O is in
providers/, all persistence is in storage/ — components stay
presentational.
☁️ Deployment
The build is 100% static — host it anywhere.
GitHub Pages (automated)
Every push to main triggers deploy-pages.yml:
build with VITE_BASE=/zyquo-cloud-web/ → deploy to
https://spboucher-ai.github.io/zyquo-cloud-web/.
Self-host (any box, 30 seconds)
npm run build
node scripts/server.mjs 8080 distserver.mjs is a zero-dependency Node server that adds the full security
header set (CSP, HSTS preload, nosniff, Referrer-Policy, Permissions-Policy),
immutable caching for hashed assets, SPA fallback, and /healthz.
Production (www.zyquo.cloud)
The primary instance runs the same server.mjs under PM2 behind an
ngrok custom domain:
pm2 start server.mjs --name zyquo-cloud-web -- 8080 dist
pm2 start "ngrok http --url=www.zyquo.cloud 8080" --name zyquo-cloud-ngrok
pm2 saveStatic hosts
Cloudflare Pages / Netlify / Vercel: build command npm run build, output
dist/. Add the security headers from scripts/server.mjs via your host's
headers config for parity.
✅ Verification
This app was verified live, with real keys, from a real browser — full
report in docs/VERIFICATION.md:
| Check | Result |
|---|---|
| 🌊 Streaming, token-by-token | 169 / 169 tested models ✅ |
| 📬 Non-streaming completion | 12 / 12 providers ✅ |
⏹ Stop / AbortController |
12 / 12 providers ✅ |
| 👁 Vision (base64 image parts) | 9 / 9 vision-capable providers ✅ |
| 🧠 Reasoning deltas | all thinking models ✅ |
| 🔗 Perplexity citations | ✅ (19 chips on one query) |
| 🌍 CORS direct-from-browser | 12 / 12 ✅ |
| 💾 Persistence (reload + restart + vault) | 13 / 13 checks ✅ |
| 🧭 Cross-browser (Chromium/WebKit/Firefox) | 15 / 15 checks ✅ |
| 🕵️ Network audit | provider origins + localhost, nothing else ✅ |
🗺 Roadmap
- Function calling / tools UI (the catalog already tracks
toolscapability) - Model aliases editor in Settings (aliases already surface in the Model Menu)
- Client-side PDF export (Markdown/JSON exports ship today)
- Transcript virtualization for 1,000+ message threads
- Conversation tags & archive surfaces (data model ready)
- i18n (FR first)
🤝 Contributing
Contributions are welcome!
- Fork and create a branch:
git checkout -b feat/my-feature - Keep the invariants: networking only in
providers/, persistence only instorage/, design tokens only (no raw hex in components), and the file header on every code file. npm run checkmust be green.- Open a PR with a clear description.
🔐 Never commit keys.
.keys.local.json,.env*and friends are gitignored; CI contains a history secret-scan precedent.
📄 License
Released under the MIT License — © 2026 Simon-Pierre Boucher.
👤 Author
Simon-Pierre Boucher
📫 Contact: contact@spboucher.ai
Zyquo Cloud Web is part of the Zyquo family — native Zyquo Cloud (macOS), Zyquo Router (local gateway), and friends.
⭐ If this project is useful to you, a star makes my day. ⭐




