SPB Git

spb/zyquo-router Public MIT

One local endpoint, every AI provider — a private OpenAI-compatible LLM gateway for your Mac (170 models, 12 providers).

Swift 95.7% Python 2.3% Shell 1.2% Makefile 0.9%

docs: ultra-detailed README with icon, badges, screenshots; MIT license; publication gitignore

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 12 days ago (Jul 31, 2026) parent 8b65336

Showing 6 changed files with +333 and −67

modified .gitignore +32 −4
@@ -1,9 +1,37 @@
1 +# Build products
1 2 .build/
3 +dist/
4 +build/
5 +DerivedData/
2 6 *.app
3 7 *.icns
4 AppIcon.iconset/
5 .DS_Store
6 .env.test-keys
7 dist/
8 8 *.zip
9 9 *.dmg
10 +AppIcon.iconset/
11 +
12 +# Xcode user state
13 +*.xcuserstate
14 +xcuserdata/
15 +*.xcworkspace/xcuserdata/
16 +*.pbxuser
17 +*.mode1v3
18 +*.mode2v3
19 +*.perspectivev3
20 +
21 +# Dependencies
22 +Pods/
23 +Carthage/Build/
24 +.swiftpm/
25 +
26 +# Secrets — never commit keys
27 +.env.test-keys
28 +.env*
29 +*.pem
30 +vault.zq
31 +
32 +# macOS
33 +.DS_Store
34 +.AppleDouble
35 +.LSOverride
36 +Icon?
37 +._*
added LICENSE +21 −0
@@ -0,0 +1,21 @@
1 +MIT License
2 +
3 +Copyright (c) 2026 Simon-Pierre Boucher
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in all
13 +copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 +SOFTWARE.
modified README.md +280 −63
@@ -1,121 +1,338 @@
1 1 <div align="center">
2 2
3 +<img src="assets/readme/app-icon.png" width="180" alt="Zyquo Router icon" />
4 +
3 5 # Zyquo Router
4 6
5 **Your Mac as a local LLM gateway — one OpenAI-compatible endpoint for every provider.**
7 +### One local endpoint. Every AI provider. Spec-exact.
8 +
9 +*Turn your Mac into a private LLM gateway — 170 models from 12 providers behind a single OpenAI-compatible API.*
10 +
11 +<br/>
12 +
13 +[![Release](https://img.shields.io/github/v/release/spboucher-ai/zyquo-router?style=for-the-badge&color=0891B2&label=Release)](https://github.com/spboucher-ai/zyquo-router/releases/latest)
14 +[![Platform](https://img.shields.io/badge/Platform-macOS%2013%2B-374151?style=for-the-badge&logo=apple&logoColor=white)](#-requirements)
15 +[![Swift](https://img.shields.io/badge/Swift-5.9%2B-F05138?style=for-the-badge&logo=swift&logoColor=white)](#%EF%B8%8F-building-from-source)
16 +[![License](https://img.shields.io/badge/License-MIT-2FA36B?style=for-the-badge)](LICENSE)
17 +[![Downloads](https://img.shields.io/github/downloads/spboucher-ai/zyquo-router/total?style=for-the-badge&color=4C2BE0&label=Downloads)](https://github.com/spboucher-ai/zyquo-router/releases)
18 +
19 +[![Notarized](https://img.shields.io/badge/Signed%20%26%20Notarized-Developer%20ID-0891B2?style=flat-square&logo=apple&logoColor=white)](#-installation)
20 +[![Universal](https://img.shields.io/badge/Universal-Apple%20Silicon%20%2B%20Intel-9BA3AA?style=flat-square)](#-installation)
21 +[![SwiftNIO](https://img.shields.io/badge/Server-SwiftNIO%202-E4E8EB?style=flat-square)](#%EF%B8%8F-architecture)
22 +[![No Electron](https://img.shields.io/badge/100%25%20Native-SwiftUI-0891B2?style=flat-square)](#%EF%B8%8F-architecture)
6 23
7 Native macOS · Swift + SwiftUI · built with SPM, no Xcode IDE
24 +<br/>
8 25
9 `Anthropic` · `OpenAI` · `xAI` · `Mistral` · `Gemini` · `Qwen` · `DeepSeek` · `Kimi` · `Perplexity` · `Together` · `DeepInfra` · `Cerebras`
26 +`Anthropic` · `OpenAI` · `xAI` · `Mistral` · `Google Gemini` · `Alibaba Qwen` · `DeepSeek` · `Kimi` · `Perplexity` · `Together AI` · `DeepInfra` · `Cerebras`
10 27
11 28 </div>
12 29
13 30 ---
14 31
15 Zyquo Router turns your Mac into a private, local LLM gateway. Store your provider API
16 keys once (encrypted AES-256-GCM vault — no Keychain), pick a port, hit **Start**, and
17 every tool that speaks the OpenAI API instantly gets **170 models from 12 providers**
18 through one normalized endpoint:
32 +## 📖 Table of Contents
33 +
34 +- [Why Zyquo Router](#-why-zyquo-router)
35 +- [Features](#-features)
36 +- [Screenshots](#-screenshots)
37 +- [Installation](#-installation)
38 +- [Requirements](#-requirements)
39 +- [Usage](#-usage)
40 +- [Architecture](#%EF%B8%8F-architecture)
41 +- [Building from Source](#%EF%B8%8F-building-from-source)
42 +- [Verification](#-verification)
43 +- [Roadmap](#-roadmap)
44 +- [Contributing](#-contributing)
45 +- [License](#-license)
46 +- [Author](#-author)
19 47
20 ```
21 http://localhost:8787/v1
22 ```
48 +---
23 49
24 Think *OpenRouter / LiteLLM — but native, local, private, and gorgeous.*
50 +## 💡 Why Zyquo Router
25 51
26 ## Why
52 +Every AI provider speaks a slightly different dialect. Anthropic wants `x-api-key` and
53 +content blocks; Gemini wants `contents/parts` and camelCase; Mistral renames `seed`;
54 +Perplexity ends its streams with a non-spec event. Your tools — SDKs, CLIs, IDE plugins,
55 +agents — mostly speak **one** dialect: the OpenAI API.
27 56
28 - **One request format, all providers.** Anthropic's Messages API and Gemini's
29 `generateContent` are translated bidirectionally into spec-exact OpenAI wire format —
30 streaming (byte-exact `chat.completion.chunk` SSE), tool calling, vision, JSON mode,
31 and reasoning included. The other nine providers get per-provider parameter
32 strip/rename/clamp tables and quirk normalization.
33 - **Private by design.** Keys live only in an encrypted vault on your Mac. The server
34 binds to localhost by default; LAN exposure requires a local API key. Logs are
35 redacted by default. No endpoint ever returns a key.
36 - **A control room, not a black box.** Live dashboard (requests/min, tokens, cost,
37 per-provider breakdown), full request inspector with a timing waterfall, model
38 aliases, fallback chains, a built-in playground, and in-app API docs.
57 +**Zyquo Router** runs a tiny, native gateway on your Mac. You store your provider keys
58 +once in an encrypted vault, pick a port, press **Start** — and everything that can talk
59 +to OpenAI can now talk to *twelve providers* through `http://localhost:8787/v1`, with
60 +per-request model routing, live traffic inspection, and cost tracking.
39 61
40 ## Quickstart
62 +Think **OpenRouter / LiteLLM — but native, local, private, and gorgeous.**
41 63
42 ```bash
43 git clone <repo> && cd zyquo-router
44 make dev # build + assemble dist/Zyquo Router.app (ad-hoc signed)
45 open "dist/Zyquo Router.app"
46 ```
64 +- 🔒 **Private by design** — keys never leave your Mac; requests go straight from your
65 + machine to the provider. No middleman, no telemetry, no accounts.
66 +- 🎯 **Spec-exact** — byte-exact `chat.completion.chunk` SSE streams that the official
67 + OpenAI Python and JS SDKs parse **unmodified** (verified across all 170 models).
68 +- 🖥 **A real Mac app** — SwiftUI control room with a menu bar extra, not a Docker
69 + container with a YAML file.
70 +
71 +---
72 +
73 +## ✨ Features
74 +
75 +### 🌐 The Gateway
76 +
77 +| Capability | Details |
78 +|---|---|
79 +| **OpenAI-compatible API** | `POST /v1/chat/completions` (streaming + non-streaming), `GET /v1/models`, `GET /v1/models/{id}`, `GET /health` |
80 +| **170 models, 12 providers** | Full catalog with context windows, capabilities, and per-Mtok pricing under the `x_zyquo` extension key |
81 +| **Namespaced routing** | `provider/model-id` (e.g. `anthropic/claude-sonnet-4-5`, `deepinfra/meta-llama/Llama-4-Maverick`); bare IDs accepted when unambiguous |
82 +| **Aliases** | Friendly names — `fast``cerebras/…`, `best``anthropic/…` |
83 +| **Fallback chains** | Ordered model lists tried on upstream failure; the response honestly reports the model that answered |
84 +| **Full translation** | Anthropic Messages API and Gemini `generateContent` translated bidirectionally: system extraction, turn merging, tools ⇄ `tool_use`/`functionCall`, images, finish-reason and usage normalization |
85 +| **Tool calling** | Streamed `tool_calls` argument deltas in exact OpenAI shape, from all three wire formats |
86 +| **Vision** | `image_url` content parts (data-URI base64 and remote URLs where supported) |
87 +| **Reasoning models** | Thinking output normalized to `reasoning_content` (DeepSeek convention) — works with Claude thinking, Gemini thoughts, DeepSeek-R1, Qwen, Magistral, Perplexity `<think>`, and more |
88 +| **Quirk normalization** | Together `finish_reason:"eos"`, Mistral thinking arrays, Perplexity citations + `.done` events, missing `object` fields, argument-repeat streams — all ironed into the spec |
89 +| **Resilience** | Exponential-backoff retries with jitter (never after the first streamed byte), honest OpenAI-format error mapping (401/429/502/504), client-disconnect cancels the upstream call in <1s |
90 +
91 +### 🔐 Control & Security
92 +
93 +- **Encrypted key vault** — AES-256-GCM with an HKDF-derived, machine-bound master key
94 + (hardware UUID + salted pepper). No Keychain, no plaintext, ever.
95 +- **Localhost by default** — LAN exposure (`0.0.0.0`) is an explicit opt-in that
96 + *requires* at least one local API key.
97 +- **Local API keys**`zyquo-sk-…` bearer tokens, SHA-256-hashed at rest, shown once
98 + at creation, per-key enable/revoke and model allow-lists.
99 +- **Redacted logging** — request/response bodies hidden by default; revealing is an
100 + explicit per-session switch. Provider keys never appear in any response, log, or error.
101 +
102 +### 📊 Observability
103 +
104 +- **Live dashboard** — requests/min sparkline, tokens, estimated cost (from real
105 + per-model pricing), error rate, active streams, uptime, per-provider breakdown bar.
106 +- **Request inspector** — every routed call with provider, status, latency, tokens,
107 + cost, SSE badge, and a detail pane with an upstream-TTFB timing waterfall.
108 +- **Filters & export** — by provider/status/model (⌘F), pause/clear, JSON export.
109 +
110 +### 🛠 Developer Experience
111 +
112 +- **Playground** — built-in tester that calls the router's *own* endpoint, with
113 + side-by-side request JSON / raw SSE panes and copy-as-code.
114 +- **In-app API docs** — the full reference rendered beautifully inside the app, from
115 + the same source of truth the server implements.
116 +- **Copy-as snippets** — endpoint URL, `curl`, OpenAI-Python, OpenAI-JS, pre-filled
117 + with your port.
118 +- **Menu bar extra** — status dot, Start/Stop, req/min, today's cost, copy endpoint.
119 +- **⌘K command palette** — server control, section jumps, fuzzy model-ID copy.
120 +- **Headless modes**`ZyquoRouter --serve [port]` (no UI) and `--load-vault`
121 + (seed keys from environment variables) for scripting and CI.
122 +- **Shortcuts** — ⌘R start/stop · ⌘1–6 sections · ⌘F filter · ⌘⇧C copy endpoint · ⌘K palette.
123 +
124 +---
125 +
126 +## 📸 Screenshots
127 +
128 +<div align="center">
129 +
130 +**Dashboard — the control room**
131 +
132 +<img src="assets/readme/screenshot-dashboard.png" width="850" alt="Dashboard: server card with endpoint and copy-as snippets, live tiles, sparkline" />
133 +
134 +<br/><br/>
135 +
136 +**In-app API reference**
137 +
138 +<img src="assets/readme/screenshot-docs.png" width="850" alt="Docs: hero card, code blocks with copy, styled sections" />
139 +
140 +</div>
141 +
142 +---
143 +
144 +## 📦 Installation
145 +
146 +### Download (recommended)
147 +
148 +1. **[⬇️ Download ZyquoRouter.dmg](https://github.com/spboucher-ai/zyquo-router/releases/latest/download/ZyquoRouter.dmg)** from the latest release
149 +2. Open the DMG and drag **Zyquo Router** into **Applications**
150 +3. Launch it — the app is **Developer ID signed, notarized, and stapled**, so Gatekeeper
151 + opens it without warnings
152 +
153 +### First run — three steps to one endpoint
154 +
155 +1. **Keys** → paste the API keys for the providers you use (each row has a **Test**
156 + button that verifies the key and shows latency)
157 +2. **Dashboard** → pick a port (default `8787`) → press **Start**
158 +3. Point anything OpenAI-compatible at `http://localhost:8787/v1` 🎉
159 +
160 +---
161 +
162 +## 🖥 Requirements
163 +
164 +| | Minimum |
165 +|---|---|
166 +| **macOS** | 13.0 Ventura or later |
167 +| **Architecture** | Universal binary — Apple Silicon & Intel |
168 +| **Disk** | ~15 MB |
169 +| **Accounts** | None. Bring your own provider API keys |
47 170
48 1. **Keys** → paste your provider API keys (each has a Test button).
49 2. **Dashboard** → pick a port → **Start**.
50 3. Point anything OpenAI-compatible at it:
171 +---
172 +
173 +## 🚀 Usage
174 +
175 +### Python (official OpenAI SDK — works unmodified)
51 176
52 177 ```python
53 178 from openai import OpenAI
54 179
55 180 client = OpenAI(base_url="http://localhost:8787/v1", api_key="zyquo")
56 r = client.chat.completions.create(
57 model="anthropic/claude-sonnet-4-5", # any provider/model from GET /v1/models
58 messages=[{"role": "user", "content": "Hello"}],
181 +
182 +stream = client.chat.completions.create(
183 + model="anthropic/claude-sonnet-4-5", # any of the 170 models
184 + messages=[{"role": "user", "content": "Hello!"}],
59 185 stream=True,
60 186 )
61 for chunk in r:
187 +for chunk in stream:
62 188 print(chunk.choices[0].delta.content or "", end="")
63 189 ```
64 190
191 +### JavaScript / TypeScript
192 +
65 193 ```javascript
66 194 import OpenAI from "openai";
195 +
67 196 const client = new OpenAI({ baseURL: "http://localhost:8787/v1", apiKey: "zyquo" });
197 +const r = await client.chat.completions.create({
198 + model: "gemini/gemini-2.5-flash",
199 + messages: [{ role: "user", content: "Hello!" }],
200 +});
68 201 ```
69 202
203 +### curl
204 +
70 205 ```bash
71 206 curl http://localhost:8787/v1/chat/completions \
72 207 -H "Content-Type: application/json" \
73 -d '{"model": "deepseek/deepseek-chat", "messages": [{"role": "user", "content": "Hi"}]}'
208 + -d '{"model": "deepseek/deepseek-chat", "messages": [{"role": "user", "content": "Hi"}], "stream": true}'
74 209 ```
75 210
76 Models are namespaced `provider/model-id`; bare IDs work when unambiguous, and you can
77 define aliases (`fast``cerebras/…`). `GET /v1/models` lists the full catalog with
78 context windows and pricing under the `x_zyquo` extension key.
211 +### Good to know
79 212
80 ## Highlights
213 +- **Model discovery:** `GET /v1/models` lists everything with context windows, pricing,
214 + and capability flags. Disabled models 404; favorites float to the top in-app.
215 +- **Reasoning:** pass the standard `reasoning_effort` — the router translates it per
216 + provider (Anthropic thinking budgets, Gemini `thinkingConfig`, Mistral `prompt_mode`, …)
217 + and normalizes the output into `reasoning_content`.
218 +- **Provider extras** pass straight through: Perplexity `search_domain_filter`, Qwen
219 + `enable_thinking`, Together `top_k`, Anthropic `thinking`, …
220 +- **Auth:** with no local keys the router is open on localhost. Create `zyquo-sk-…`
221 + keys in **Keys → Local API Keys** to require `Authorization: Bearer …` (mandatory for
222 + LAN mode).
223 +- The full contract lives in [`docs/API.md`](docs/API.md) — also rendered in-app under **Docs**.
81 224
82 | | |
83 |---|---|
84 | **Spec-exact API** | `POST /v1/chat/completions` (streaming + non-streaming), `GET /v1/models`, `GET /health` — verified against the official OpenAI Python and JS SDKs, unmodified. |
85 | **Full translation** | Anthropic + Gemini native APIs ⇄ OpenAI format: tools, vision, reasoning (`reasoning_content`), usage normalization, finish-reason mapping. |
86 | **Resilience** | Exponential-backoff retries (never after the first streamed byte), user-defined fallback chains that honestly report the model that answered, client-disconnect cancels the upstream call. |
87 | **Access control** | Local `zyquo-sk-…` bearer keys (hashed at rest, shown once), per-key model allow-lists, LAN mode gated behind keys. |
88 | **Observability** | Live traffic table with filters, redacted-by-default body inspector, TTFB waterfall, cost estimates from real per-model pricing. |
89 | **Mac-native polish** | Menu bar extra, ⌘K command palette, launch-at-login, light-flagship theme with a graphite-cyan control-room identity. |
225 +---
226 +
227 +## 🏗️ Architecture
90 228
91 ## Building
229 +100 % native Swift — no Electron, no Python sidecar, no Docker.
92 230
93 Requirements: macOS 13+, Swift 5.9+ toolchain, `librsvg` (icon pipeline only).
231 +```
232 +SwiftUI control room ─┐
233 + ├─► SwiftNIO HTTP/1.1 server (structured concurrency,
234 +Menu bar extra ───────┘ NIOAsyncChannel, spec-exact SSE writer)
235 +
236 + RequestRouter (namespaces, aliases,
237 + fallback chains, capability gates)
238 +
239 + ┌───────────────────┼────────────────────┐
240 + ▼ ▼ ▼
241 + AnthropicTranslator GeminiTranslator CompatAdjuster
242 + (Messages API ⇄ (generateContent ⇄ (per-provider param
243 + OpenAI, SSE event OpenAI, SSE tables + quirk
244 + state machine) chunks) normalization ×10)
245 + └───────────────────┼────────────────────┘
246 +
247 + UpstreamCall → provider APIs (your keys,
248 + straight from your Mac — AES-256-GCM vault)
249 +```
250 +
251 +- **Server:** SwiftNIO 2 with one task per connection; client disconnects propagate as
252 + cancellation all the way into the upstream `URLSession` transfer.
253 +- **Translation:** fixture-tested state machines convert Anthropic events and Gemini
254 + chunks into byte-exact OpenAI `chat.completion.chunk`s.
255 +- **Persistence:** JSON documents in `~/Library/Application Support/ZyquoRouter/`;
256 + keys in `vault.zq` (AES-256-GCM, machine-bound HKDF key, no Keychain).
257 +- **Zero heavyweight deps:** SwiftNIO, swift-nio-extras, swift-markdown. That's it.
258 +
259 +---
260 +
261 +## ⚙️ Building from Source
262 +
263 +Requirements: macOS 13+, Swift 5.9+ toolchain (Xcode or CLT), `librsvg` for the icon
264 +pipeline (`brew install librsvg`). Built entirely with SPM — **no Xcode IDE required**.
94 265
95 266 ```bash
96 make dev # debug-friendly release build + ad-hoc signed .app
97 make test # swift test (unit, fixture, and gateway-behavior suites)
267 +git clone https://github.com/spboucher-ai/zyquo-router.git
268 +cd zyquo-router
269 +
270 +make dev # release build + ad-hoc signed dist/Zyquo Router.app
271 +make test # unit, fixture, and gateway-behavior test suites
98 272 make icon # regenerate AppIcon.icns from assets/icon/zyquo-router.svg
99 make release # universal binary, Developer ID signing + notarization (Phase 8)
273 +make release # universal binary + Developer ID signing + notarization + DMG
274 +```
275 +
276 +Headless gateway for scripts/CI:
277 +
278 +```bash
279 +.build/release/ZyquoRouter --load-vault # seed the vault from env vars
280 +.build/release/ZyquoRouter --serve 8787 # run the gateway without the UI
100 281 ```
101 282
102 Headless modes for scripting: `ZyquoRouter --serve [port]` runs the gateway without the
103 UI; `ZyquoRouter --load-vault` seeds the vault from environment variables.
283 +---
284 +
285 +## ✅ Verification
286 +
287 +Every release is verified by [`scripts/verify.py`](scripts/verify.py), which drives
288 +**all 170 catalog models** through the local endpoint with the official OpenAI Python
289 +SDK — non-streaming, streaming chunk discipline, tool calling, vision, and reasoning.
290 +The current matrix is **170/170 green**: see [`docs/VERIFICATION.md`](docs/VERIFICATION.md).
291 +
292 +Gateway behavior is integration-tested against mock upstreams: client-disconnect
293 +cancellation, fallback-chain honesty, 429 retries, and graceful shutdown with active
294 +streams.
295 +
296 +---
297 +
298 +## 🗺 Roadmap
299 +
300 +- [ ] `POST /v1/embeddings` routed to embedding-capable providers
301 +- [ ] `POST /v1/responses` compatibility surface
302 +- [ ] Prompt-level request/response caching
303 +- [ ] Per-key rate limiting (token bucket) and budgets
304 +- [ ] Usage history persistence with daily/weekly summaries
305 +- [ ] Custom OpenAI-compatible endpoints (self-hosted vLLM/Ollama upstreams)
104 306
105 ## Documentation
307 +---
308 +
309 +## 🤝 Contributing
310 +
311 +Issues and pull requests are welcome!
312 +
313 +1. Fork → branch → make your change
314 +2. `make test` must stay green (unit + fixture + gateway-behavior suites)
315 +3. `swift build` must produce **zero warnings**
316 +4. Every code file carries the project header (see any source file)
317 +
318 +---
319 +
320 +## 📄 License
106 321
107 - [`docs/API.md`](docs/API.md) — the exact public API contract (also rendered in-app)
108 - [`docs/VERIFICATION.md`](docs/VERIFICATION.md) — the 170-model compatibility matrix
109 - [`docs/ROUTER-RESEARCH.md`](docs/ROUTER-RESEARCH.md) — gateway research & design decisions
110 - [`docs/PROVIDER-REUSE.md`](docs/PROVIDER-REUSE.md) — Zyquo Cloud provider-layer study
111 - [`docs/PLAN.md`](docs/PLAN.md) — phase-by-phase execution log
322 +Released under the [MIT License](LICENSE).
112 323
113 324 ---
114 325
326 +## 👤 Author
327 +
115 328 <div align="center">
116 329
330 +**Simon-Pierre Boucher**
331 +
332 +📫 Contact: [contact@spboucher.ai](mailto:contact@spboucher.ai)
333 +
117 334 Part of the **Zyquo** family — Cloud · Local · Agent · Atlas · MLX · **Router**
118 335
119 © 2026 Simon-Pierre Boucher
336 +<sub>© 2026 Simon-Pierre Boucher. All rights reserved.</sub>
120 337
121 338 </div>
added assets/readme/app-icon.png +0 −0

Binary file not shown.

added assets/readme/screenshot-dashboard.png +0 −0

Binary file not shown.

added assets/readme/screenshot-docs.png +0 −0

Binary file not shown.