{"id":48470198,"url":"https://github.com/zwgnr/modelverse","last_synced_at":"2026-04-07T06:03:03.278Z","repository":{"id":299902019,"uuid":"1003779915","full_name":"zwgnr/modelverse","owner":"zwgnr","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-18T21:45:09.000Z","size":592,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-18T22:32:51.129Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://modelverse.chat/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zwgnr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-17T16:50:06.000Z","updated_at":"2025-06-18T21:45:13.000Z","dependencies_parsed_at":"2025-06-18T22:33:01.147Z","dependency_job_id":"ad266625-3ce7-41ad-88af-30f171b0264a","html_url":"https://github.com/zwgnr/modelverse","commit_stats":null,"previous_names":["zwgnr/modelverse"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zwgnr/modelverse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zwgnr%2Fmodelverse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zwgnr%2Fmodelverse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zwgnr%2Fmodelverse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zwgnr%2Fmodelverse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zwgnr","download_url":"https://codeload.github.com/zwgnr/modelverse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zwgnr%2Fmodelverse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31501903,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-07T06:03:01.973Z","updated_at":"2026-04-07T06:03:03.272Z","avatar_url":"https://github.com/zwgnr.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Modelverse\n\n**Chat with all your favorite AI models in one place.**\n\n---\n\n\u003cimg width=\"1865\" alt=\"Screenshot 2025-06-18 at 12 41 41 PM\" src=\"https://github.com/user-attachments/assets/860d84e6-8869-4a05-b192-6d179a596232\" /\u003e\n\n\nModelverse is an **open‑source, multi‑model chat application**  \n\nBring your own [OpenRouter](https://openrouter.ai) API key and instantly switch between GPT‑4o, Claude, Gemini 2.5, Grok 3, DeepSeek and many more. All from a single chat UI.\n\n\n## Powered by\n\n\u003cimg width=\"24\" alt=\"Screenshot 2025-06-18 at 12 41 41 PM\" src=\"https://github.com/user-attachments/assets/93bfae26-4ec1-45b0-a21b-97c86151fa02\" /\u003e [Tanstack Start](https://tanstack.com/start/latest) \n\n\u003cimg width=\"24\" alt=\"Screenshot 2025-06-18 at 12 41 41 PM\" src=\"https://github.com/user-attachments/assets/eaa85ba6-544e-40ae-89af-cfd1cea4c443\" /\u003e [Convex](https://www.convex.dev/) \n\n\u003cimg width=\"24\" alt=\"Screenshot 2025-06-18 at 12 41 41 PM\" src=\"https://github.com/user-attachments/assets/7c85ec86-f372-4592-8ecb-0f9f318acf9c\" /\u003e [Better Auth](https://www.better-auth.com/) \n\n[Open Router](https://openrouter.ai/)\n\n\n\n\n\n\n---\n\n## ✨ Features\n\n* 🔌 **Plug‑and‑play model picker** \n* 🌐 **Web search toggle** \n* 🧑‍💻 **GitHub OAuth** via `@convex-dev/better-auth`\n* 🔒 **API‑key encryption** \n* 📡 **Resumable streaming** with `@convex-dev/persistent-text-streaming`\n* 🔄 **SSR** \n* 🛠 **TypeScript everywhere** – strict types from DB → server → client\n\n\n## Local Dev\n\n```bash\n# 1. Clone\n$ git clone https://github.com/zwgnr/modelverse.git\n$ cd modelverse\n\n# 2. Install\n$ pnpm i # \n\n# 3. Configure environment variables (see table below)\n\n# 4. Dev servers (frontend + Convex)\n$ pnpm dev\n```\n\nhttp://localhost:3000 will open automatically and sign in with GitHub.  The first time you run a chat, the app will ask for your **personal OpenRouter API key** and store it encrypted in Convex.\n\n### Required Environment Variables\n\n| Name                   | Description                                                                           |\n| ---------------------- | ------------------------------------------------------------------------------------- |\n| `VITE_CONVEX_URL`      | Convex deployment URL (e.g. `https://happy‑yorkie‑123.convex.cloud`)                  |\n| `VITE_CONVEX_SITE_URL` | The public origin that hosts your frontend (used by Better Auth redirects)            |\n| `OPEN_ROUTER_API_KEY`  | OpenRouter key for your own account\n| `BETTER_AUTH_SECRET`   | 32+‑byte secret for `better-auth` JWT signing                                         |\n| `ENCRYPTION_KEY`       | 32‑byte **hex‑encoded** master key used for AES‑256‑GCM encryption of user secrets    |\n| `ENCRYPTION_ID`        | This can be anything and is in place for easier future key roations                   |\n| `GITHUB_CLIENT_ID`     | OAuth app/client ID from GitHub                                                       |\n| `GITHUB_CLIENT_SECRET` | Corresponding client secret                                                           |\n\n\n## 🔐 Security \u0026 Key Management\n\nModelverse follows the **principle of least privilege**.  The server alone can decrypt user API keys;\nno plaintext secrets ever touch the client.\n\n1. **Client → Server** – the user types or pastes their OpenRouter key in the settings modal.  The key is sent over HTTPS to a Convex **action**.\n2. **AES‑256‑GCM Encryption** – the action calls `encrypt(plaintext, ENCRYPTION_KEY)` (see `/convex/lib/encryption.ts`).  The IV is prepended, ciphertext is base64‑encoded.\n3. **Convex Database** – encrypted blob + keyId are stored.\n4. **Runtime Usage** – when the user makes a chat request, the server **decrypts** the key on‑the‑fly and forwards it to OpenRouter.  The plaintext is *never* returned to the browser.\n\n### Why not Local Storage?\n\nStoring provider keys unencrypted in `localStorage` (or `sessionStorage`, `IndexedDB`, etc.) leaves them\nvulnerable to:\n\n* **XSS** – any successful script injection instantly exfiltrates a user’s key.\n* **Multi‑device use** – your key is locked to one browser; you have to paste it again on each device.\n* **Data loss** – clearing cache or using private tabs wipes the key.\n\nBy contrast, **server‑side encryption** means:\n\n* Only authenticated requests can access the ciphertext.\n* The master key lives exclusively in your Convex deployment’s environment; it is not bundled with the client.\n* Rotating keys is straightforward: bump `keyId`, re‑encrypt in a migration script, restart.\n\n## 🧑‍🤝‍🧑 Contributing\n\nContributions are welcome!\n\n## 📝 License\n\nDistributed under the **MIT License**.  See [`LICENSE`](LICENSE) for more information.\n\n---\n\n\u003e ❤️ built with love and coffee!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzwgnr%2Fmodelverse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzwgnr%2Fmodelverse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzwgnr%2Fmodelverse/lists"}