Overview
Eltes is a browser-based AI workspace. Connect a real provider account (or paste a key), pick a model, and Eltes streams that provider's answer, renders tool calls, and keeps history in this browser. Direct mode calls providers from the browser; an optional separately hosted Vercel/local proxy can test keys and forward requests. If a hosted provider is not connected, the app says so instead of inventing an answer — and Eltes CAI, a small on-device helper, is available with no key.
Three ideas, in order
- Local-first. Chats, settings, keys and profiles live in
localStorage. Clearing site data is the nuclear option, and it is yours to press. - Bring your own key. By default, requests go from your tab to the provider. An optional trusted backend can relay a key for each request without persisting or logging it in the function code.
- Real agent loop. When tools are on, the model can call
calculate,current_timeandsearch_history; every call is shown as a card with its arguments, result and runtime. - On-device when you need it. Eltes CAI-1 answers chat, Eltes questions, model choice, arithmetic, units and dates in this tab. No key, no network, and it is explicit about what it does not know.
What changed in v3
| Area | v3 behaviour |
|---|---|
| Replies | Live streams only. A missing key produces a connect card; failures produce typed error cards with retry. CAI answers locally and labels itself. |
| Chat | Dark-blue theme with a block field that moves with the scroll. Step timeline, tool cards, per-message chips, first-token latency, tokens and tok/s. |
| Models | 36 real models across 13 providers, including Claude Sonnet 5.5, DeepSeek V4.1, NVIDIA NIM and three Eltes CAI models. |
| Accounts | Sign up or in with OpenAI, Anthropic or DeepSeek, or keep a local profile. GitHub sign-up is gone. |
| Brand | Green cloud mark — one eye closed, one open — across the site, favicon, app icon and maskable manifest icon. |
| Downloads | A portable Windows package with eltes.bat, a Start-Eltes helper and the whole static site. |
Quickstart
From a blank tab to a live answer, with one provider key.
Open the workspace
Go to app.html. No install and no sign-in. An account is optional and only adds a profile.
Connect a provider
Press Connect an account, choose OpenAI, Anthropic or DeepSeek, and save the key. Or open Settings → API keys. The pill in the top bar turns green when at least one key is present. No key yet? Pick Eltes CAI-1.
Pick a model
Use the model button or ⌘M. Models without a key show a needs key badge; star the ones you use most and they move to the top.
Send a message
Type and press Enter. Tokens stream in as they arrive; press the stop button to keep a partial answer. Metrics appear under the reply when it finishes.
# clone the repository
git clone https://github.com/gefrus112/eltes
cd eltes
# any static server works; there is no build step
python3 -m http.server 4173
# open http://localhost:4173
http://localhost:11434/v1 under Custom endpoints.
Accounts
An Eltes account is a profile plus a set of provider connections. It stores a name, an email, the provider that created it and — when you connect one — a provider key. There is no password, no session cookie and no email delivery. The profile is written to eltes.account.v1; provider keys live in eltes.settings.v1.
Create or sign in
Open auth.html. Sign up with OpenAI, Anthropic or DeepSeek, or create a local profile with a display name and email. Signing in restores the profile already in this browser. Because there is no Eltes server, a local email is a label, not a credential.
| Provider | What “connect” does on a static copy | What it does with a host |
|---|---|---|
| OpenAI | Validates the key against /v1/models, then stores it locally | Redirects to /openai/start and returns a profile |
| Anthropic | Validates against /v1/models with the browser-access header | Redirects to /anthropic/start and returns a profile |
| DeepSeek | Saves the key; verification is best-effort because CORS varies | Redirects to /deepseek/start and returns a profile |
| Local profile | Name + email, stored in this browser only | Same — no hosted identity required |
Hosted OAuth contract
A static page cannot hold a client secret, so the hosted path is a contract rather than a fake success state. Set the endpoint before the auth scripts load:
<!-- Only when a real auth backend exists -->
<script>
window.ELTES_CONFIG = {
authEndpoint: 'https://auth.example.com'
};
</script>
With authEndpoint set, a provider button redirects to:
# provider is openai, anthropic or deepseek
GET https://auth.example.com/<provider>/start
?return=<the full auth.html URL>
&mode=signup
# your backend completes OAuth, then returns the user to:
https://eltes.example/auth.html?account=<base64url(profile)>
# profile
{
"name": "Ada Lovelace",
"email": "ada@example.com",
"provider": "openai",
"avatarUrl": "https://example.com/ada.png"
}
The page decodes the profile, writes it locally, clears the parameter and shows the signed-in state. Avatar URLs are restricted to https:. An error=… or provider_error=… parameter shows a failure message instead, so a cancelled login never looks successful. code and state are stripped from the address bar after the callback.
API keys
Keys are read from localStorage at request time and are excluded from exports. In direct mode, OpenAI-compatible providers receive Authorization: Bearer …; Anthropic receives x-api-key and anthropic-version. With the optional proxy, the browser sends the key in a request header to the configured backend, which forwards it using the provider's required authentication. Eltes function code does not persist or log it; the key still transits the backend platform.
| Provider | Key prefix | Where to get it |
|---|---|---|
| Anthropic | sk-ant-… | console.anthropic.com → API keys |
| OpenAI | sk-proj-… | platform.openai.com → API keys |
AIza… | aistudio.google.com → API key | |
| Z.ai · GLM | account token | z.ai → API keys |
| DeepSeek | sk-… | platform.deepseek.com |
| xAI | xai-… | console.x.ai |
| Qwen · Alibaba | sk-… | Model Studio console |
| Mistral | API key | console.mistral.ai |
| Groq | gsk_… | console.groq.com |
| OpenRouter | sk-or-v1-… | openrouter.ai/keys |
| NVIDIA NIM | nvapi-… | build.nvidia.com |
What a request looks like
POST {baseUrl}/chat/completions
Authorization: Bearer <your key>
Content-Type: application/json
{
"model": "gpt-4.1",
"stream": true,
"messages": [{ "role": "user", "content": "Hello" }]
}
Rotating and removing keys
- Keys can be replaced at any time under Settings → API keys; saving overwrites the old value immediately.
- Removing a key disables that provider's models until a new one is saved.
- Exports never include keys. Backups are safe to share;
localStorageis not.
Providers & CORS
By default, providers are called directly from the page, which requires permissive provider CORS headers. Some providers allow it; others do not. If a direct call is blocked, Eltes shows a network/CORS error with retry guidance. You may instead configure the optional Vercel/local backend to relay supported hosted-model requests; review the proxy trust and privacy notes before using it.
| Provider | Base URL | Direct browser calls |
|---|---|---|
| Anthropic | api.anthropic.com/v1 | Yes — sends anthropic-dangerous-direct-browser-access |
| OpenAI | api.openai.com/v1 | Yes |
| Google Gemini | generativelanguage.googleapis.com/v1beta/openai | Yes |
| Z.ai · GLM | api.z.ai/api/paas/v4 | Usually |
| DeepSeek | api.deepseek.com/v1 | Varies by region |
| xAI | api.x.ai/v1 | Varies |
| Qwen | dashscope-intl.aliyuncs.com/compatible-mode/v1 | Varies |
| Mistral | api.mistral.ai/v1 | Varies |
| Groq | api.groq.com/openai/v1 | Yes |
| OpenRouter | openrouter.ai/api/v1 | Yes |
| NVIDIA NIM | integrate.api.nvidia.com/v1 | Yes |
| Eltes CAI | in this tab | Not applicable — no network request |
| Custom | your URL | Depends on your server |
Error states you will actually see
| State | What Eltes shows | What to do |
|---|---|---|
401 / 403 | “Provider rejected that key”, with Fix key | Paste a fresh key; the failed turn retries automatically after saving |
429 | Rate limit or quota reached | Wait, retry, or switch to another connected model |
| Network / CORS | “Could not reach provider” | Check the network, try another provider, or route through a proxy |
404 | Model not available on this account | Pick another model from the picker |
| No key | Connect card with the provider row | Add a key or switch models — no fake answer is generated |
| Offline | “You are offline” | Reconnect and press retry |
Custom endpoints
Anything that speaks OpenAI's /chat/completions can back Eltes: Ollama, vLLM, LiteLLM, LM Studio, a Cloudflare Worker or your team gateway. Set a base URL, a model id and — only if your server requires one — a token.
- Open Settings → API keys → Custom endpoint.
- Enter the base URL, including
/v1when that is what your server expects. - Enter the model id exactly as the server reports it.
- Pick Custom endpoint from the model picker and send a message.
# 1. run a model
ollama run llama3.1
# 2. in Eltes → Settings → Custom endpoint
Base URL http://localhost:11434/v1
Model id llama3.1
# Ollama allows browser origins from localhost by default.
# For another device, set OLLAMA_ORIGINS to your static host.
OLLAMA_ORIGINS="https://eltes.example" ollama serve
When CORS blocks the browser
Configure the optional Vercel backend described in Self-hosting, or run Eltes with npm start for the local API during development. The proxy forwards the user's key for that request and only allows fixed provider targets. Do not paste a provider key into a source file, public static host, or shared deployment environment variable.
http://localhost endpoint will be blocked by mixed-content rules. Serve Eltes locally for local models, or expose the model over HTTPS.
Models
HTML code fences include a sandboxed preview, copy and download actions. Preview scripts run in an isolated frame with network access blocked.
NVIDIA NIM uses https://integrate.api.nvidia.com/v1 and a key from NVIDIA Build. Catalog access and any free quota depend on NVIDIA's account and model availability; these models are not guaranteed to be free.
The catalogue ships 36 models across 13 providers. Each hosted entry is a real API target with its own context window, tier and capability flags. Nothing is simulated, so a hosted model only answers when its provider is connected. The three Eltes CAI entries run locally instead.
| Provider | Models | Best at |
|---|---|---|
| Eltes CAI | CAI-1, CAI-1 Fast, CAI Docs | On-device chat, Eltes help, maths, units, dates |
| OpenAI | GPT‑6 Sol, GPT‑6.1 Sol, GPT‑6 Luna, GPT‑4.1 | Reasoning, coding and vision |
| Anthropic | Claude Sonnet 5.5, Opus 5.5, Haiku 5.5, Fable 5.1, Opus 4.5 | Code, long context, careful writing |
| DeepSeek | V4.1 Flash, V4 Pro | Cost, 1M context, multimodal |
| Gemini 2.5 Pro, 2.5 Flash | 1M context, multimodal | |
| Z.ai | GLM-4.6, GLM-4.5, GLM-4.5 Air | Agentic coding, low cost |
| xAI | Grok 4, Grok 3 | Direct answers, reasoning |
| Qwen | Qwen3 Max, Qwen3 Coder Plus | Bilingual, repository-scale code |
| Mistral | Mistral Large, Codestral | Function calling, completion |
| Groq | Llama 3.3 70B, Llama 4 Scout | Very high tokens per second |
| NVIDIA NIM | Llama 3.3 70B, Nemotron Ultra | Open models via NVIDIA-hosted NIM; key and model access required |
| OpenRouter | Auto, Claude Sonnet 5.5, Gemini 2.5 Pro, GLM-4.6, DeepSeek Flash, Qwen3 Coder | One key, routing and fallbacks |
| Custom | Whatever your endpoint serves | Local and private models |
Eltes CAI — our own models
CAI (conversational agent intelligence) is a small deterministic model implemented in assets/js/cai.js. It runs inside the tab with no key and no network. It is accurate on its domain and explicit about its limits; it never pretends to browse.
| Model | Strength | Example |
|---|---|---|
| Eltes CAI-1 | General on-device chat | “Which model should I use for code?” |
| Eltes CAI-1 Fast | Shorter answers, quicker pacing | “Convert 68 °F to °C” |
| Eltes CAI Docs | Eltes setup, keys, accounts, downloads | “How do I connect a DeepSeek key?” |
CAI covers Eltes documentation, model recommendations from the live catalogue, arithmetic and percentages, unit and temperature conversion, the current date and time, word counts and a glossary of AI terms. Anything outside that gets an honest fallback with a suggestion to switch to a hosted model.
Real model streaming, including Claude
Eltes streams from provider APIs when Settings → General → Stream responses is enabled. Claude uses Anthropic’s native Messages API; OpenAI-compatible providers stream chat-completion events. Replies are never simulated for hosted models.
- Save a valid provider API key in Settings → API keys. Eltes checks locally for a key before sending and opens a connect prompt if it is missing.
- Claude Sonnet 5.5 uses Anthropic SSE and paints tokens as they arrive. OpenAI-compatible providers use SSE with a live cursor and first-token metrics.
- 401/403, 429, model-not-found and CORS/network failures have separate retry guidance.
- Eltes does not include hosted model credits or determine whether a provider account has free quota. CAI is the free, on-device option; hosted usage follows provider terms and billing.
Per-chat models
The model is stored on the conversation, not globally. Switch mid-thread and the next reply uses the new model while the history stays intact. New chats use the default model chosen in Settings → Models. Deep links such as app.html?model=glm-4-6 preselect a model from the landing page.
Agent & tools
Tools are real function calls, not a scripted animation. Turn on Tools in the composer (or set it as the default in Settings) and the model may call one of three local tools. Eltes executes the call in the page, returns the result to the model, and lets it continue — up to four rounds.
| Tool | Input | What it does |
|---|---|---|
calculate | { expression } | Evaluates arithmetic in a restricted sandbox — digits and operators only |
current_time | { timezone? } | Returns the current date and time, optionally in an IANA timezone |
search_history | { query } | Searches saved conversations locally and returns titles plus snippets |
What the timeline shows
- Thinking while the request is open, then streaming once tokens arrive.
- Tool cards with the tool name, arguments, a running/done state and the runtime in milliseconds.
- Metrics after the turn: first-token latency, total time, tokens in/out and tokens per second.
- Model and provider chips on every assistant message, so a long thread with switches stays readable.
Tool calling support
Tools are only offered when the selected model advertises support. Models without tool support — the Eltes CAI family and completion models such as Codestral — are sent the plain conversation. If a provider rejects the tool schema, the error card reports it and retry without tools still works.
Chats & export
Conversations are grouped into Today, Yesterday, Previous 7 days, Previous 30 days and Older. Pinned chats sit on top. Every row has a menu with rename, pin, clear, export and delete.
Editing and retrying
- Edit a user message to rewind the thread to that point and put the text back in the composer.
- Retry re-runs the last turn with the current model; the failed turn is kept out of the history.
- Copy works on replies and on each code block independently.
- Feedback thumb buttons are stored locally and never uploaded.
Exports
| Format | Contains | Use it for |
|---|---|---|
| Markdown | Full transcript with model, tool runtimes and token counts | Docs, issues, sharing in a repo |
| JSON (one chat) | Raw conversation object | Debugging or your own tooling |
| JSON (all chats) | Chats and settings, keys stripped | Backups and moving devices |
Settings
Open with ⌘, or the gear in the top bar. Settings are stored in eltes.settings.v1 and apply instantly.
| Panel | Controls |
|---|---|
| General | Default model, streaming, send key, tools by default, density, reduced motion, timestamps, metrics |
| API keys | Provider keys, reveal/save/remove and provider key test through configured proxy, Vercel/local backend URL, custom model endpoint |
| Models | Star favourites, choose the default model for new chats |
| Account | Profile, display name, sign out, delete profile, plan |
| Data | Chat count, storage meter, export all, import, delete all chats |
| About | Version, model count, shortcuts and links |
Storage meter
The sidebar footer shows how much of the browser's local storage the app is using. The meter is deliberately conservative: browsers commonly allow about 5 MB per origin, and large attachments or very long threads consume it fastest.
Keyboard shortcuts
On Windows and Linux, use Ctrl wherever ⌘ is shown. The interface relabels the modifier automatically.
Downloads
Everything Eltes ships is static. The Windows download is the same site wrapped in a portable launcher, so the only requirement is a way to serve files locally.
eltes-windows/
├── eltes.bat # double-click to launch
├── Start-Eltes.ps1 # the actual server + browser open
├── README-WINDOWS.txt
├── index.html
├── app.html
├── auth.html
├── docs.html
└── assets/ … # css, js, fonts, icons
What the launcher does
- Changes into its own folder, so it works from any drive or USB stick.
- Finds
python,py -3ornode. - Starts a static server bound to
127.0.0.1:4173. - Waits for the port, then opens your default browser at the workspace.
- Leaves the server running until you close the console window.
eltes.bat in Notepad before running it. There is no installer, no elevation prompt and no outbound connection; the server listens on localhost only.
Optional Start-menu shortcut
:: create a shortcut in the Start menu
eltes.bat --shortcut
:: use a different port (default 4173)
eltes.bat --port 8080
macOS and Linux
Clone or unpack the same files and serve the root:
python3 -m http.server 4173
# then open http://127.0.0.1:4173/app.html
Install as an app
site.webmanifest declares standalone display, a dark theme colour and maskable icons at 192 and 512 px. Chrome, Edge and Safari can install the page as a window with the green-cloud icon; there is no Electron wrapper and no auto-updater.
localStorage. Chats, keys and your profile live in the browser, not in the download.
Pricing
The static Eltes workspace is free. Hosted replies require your own API key and may incur provider charges at its published rates. Eltes does not supply free hosted credits. CAI runs on-device without a key or per-token cost. There is no markup, subscription or card on file. A hosted tier with managed keys is in design; the pricing section shows pricing not announced because nothing is purchasable yet.
| Plan | Status | What you get |
|---|---|---|
| BYOK | Available now · $0 | Every model, tool loop, local history, accounts, exports. You pay the provider. |
| Hosted | In design | Managed keys and sync without handling credentials. Pricing not announced. |
| Self-hosted | Available now · $0 | Clone the repo and serve the static build wherever you like. |
Self-hosting
Publish with GitHub Pages
The repository’s .github/workflows/pages.yml workflow assembles and deploys only the static site files to GitHub Pages. Subsequent updates publish through the repository workflow. GitHub Pages serves HTML, CSS, JavaScript, images and the Windows download; it does not run the API backend, store secrets, proxy model requests, or grant provider free-tier access.
Without a configured proxy, users save keys in their browser and provider APIs receive requests directly from that browser, subject to provider CORS. Eltes also includes Node-based local API routes for development and separate Vercel functions for deployment.
Run the local site and API
With Node.js 18 or newer, run npm install and npm start, then open http://localhost:4173/app.html. This serves the static site and the three local API routes. The server binds to 0.0.0.0 for development-preview compatibility and has no user authentication; keep it on a trusted network. Use HOST=127.0.0.1 npm start to bind only to loopback. A Python static server serves only files, so it does not provide key testing or proxy routes.
Deploy the optional proxy to Vercel
- Import the repository into Vercel with the project root as the root directory. The functions are in
api/;vercel.jsonsets a 60-second function limit. - Set the project environment variable
ELTES_ALLOWED_ORIGINSto exact comma-separated browser origins, without paths or trailing slashes. Example:https://gefrus112.github.io,http://localhost:4173. - Deploy, then in Eltes Settings → API keys → Optional Vercel/local backend, enter the Vercel deployment origin and use Save and test.
The available routes are /api/health, /api/key-check, and streaming/non-streaming /api/chat. The backend only contacts fixed provider hosts and allowlisted provider/model pairs; it never accepts an arbitrary upstream URL. CORS origin checks are not authentication; these routes are public to non-browser clients unless you add access controls. Do not offer an unprotected shared proxy to untrusted users without adding authentication and rate limiting. The key transits the backend for each call; the function code does not persist or log it, but the platform and provider process the request. Use only a deployment you trust and set provider spending limits. Static hosting and Vercel backend deployment are separate; this documentation does not mean a deployment has already occurred.
eltes/
index.html # marketing home
app.html # the workspace
auth.html # local accounts
docs.html # this page
assets/
css/ # theme, landing, app, docs, auth
js/ # models, brands, icons, app engine
fonts/ # Inter + JetBrains Mono, self-hosted
img/ # favicon and social card
site.webmanifest
LICENSE · NOTICE.md
Deployment checklist
- Serve over HTTPS in production. The clipboard API and some provider endpoints require a secure context.
- Keep every asset path relative so the app works from a subdirectory.
- Do not add analytics that read
localStorage; it holds keys. - If you add a content security policy, allow
connect-srcfor each provider you use.
Privacy
Eltes includes no analytics, telemetry, or third-party font requests. In direct mode, a model request goes from your browser to the selected provider. If you configure the optional backend, key checks and model requests transit that backend; the function code does not persist or log provider keys, but the backend platform processes request metadata and the key while handling the call.
What Eltes stores
eltes.chats.v1 — conversations and per-message metadatalocaleltes.settings.v1 — provider keys, backend URL, model default, preferenceslocaleltes.account.v1 — optional profilelocaleltes.waitlist.v1 — pricing waitlist entries, if you joinlocalWhat providers see
Your prompt, the conversation history sent with it, and whatever tool definitions or results are included in that request. That is exactly what any chat client must send to get an answer. Eltes adds no account identifier, because it does not have one to add.
Deleting data
Use Settings → Data to delete all chats, or Account → Delete profile to remove the profile. Keys can be removed individually. Clearing site data for the origin removes everything at once.
FAQ
Why is there no demo mode?
A simulated answer is indistinguishable from a wrong one. Eltes would rather show a connect card or a typed error than pretend a model replied. If you want to try the interface without a paid provider, run Ollama locally and use a custom endpoint.
Do I need an account?
No. Accounts are optional profiles. The workspace, keys, history and exports work without one. Create a profile only if you want a name and avatar attached.
Is it safe to paste an API key into a web page?
Only on a page you trust and control. Eltes is static and self-hostable, so you can audit every line and serve it yourself. Keys sit in localStorage, which any script on the origin can read — do not load untrusted third-party scripts alongside it.
Why does a provider fail with a network error?
Usually CORS. The provider must allow the browser origin in direct mode. See Providers & CORS, or configure the optional Vercel/local backend in Settings → API keys.
Which models can use tools?
Models with tool support in the catalogue: Claude 5.5 and 4.5, GPT‑6 Sol/Luna, GPT‑4.1, Gemini 2.5, GLM-4.x, DeepSeek V4.1/V4, Grok, Qwen3, Mistral Large, Llama 3.3/4 and most OpenRouter routes. The Eltes CAI models run their own local intents and Codestral is sent the plain conversation.
Can I use it offline?
Yes, partly. Eltes CAI-1 answers chat, Eltes questions, arithmetic, units and dates with no network at all. Hosted models need a connection, unless you point a custom endpoint at a model running on your machine. The Windows launcher serves the whole site locally.
Can I sign up with OpenAI, Anthropic or DeepSeek?
Yes. The account page offers all three. On a static copy, connecting means validating and storing a real provider key in this browser. On a hosted deployment with ELTES_CONFIG.authEndpoint, the same buttons start a real OAuth redirect. GitHub sign-up has been removed.
What is the Windows .bat file?
eltes.bat is a readable launcher included in eltes-windows.zip. It finds Python or Node, starts a static server on 127.0.0.1:4173 and opens the workspace. It does not install a service, touch the registry or need admin rights.
What happens to my chats if I clear site data?
They are gone. localStorage is the only copy. Export a JSON backup from Settings → Data first if the thread matters.
Ready to connect a real model?
Bring one key. Eltes does the rest — streaming, tools, metrics and history that stays here.