GPTPortal: A Self-Hosted Multi-Provider Chat UI With No Build Step
A feature-rich portal to chat with GPT-4, Claude, Gemini, Mistral, & OpenAI Assistant APIs via a lightweight Node.js web app; supports customizable multimodality for voice, images, & files.
At a glance
- What is it?
- GPTPortal puts OpenAI, Anthropic, Google, Mistral, OpenRouter and any OpenAI-compatible endpoint behind one Node/Express chat interface you run yourself. The trade-off is a single-user Basic Auth gate and no RAG, agents or vector storage.
- Who is it for?
- Adopt GPTPortal if you already hold provider API keys, want one interface for several vendors, and are willing to run node server.js on your own machine with a .env you control. Do not adopt it if you need multi-tenant accounts, retrieval over a document corpus, or an agent runtime; the README states there is no vector database, no RAG pipeline and no agent-orchestration subsystem, and the single Basic Auth gate is built for one person or a trusted household.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 72 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem: five browser tabs, five billing pages, five key stores
Anyone who uses more than one model vendor ends up with the same mess. A chat subscription for one provider, a separate console for another, a third tab for a local model, and no shared history between them. Switching mid-task means copying a prompt out of one window and pasting it into another, and comparing two answers means reading them in sequence rather than side by side. GPTPortal's answer is a single Express application that holds your API keys in a .env file and routes each message to whichever provider you selected in a dropdown. The README describes it as local-first: conversations live in the browser session and optionally in files under public/uploads/, and nothing leaves the machine except the API call to the provider you picked. That framing tells you the intended user. It is one person, or a trusted household, with their own keys, who wants to stop juggling vendor UIs. It is not a product team that needs per-user accounts or an audit trail.
Architecture: one process, no bundler, no database
The README is explicit that there is no build step, no bundler, no frontend framework and no database. The backend is modular Node/Express and the frontend is hand-written HTML, CSS and vanilla JavaScript, which is why the whole thing restarts in seconds after an edit. The entry point is node server.js, which constructs an Application object. That object wires three managers together: a MiddlewareManager handling Basic Auth, sessions, CORS, rate limiting, CSP, body parsing and static files; a ServiceManager that instantiates and holds the services; and a RouteManager. The Application emits lifecycle events as it starts. The supplied README truncates the architecture diagram at that point, so the specific service classes below the ServiceManager are not visible in the material, and I will not guess at them. What is visible is the shape: one process, a small set of managers, and a request path that goes through middleware before it reaches a route. That is a deliberate constraint, and the README calls it one, describing the goal as a tool you own and understand rather than a black box.
Model catalog: a JSON file, a live OpenRouter fetch, and env-var endpoints
Three mechanisms feed the model picker, and they behave differently. The core catalog is a curated set of current models across OpenAI, Anthropic, Google, xAI, DeepSeek, Groq, Moonshot (Kimi) and Mistral, defined in a single JSON file. Because it is a file, it goes stale whenever a vendor ships or retires a model, and updating it means editing that file by hand. The second mechanism is different: the entire OpenRouter catalog is loaded live and cached, so anything OpenRouter serves is available without a code change. The third covers self-hosted inference. Ollama, LM Studio, vLLM, LocalAI or a remote gateway are added through environment variables with no code, and the README states they are auto-discovered into the model picker. The picker itself is searchable and categorized with per-provider logos. The practical consequence is that the OpenRouter and custom-endpoint paths stay current on their own, while the curated catalog is the part that depends on someone maintaining it.
Getting it running: node server.js and a .env
The documented path is short. The README's quick start points at a single command, node server.js, and the default branch's homepage field lists http://localhost:3000/portal as the address. Configuration lives in a .env file that the README says is never committed, and the repository ships a configuration reference section for it. Keys are per provider, so you need at least one provider key before anything works; the README is direct that every model call goes through a provider API and that this is not a no-key demo. It also notes that most providers give new accounts some free credit and that several models are effectively free through Groq or a local endpoint, which is the cheapest way to try the thing. The README lists a requirements section and a step-by-step installation section, but the supplied excerpt does not include their contents, so the exact Node version and install steps are not something I can state. Read those two sections in the repository before running anything. Two configuration surfaces are worth knowing about: the .env, and the fact that the README says you can edit the system prompt and your .env from inside the browser. An edit-capable .env endpoint is convenient on localhost and a different proposition the moment the app is reachable from a network.
What the message path actually does: streaming, caching, and where state lives
The README devotes a section to streaming and how a message flows, and another to prompt caching for Claude. Token streaming runs end to end, and models that expose reasoning stream that separately from the final answer, which is why the UI can show a reasoning channel distinct from the reply. There is a dedicated prompt caching section for Claude, aimed at long, stable system prompts; the mechanism as described is a cost measure for repeated prefixes, not a general cache. State is split in a way that matters for anyone planning to deploy this beyond their laptop. Conversations live in the browser session, with optional files under public/uploads/. There is a Basic Auth gate and per-browser session state, and the README states plainly that the app is designed for one person or a trusted household. So the server holds almost no conversation state of its own, and the browser is the source of truth for a thread. Close the tab on the wrong machine and the thread is not waiting for you on another. The export-to-self-contained-HTML feature is the documented escape hatch for that.
Features that assume a specific workflow, and one that does not fit the rest
Most of the feature list is what you would expect from a chat front end: markdown with syntax-highlighted code blocks via highlight.js, math via KaTeX, per-block copy buttons, sanitized output before it touches the DOM, temperature and max-token sliders, reasoning and verbosity controls for models that support them. Vision takes an attached image. File uploads take text-based files and add their name and contents to the prompt, which is a prompt-stuffing approach rather than retrieval, and it will not scale to a large corpus. Voice covers both directions: dictation through speech-to-text and replies read back through text-to-speech. Image generation is triggered by prefixing a message with Generate:. The compare view sends one prompt to several models and shows the answers side by side with each one's cost, which is the feature that most justifies running this instead of a vendor's own UI. Assistants mode is the outlier. It is OpenAI-only, stateful, tool-using, with a persistent thread and code execution. Everything else in GPTPortal is a stateless proxy in front of a chat completion endpoint; Assistants is a different API with server-side thread state, and it sits oddly next to the local-first, browser-session model described everywhere else.
Where it is the wrong tool
The README draws the boundary itself, and it is worth taking at face value. There is no vector database, no RAG pipeline, no agent-orchestration subsystem and no packaged desktop binary. If your requirement is retrieval over a document set, or a multi-step agent that calls tools on a schedule, GPTPortal is a base to build on rather than a solution, and the file-upload feature will not substitute for retrieval because it puts file contents directly into the prompt. The single Basic Auth login gate and per-browser session state rule out multi-tenant use. The README says you can expose it on a network, but designed-for-one-person and safe-to-expose are different claims, and the browser-accessible .env editing makes that gap wider. There is also a maintenance question the repository metadata raises rather than answers. The last tagged release listed is v1.1.2 from February 2024, with v1.1.1 and v1.1.0-voice before it, yet the repository shows a push in July 2026 and the README describes a 2.0 rewrite. That combination suggests active work between tags rather than a stalled project, but it also means the release notes do not describe the code on main. If you deploy from a tag you get the 2024 behavior the release notes document; if you deploy from main you get the rewrite with whatever documentation exists in the README.
The alternative, and the actual difference in approach
The obvious comparison is Open WebUI, another self-hosted chat front end that fronts multiple providers. The difference is in what each one carries. Open WebUI is a Python application with a database behind it, which is what lets it offer persistent multi-user accounts, server-side conversation history and built-in retrieval over uploaded documents. GPTPortal takes the opposite position on purpose: no database, no build step, no framework, conversations in browser session state, and a single login gate. The consequence cuts both ways. GPTPortal is easier to read end to end and to modify, because the frontend is hand-written HTML, CSS and vanilla JavaScript and the backend is a small set of managers. It is harder to run for a group, because there is no per-user account model to run. If you want a shared internal tool with history that survives a browser restart, the database-backed approach fits better. If you want a single-user portal you can audit in an afternoon and edit without a toolchain, the no-database design is the reason to pick this one. A second comparison point is the vendor UIs themselves. They cost a subscription per provider and keep your history on someone else's servers, but they need no maintenance from you and no .env file to protect.
Licence, cost and the maintenance you are signing up for
The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are preserved. That is a permissive licence and it is compatible with the kind of local editing the README encourages; it is not legal advice, and if you redistribute a modified version you should read the licence text in the repository rather than this summary. On running cost, the README's position is pay-as-you-go: you are billed by each provider for exactly what you use through your own keys, with no subscription and no middleman, and it notes that heavy users may find this cheaper than several chat subscriptions while light users may spend a few dollars a month. The compare view and the live cost indicator exist to make that spending visible per message. The maintenance cost is the part that is easy to underestimate. You are responsible for the .env, for keeping the curated model JSON current as vendors change their lineups, and for deciding whether to track main or stay on the v1.1.2 tag. The README's own testing section is the place to look for what the project verifies, and the troubleshooting and FAQ section is where the documented failure modes live; the supplied excerpt does not include either, so check them before you file an issue. The honest summary is that GPTPortal trades operational work for legibility, and the trade only pays off if you actually want to read and edit the code.
Editorial conclusion
Adopt GPTPortal if you already hold provider API keys, want one interface for several vendors, and are willing to run node server.js on your own machine with a .env you control. Do not adopt it if you need multi-tenant accounts, retrieval over a document corpus, or an agent runtime; the README states there is no vector database, no RAG pipeline and no agent-orchestration subsystem, and the single Basic Auth gate is built for one person or a trusted household. Before committing, verify three things on your own copy: that your chosen providers are reachable from your network, that public/uploads/ is not exposed publicly, and that the model IDs in the JSON catalog still resolve, since the last tagged release is v1.1.2 from February 2024 while the repository has been pushed to since.
Community notes