No description
  • Ruby 46.2%
  • HTML 28.7%
  • CSS 14.8%
  • JavaScript 8.6%
  • Dockerfile 1.6%
  • Other 0.1%
Find a file
pi-coding-agent f46d0333f4
All checks were successful
CI / scan_ruby (push) Successful in 8s
CI / scan_js (push) Successful in 5s
CI / lint (push) Successful in 7s
CI / test (push) Successful in 6s
CI / system-test (push) Successful in 6s
Fix CI system-test job: add test/system scaffolding
bin/rails test:system passed the literal 'test/system' path to the
runner, which requires it as a file when the directory is missing,
raising LoadError. Add the test/system directory (with .keep) and the
standard test/application_system_test_case.rb so the task globs
test/system/**/*_test.rb, runs 0 tests, and exits 0. Ready for system
tests to be added later.
2026-08-19 20:16:54 +00:00
.github Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
app Fix RuboCop lint offenses (CI lint job) 2026-08-19 20:14:29 +00:00
bin Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
config Fix RuboCop lint offenses (CI lint job) 2026-08-19 20:14:29 +00:00
db Fix RuboCop lint offenses (CI lint job) 2026-08-19 20:14:29 +00:00
lib/tasks Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
log Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
public Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
script Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
storage Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
test Fix CI system-test job: add test/system scaffolding 2026-08-19 20:16:54 +00:00
tmp Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
vendor Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
.dockerignore Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
.env.example Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
.gitignore Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
.rubocop.yml Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
.ruby-version Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
config.ru Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
Dockerfile Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
Gemfile Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
Gemfile.lock Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
Rakefile Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00
README.md Initial commit: Ollama multi-user chat (Rails 8.1) 2026-08-17 21:59:56 +00:00

Ollama Chat

A small, minimalistic multi-user web chat for an Ollama server on your network. Built with Ruby on Rails 8, Turbo/Stimulus, and SQLite.

  • Multi-user via Authelia — the app sits behind your existing Authelia (or any compatible reverse proxy). It trusts the identity headers Authelia injects (Remote-User, Remote-Email, …) and auto-provisions users. There is no password code in the app.
  • Chat history — every chat and message is persisted. You own your chats.
  • Shared chat rooms — share a chat with other users on the same platform by username. Everyone in the room (1+ users + the AI) sees messages stream live.
  • Ollama web search — toggle per-chat. Sends Ollama's native search tool; the model can browse and cite sources (requires the Ollama server to have a search provider configured — see below).
  • Thinking/reasoning — optional per-chat toggle to show the model's reasoning trace alongside the answer.

Architecture

Browser ──(Authelia headers)──▶ Rails (Puma) ──▶ SQLite (primary)
                                  │
                                  ├── OllamaClient ──HTTP/NDJSON──▶ Ollama /api/chat
                                  │
                                  └── GenerateJob (Solid Queue) ──Turbo Streams──▶ room members
Concern Dev Production
Jobs :async Solid Queue (bin/jobs)
Cable/pubsub :async Solid Cable (SQLite)
Cache :memory_store Solid Cache (SQLite)
DB SQLite SQLite (primary/queue/cache/cable)

In production the Solid Queue worker is a separate process, so broadcasts from the worker reach web clients through Solid Cable (SQLite-backed pubsub).

Requirements

  • Ruby 3.3+, Bundler
  • An Ollama server reachable from the app (e.g. http://10.0.20.58:11434)
  • Authelia (or compatible) in front of the app, injecting identity headers

First run (development)

bundle install
bin/rails db:prepare

export OLLAMA_URL=http://10.0.20.58:11434
export OLLAMA_MODEL=qwen3.8:27b-mlx
export DEV_REMOTE_USER=alice      # dev-only fallback user (skips Authelia)

bin/rails server

Open http://localhost:3000. With DEV_REMOTE_USER set you're logged in as alice without Authelia — handy for local hacking. Remove it in production.

Configuration (environment variables)

Variable Default Purpose
OLLAMA_URL http://localhost:11434 Base URL of the Ollama server
OLLAMA_MODEL llama3.2 Default model for new chats (overridable per chat)
OLLAMA_CONNECT_TIMEOUT 5 Seconds to connect to Ollama
OLLAMA_READ_TIMEOUT 600 Seconds before a streaming response times out
AUTHELIA_USER_HEADER Remote-User Header carrying the username
AUTHELIA_EMAIL_HEADER Remote-Email Header carrying the email
AUTHELIA_NAME_HEADER Remote-Name Header carrying the display name
AUTHELIA_GROUPS_HEADER Remote-Groups Header carrying groups
TRUSTED_PROXIES empty Comma-separated CIDRs allowed to set identity headers. When empty, all sources are trusted (fine only if the app is only reachable via the proxy). Set this to your proxy's IP(s) to prevent header spoofing if the app is ever exposed directly.
DEV_REMOTE_USER empty Dev-only: username to impersonate when no header is present

Authelia setup

Authelia authenticates the user and forwards identity headers to the app. A typical reverse-proxy config (Caddy example, Authelia via forward_auth):

chat.example.com {
    forward_auth authelia:9091 {
        uri /api/authz/forward-auth
        copy_headers Remote-User Remote-Email Remote-Name Remote-Groups
    }
    reverse_proxy ollama_chat:3000
}

The app must only be reachable through the proxy. To harden it, set TRUSTED_PROXIES to the proxy's address so identity headers are ignored when a request does not come from the proxy:

export TRUSTED_PROXIES=10.0.0.5,10.0.0.0/24

The "Enable Ollama web search" toggle sends Ollama's built-in search tool (tools: [{"type":"search"}]). Web search actually happening depends on the Ollama server having a search provider configured — e.g. Ollama Cloud search, or a self-hosted provider such as SearXNG/Tavily set via the server's OLLAMA_SEARCH_PROVIDER/OLLAMA_SEARCH_API_KEY environment variables (see the Ollama web search docs).

If a particular model rejects the search tool, the client automatically retries without it so the chat keeps working.

Production

export RAILS_ENV=production
export OLLAMA_URL=http://10.0.20.58:11434
export OLLAMA_MODEL=qwen3.8:27b-mlx
export TRUSTED_PROXIES=<your proxy ip(s)>

bin/rails db:prepare          # creates primary/queue/cache/cable SQLite DBs
bin/rails server              # web (Puma + Thruster on :80)
bin/jobs                      # Solid Queue worker (in another process/container)

The included Dockerfile builds a production image (runs Thruster on port 80). Run the web container and a second container from the same image with command bin/jobs for the worker. Mount storage/ as a persistent volume.

Using the app

  • New chat — click + New chat (sidebar) or use the form on the home page. Pick a model; optionally add a system prompt, enable web search/thinking.
  • Settings — in a chat, the ⚙ button edits title/model/web-search/thinking/ system prompt (owner only).
  • Share — the 👥 button (owner only) adds members by username. Shared chats are live for all members: everyone sees user messages and the streamed AI reply. Non-owners see a "shared by …" badge and can leave the chat.
  • Delete — owner can delete a chat (removes it for everyone).

Project layout

app/
  controllers/  concerns/authentication.rb, chats/messages/memberships
  jobs/         generate_job.rb            # streams from Ollama → Turbo broadcasts
  lib/          authelia.rb, ollama_client.rb, chat_broadcaster.rb
  models/       user, chat, chat_membership, message
  channels/     application_cable/connection.rb  # ActionCable identity from headers
  views/        chats/, messages/
config/        cable.yml, database.yml, environments/production.rb