Neo Chat's compose file enables ephemeral BYOK keys by default while its env example disables them
A local-first AI chat workspace for models, agents, skills, plugins, search, RAG, voice, memory, and artifacts.
At a glance
- What is it?
- u14app/neo-chat is an MIT licensed Next.js workspace for multi-model chat, agent runs, and deep research that keeps conversations in browser storage and can self-host on Docker, Vercel, or Cloudflare Workers. Its two documented install paths disagree on how much is exposed and whether credential encryption has a stable key, and agent execution lives in the page rather than on the server.
- Who is it for?
- Neo Chat fits a person or a small group who wants a self-hosted multi-model workspace with the provider keys held by their own deployment rather than a hosted service, and the deployment guide is written for exactly that. Check five things before exposing it.
- 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 14 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- u14app/neo-chat1,882 ★ · +56
| Repository | 2026-09-15 | 2026-11-14 |
|---|---|---|
| u14app/neo-chat | 1.8k | 1.9k |
56 Stars in the last 60 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The compose file defaults the ephemeral key on and the env example defaults it off
One boolean decides whether your stored provider credentials survive a restart. BYOK_ALLOW_EPHEMERAL_KEY is the flag, and the two configuration files in the repository disagree about it. docker-compose.yml sets it to true, and .env.example sets it to false, with the comment that the flag controls non-hosted production. The consequence is spelled out next to it: when BYOK_PRIVATE_KEY_PEM is empty, a hosted single instance falls back to an ephemeral process key, and you are told to use a stable key for restarts, replicas, and long lived production. The single container example in the quick start passes the flag as true on the command line and then warns about key rollover across restarts and replicas. A related pair, BYOK_PRIVATE_KEY_PEM and BYOK_KEY_ID, is empty in both files, so out of the box the deployment is generating its own key rather than using one you hold. There is a generator script for it, byok:generate.
The one line run binds loopback and the compose file publishes every interface
Two documented ways to run the same image, with different exposure. The quick start runs:
docker run --rm -p 127.0.0.1:3000:3000 \
-e ACCESS_PASSWORD='replace-with-a-strong-password' \
-e BYOK_ALLOW_EPHEMERAL_KEY=true \
ghcr.io/u14app/neo-chat:latestThe port mapping is bound to 127.0.0.1, and a password is required in the example. docker-compose.yml maps `"3000:3000"` with no host address, and sets ACCESS_PASSWORD to an empty default, which the environment reference says disables the password gate. So the shorter path is the safer one and the compose path is the one people copy. The compose file does carry the compensating switches: ALLOW_INSECURE_LOCAL_PRODUCTION defaults to false, TRUST_PROXY_HEADERS defaults to false so x-forwarded headers are ignored unless your platform strips spoofed values, and SHARING_ENABLED defaults to false. The password gate is also described plainly as an access gate and not a multi-user account system, and multiple accepted passwords are separated by commas rather than being per-user.
Three stores default to process memory and four research adapters need Upstash anyway
Deployment wide state is three variables, and all three default to memory. RATE_LIMIT_STORE, DOCUMENT_PARSE_JOB_STORE, and PLUGIN_REGISTRY_STORE each take memory or upstash, with UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN left empty. The comments are more specific than the defaults suggest. Hosted rate limiting falls back to process memory when Upstash is unavailable, but document jobs, plugin registration, sharing, and multi-instance deployments still require Upstash for consistent or durable behaviour. And Deep Research's specialised adapters for arXiv, PubMed, EPO OPS, and SEC EDGAR use that same Upstash pair for cross-instance provider rate coordination. So a default deployment has no durable job store, no durable plugin registry, and a per-process rate limit, while four scholarly sources are documented as depending on the shared pair. The README says it once at the deployment level: a single hosted process has temporary-key and in-memory rate-limit fallbacks, and public multi-instance deployments should use stable keys and shared stores.
Agent and Research runs execute in the page, so closing the tab stops them
This is the architectural fact that shapes everything else in the project. Agent and Research runs are orchestrated in the foreground, and closing the page interrupts execution; saved runs require explicit resume. So the orchestrator driving multi-step tool workflows and deep research lives in the browser tab, not in a background worker on the server, which is why resume has to be an explicit action rather than automatic. It also explains the memory-backed job store defaults above. The same section is candid about what local-first does not mean: conversations and workspace files stay in browser storage by default, requests send the necessary content to the model providers and services you use, usually through the app's API routes, and local-first storage does not mean inference runs offline. The word usually is doing real work in that sentence, since some requests go out directly rather than through a route.
Research templates, steering, and report Q&A are excluded from every backup path
Backups and sync do not cover everything, and the gap is named. Backups exclude credentials and external service data, which is reasonable. The second exclusion is not: Research extensions such as custom templates, steering, and report Q&A are described as currently browser-local and excluded from both ZIP backups and encrypted sync. So the configuration that shapes a research workflow is the part you lose first, and the description puts the fault on browser-local storage rather than on the backup design. Encrypted sync is otherwise optional and available through WebDAV or S3 and MinIO. The deployment hardening guide is the document that covers stable keys, access protection, and shared runtime stores, and the quick start points at it as required reading before production rather than as optional detail. A separate document covers privacy and local data, one covers encrypted sync, and SECURITY.md holds the vulnerability reporting route.
User configured targets may be plain HTTP to a private address, by design
The environment reference states a policy that is worth reading before exposing the app. User-configured provider, search, RAG, plugin, and MCP targets may use HTTP or HTTPS and may point at local or private addresses in either deployment mode. Fixed registries and service endpoints remain HTTPS-only. There is a single opt-in for the HTTP half of media and image proxy surfaces, ALLOW_LOCAL_NETWORK_PROXY, described as retained to opt deployment-gated media and image proxy surfaces into HTTP. So the design decision is that anything a user configures can be fetched, including an address on the operator's own network over plain HTTP, and only the endpoints the operator controls are held to TLS. That is the right trade for a tool whose purpose is connecting to whatever service you already run, and it is also the property to be aware of before you let someone else use the instance. Whether an MCP bridge is involved is a separate control, since the local MCP bridge runs behind an allowlist of stdio servers.
One Next.js app targets three platforms, and Cloudflare goes through an adapter
The deploy table has three rows and three different mechanisms. Docker uses the official image and is the only row whose port mapping is documented in the repository. Vercel is an import of the repository with the Next.js preset and the default output directory. Cloudflare Workers is two commands, `corepack pnpm build:worker` followed by `corepack pnpm deploy:worker`, and both go through opennextjs-cloudflare rather than through Next.js directly, with wrangler carrying a dry-run target, a preview target, and a type generation step. The deploy script passes --keep-vars so dashboard configured variables survive a deployment, which is the difference between a redeploy and a rebuild from the dashboard's point of view. The root of the repository carries next.config.ts, open-next.config.ts, and wrangler.jsonc, one config per target, plus a script that checks the worker bundle size. The runtime floor is Node 24 with pnpm 10.30.3 through Corepack, and the container is built on node:24-bookworm-slim.
The manifest reads 2.5.2 while the newest tag is v2.5.0
The versioning is one small step ahead of the tags. The manifest version is 2.5.2, the published releases are v2.5.0 on 2026-09-11, v2.4.0 on 2026-08-01, and v2.3.0 on 2026-07-19, and the last commit is dated 2026-10-01. So two patch versions exist in the tree with no tag among the visible releases, and the releases before them are roughly monthly. The image the quick start pulls is tagged latest rather than a version, so what you run is whatever the registry last pushed. Two descriptions also differ. The project summary calls it a local-first AI chat workspace covering models, agents, skills, plugins, search, RAG, voice, memory, and artifacts, while the manifest description calls it an omni AI chat application with a clean UI and excellent performance, which is the one claim in either file with no measurement attached. The document is translated into English and Simplified Chinese, with Japanese named as supported in the interface, and the last visible line begins a reference to the original G, cut off there.
Editorial conclusion
Neo Chat fits a person or a small group who wants a self-hosted multi-model workspace with the provider keys held by their own deployment rather than a hosted service, and the deployment guide is written for exactly that. Check five things before exposing it. Whether the access password is set, because an empty value disables the gate entirely and the document is explicit that it is one shared password rather than a set of accounts. Whether BYOK keys are stable, because the shipped compose file turns on ephemeral credential encryption while the environment example turns it off, and an ephemeral key cannot decrypt what a previous boot stored. Which stores are configured, because rate limiting, document jobs, and the plugin registry all default to process memory and several features need a shared Upstash pair instead. Which port you publish, because the one line run binds loopback and the compose file publishes on every interface. And that you accept foreground orchestration, since closing the page stops an agent or research run in progress.
Frequently asked questions
How do I run Neo Chat locally?
Clone the repository, then run `corepack pnpm install --frozen-lockfile` and `corepack pnpm dev`, which needs Node.js 24 and pnpm 10.30.3 through Corepack. Open localhost:3000 and add a model provider and API key in Settings. Alternatively run the ghcr.io/u14app/neo-chat:latest image with docker run.
What does the ACCESS_PASSWORD in Neo Chat protect?
It is an access gate rather than a multi-user account system. Leaving it empty disables the gate, multiple accepted passwords are separated by commas, and production local API access additionally requires ALLOW_INSECURE_LOCAL_PRODUCTION when no password is set.
What is the BYOK key in Neo Chat used for?
It is the server key that encrypts the provider credentials you bring yourself. BYOK_PRIVATE_KEY_PEM and BYOK_KEY_ID are empty by default, so a deployment falls back to an ephemeral process key. The compose file sets BYOK_ALLOW_EPHEMERAL_KEY to true while the env example sets it to false, and stable keys are needed for restarts and replicas.
Where does Neo Chat store conversations and does inference stay local?
Conversations and workspace files stay in browser storage by default, with ZIP backup and restore and optional end-to-end encrypted sync through WebDAV or S3 and MinIO. The document says local-first storage does not mean inference runs offline, since requests send the necessary content to the model providers you configure.
What happens to an agent or Deep Research run if I close the page?
Execution is interrupted, because Agent and Research runs are orchestrated in the foreground. Saved runs require an explicit resume. Custom Research templates, steering, and report Q&A are also browser-local and excluded from both ZIP backups and encrypted sync.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/u14app-neo-chat)Star history chart for your README
[](https://hysenlabs.com/projects/u14app-neo-chat)Was this useful?