# whiskr ![screenshot](.github/chat.png) whiskr is a private, self-hosted web chat interface for interacting with AI models via [OpenRouter](https://openrouter.ai/). ## Features - Private and self-hosted (data stored in localStorage) - Supports any model available on your OpenRouter account - Real-time streaming responses - Edit, delete, or copy any message - Persistent settings for model, temperature, and other parameters - Full conversation control including clearing and modifying messages - Title generation (and refresh) - Smooth UI updates with [morphdom](https://github.com/patrick-steele-idem/morphdom), selections, images, and other state are preserved during updates - Easy model selection: - Tags indicate if a model supports **tools**, **vision**, or **reasoning** - Search field with fuzzy matching to quickly find models - Models are listed newest -> oldest - Web search tools (set the `tokens.exa` to enable): - `search_web`: search via Exa in auto mode; returns up to 10 results with short summaries - `fetch_contents`: fetch page contents for one or more URLs via Exa /contents - `github_repository`: get a quick overview of a GitHub repository (repo info, up to 20 branches, top-level files/dirs, and the README) without cloning (optionally set `tokens.github` for higher rate limits and private repos) - Images attachments for vision models using simple markdown image tags - Text/Code file attachments - Reasoning effort control - Structured JSON output - Statistics for messages (provider, ttft, tps, token count and cost) - Import and export of chats as JSON files - Authentication (optional) ## TODO - improved custom prompts - collapse messages - user defined timezone - settings - auto-retry on edit - ctrl+enter vs enter for sending - multiple chats ## Built With **Frontend** - Vanilla JavaScript and CSS - [morphdom](https://github.com/patrick-steele-idem/morphdom) for DOM diffing without losing state - [marked](https://github.com/markedjs/marked) for Markdown rendering - [highlight.js](https://highlightjs.org/) for syntax highlighting - Fonts: [Inter](https://rsms.me/inter/) (UI), [Comic Code](https://tosche.net/fonts/comic-code) (code) - Icons: [SVGRepo](https://www.svgrepo.com/) - Color palette: [Catppuccin Macchiato](https://catppuccin.com/) **Backend** - Go - [chi/v5](https://go-chi.io/) for the http routing/server - [OpenRouter](https://openrouter.ai/) for model list and completions - [Exa](https://exa.ai/) for web search and content retrieval (`/search`, `/contents`) ## Getting Started 1. Copy `example.config.yml` to `config.yml` and set `tokens.openrouter`: ```bash cp example.config.yml config.yml ``` 2. Build and run: ```bash go build -o whiskr ./whiskr ``` 3. Open `http://localhost:3443` in your browser. ## Authentication (optional) whiskr supports simple, stateless authentication. If enabled, users must log in with a username and password before accessing the chat. Passwords are hashed using bcrypt (12 rounds). If `authentication.enabled` is set to `false`, whiskr will not prompt for authentication at all. ```yaml authentication: enabled: true users: - username: laura password: "$2a$12$cIvFwVDqzn18wyk37l4b2OA0UyjLYP1GdRIMYbNqvm1uPlQjC/j6e" - username: admin password: "$2a$12$mhImN70h05wnqPxWTci8I.RzomQt9vyLrjWN9ilaV1.GIghcGq.Iy" ``` After a successful login, whiskr issues a signed (HMAC-SHA256) token, using the server secret (`tokens.secret` in `config.yml`). This is stored as a cookie and re-used for future authentications. ## Nginx (optional) When running behind a reverse proxy like nginx, you can have the proxy serve static files. ```ngnix server { listen 443 ssl; server_name chat.example.com; http2 on; root /path/to/whiskr/static; location / { index index.html index.htm; etag on; add_header Cache-Control "public, max-age=2592000, must-revalidate"; expires 30d; } location ~ ^/- { proxy_pass http://127.0.0.1:3443; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $host; } ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; } ``` ## Usage - Send a message with `Ctrl+Enter` or the send button - Just add a message with the add button - Hover over a message to edit, delete, or copy it - Adjust model, temperature, prompt, or message role from the controls in the bottom-left - Use the model search field to quickly find models (supports fuzzy matching) - Look for tags in the model list to see if a model supports tools, vision, or reasoning - Use `![alt](url)` in your message to display an image inline. If the model supports vision, the same image URL is passed to the model for multimodal input. ## License GPL-3.0 see [LICENSE](LICENSE) for details.