Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
86895e1
server: initial attempt
fa-sharp Feb 16, 2026
3c2da7d
server: refactor stream and persist logic out of API controller
fa-sharp Feb 16, 2026
08b0f26
Update chat.rs
fa-sharp Feb 16, 2026
36fae7a
tweaks
fa-sharp Feb 16, 2026
f6c279d
rename
fa-sharp Feb 16, 2026
012c3c0
perf: use pipelined database queries where possible
fa-sharp Feb 16, 2026
e4f37c6
Update db.rs
fa-sharp Feb 16, 2026
067c02e
Update db.rs
fa-sharp Feb 16, 2026
8123029
fix data url decoding and saving
fa-sharp Feb 16, 2026
76e725a
web: enable image responses (more work needed)
fa-sharp Feb 17, 2026
6d6f30c
server: switch back to using default model for session title generation
fa-sharp Feb 17, 2026
25d89fb
update readmes
fa-sharp Feb 18, 2026
d5a6fab
server: update tinistream client
fa-sharp Feb 18, 2026
646edb7
web: allow attaching files for all models
fa-sharp Feb 18, 2026
39b42d7
Update utils.rs
fa-sharp Feb 18, 2026
361c7f4
web: sort models
fa-sharp Feb 18, 2026
871b133
server: add tinirun client dep
fa-sharp Feb 27, 2026
e9239f9
server: add openai subtype for possible future use
fa-sharp Feb 27, 2026
81d3dd6
server: add code runner to docker compose
fa-sharp Feb 27, 2026
6e0027c
server: implement tinirun as code runner, remove unneeded deps
fa-sharp Mar 1, 2026
40049f9
add new server
fa-sharp Jun 22, 2026
427beb4
add database
fa-sharp Jun 22, 2026
4754b5c
service layout, login and logout
fa-sharp Jun 23, 2026
41d0935
auth tweaks
fa-sharp Jun 23, 2026
be2d722
add diesel jsonb derive macro
fa-sharp Jun 23, 2026
64c2502
re-arrange
fa-sharp Jun 23, 2026
b47750a
refactor config, add redis
fa-sharp Jun 23, 2026
7a13e02
cache sessions in redis
fa-sharp Jun 23, 2026
b651198
redis shutdown
fa-sharp Jun 23, 2026
19caaf4
rename /routes to /api
fa-sharp Jun 23, 2026
da61e5e
oauth
fa-sharp Jun 23, 2026
47382f9
oauth tweaks
fa-sharp Jun 23, 2026
3ef6526
oauth refactor
fa-sharp Jun 23, 2026
1d7a200
add google oauth
fa-sharp Jun 23, 2026
3c65280
refactor oauth
fa-sharp Jun 23, 2026
4e36b98
refactor auth errors
fa-sharp Jun 24, 2026
700f97f
Update config.rs
fa-sharp Jun 25, 2026
4ac1654
extract oauth library
fa-sharp Jun 27, 2026
469cbc8
session cleanup task
fa-sharp Jun 27, 2026
42e33f3
add oidc, moar oauth tweaks
fa-sharp Jun 27, 2026
002617a
moar oauth tweaks
fa-sharp Jun 28, 2026
398458b
use extracted simple-oauth crate
fa-sharp Jun 28, 2026
2539d7e
add OIDC provider
fa-sharp Jun 28, 2026
1e69b36
chat service - checkpoint
fa-sharp Jun 29, 2026
0c3740e
chat service - checkpoint 2
fa-sharp Jun 30, 2026
900b575
chat service - http clients
fa-sharp Jun 30, 2026
dd74bb0
chat service - full flow
fa-sharp Jul 1, 2026
65f91f9
tweaks
fa-sharp Jul 1, 2026
d85e44f
add lorem llm provider, moar auth tweaks
fa-sharp Jul 1, 2026
a653b60
Update oauth.rs
fa-sharp Jul 1, 2026
24476b9
improve session messages DB query
fa-sharp Jul 1, 2026
7558830
fix OAuth setup
fa-sharp Jul 1, 2026
22ad88b
add openapi with utoipa
fa-sharp Jul 1, 2026
ed0ce5c
db: add openai subtype
fa-sharp Jul 2, 2026
d50e4f3
avoid extra allocation in stream chunks
fa-sharp Jul 3, 2026
1bcfdfd
tweaks
fa-sharp Jul 3, 2026
1160e04
add auth config
fa-sharp Jul 3, 2026
55cbe40
add api keys
fa-sharp Jul 4, 2026
cf001b8
add proxy header auth
fa-sharp Jul 5, 2026
224c42a
extract config defaults
fa-sharp Jul 5, 2026
bea7d13
api doc - checkpoint
fa-sharp Jul 6, 2026
f398c25
api doc - checkpoint 2
fa-sharp Jul 6, 2026
1506b7f
api doc completed
fa-sharp Jul 6, 2026
433fabb
api doc tweaks
fa-sharp Jul 6, 2026
8b08455
api doc: add errors
fa-sharp Jul 6, 2026
cd3b71d
api doc: add api key auth
fa-sharp Jul 6, 2026
6aa5d45
api doc: tweaks
fa-sharp Jul 6, 2026
2333e9c
api doc: add responses to macro
fa-sharp Jul 6, 2026
9176fe4
api doc: more macro tweaks
fa-sharp Jul 6, 2026
41b70af
Update config.rs
fa-sharp Jul 6, 2026
90c1f24
add provider routes
fa-sharp Jul 7, 2026
119e7a1
list user's auth sessions
fa-sharp Jul 7, 2026
7db2827
list models from models.dev
fa-sharp Jul 7, 2026
292b0f5
list models from ollama
fa-sharp Jul 7, 2026
9b315d1
ollama model tweaks
fa-sharp Jul 7, 2026
7899099
update tinistream and oauth crates
fa-sharp Jul 14, 2026
502f00e
update aide macros crate
fa-sharp Jul 14, 2026
8987944
update axum_plugin crate
fa-sharp Jul 14, 2026
aad0303
update all deps
fa-sharp Jul 14, 2026
67a1119
readability tweaks
fa-sharp Jul 14, 2026
3a98590
add chat session routes
fa-sharp Jul 14, 2026
fdf0a1c
allow regenerating LLM response
fa-sharp Jul 14, 2026
666925b
add anthropic and ollama providers
fa-sharp Jul 15, 2026
8a9635a
add website files
fa-sharp Jul 15, 2026
35dcc92
add more chat/stream api routes
fa-sharp Jul 15, 2026
cf297af
add request logs
fa-sharp Jul 15, 2026
fd2f654
fix regeneration
fa-sharp Jul 15, 2026
e3f4417
fix order of operations in chat streams
fa-sharp Jul 15, 2026
b28edaf
rename llm_logs table
fa-sharp Jul 15, 2026
d1ceae7
accurate completion time for logs
fa-sharp Jul 15, 2026
efeefbb
Update down.sql
fa-sharp Jul 15, 2026
ed4bf10
lint: address clippy warnings
fa-sharp Jul 15, 2026
3c3adb7
renaming log status for clarity
fa-sharp Jul 15, 2026
851b3d7
save request ID from provider request errors
fa-sharp Jul 15, 2026
26a1c59
ci: update
fa-sharp Jul 15, 2026
8871761
remove unnecessary derives
fa-sharp Jul 15, 2026
a0dfce9
Update chat.rs
fa-sharp Jul 15, 2026
05a637a
use i32 for token count for consistency
fa-sharp Jul 15, 2026
85933fb
llm request log improvements
fa-sharp Jul 16, 2026
81cb657
add ttft
fa-sharp Jul 16, 2026
c02711c
calculate ttft correctly for prompts
fa-sharp Jul 16, 2026
e57df97
re-arrange log metadata
fa-sharp Jul 16, 2026
fc71f5f
migrate llm logs from message metadata to table
fa-sharp Jul 16, 2026
3eb284e
checkpoint: file storage
fa-sharp Jul 17, 2026
81b87ba
checkpoint: file database models and repository
fa-sharp Jul 17, 2026
521729f
db query logging
fa-sharp Jul 18, 2026
2b8f7aa
storage routes
fa-sharp Jul 18, 2026
4f35bf3
local file storage: clean up folders when deleting files
fa-sharp Jul 18, 2026
7cead1e
Update local.rs
fa-sharp Jul 18, 2026
554f10c
update deps
fa-sharp Jul 18, 2026
8ef9262
s3 upload
fa-sharp Jul 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file removed .docker/.gitkeep
Empty file.
33 changes: 12 additions & 21 deletions .github/workflows/server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ on:
tags-ignore: ["v*"]
paths:
- "server/**"
- "server-new/**"
- "web/**"
- Dockerfile
pull_request:
types: [opened, synchronize, reopened]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
RUST_VERSION: "1.90"
RUST_VERSION: "1.96"
NODE_VERSION: 22

jobs:
Expand All @@ -23,14 +24,14 @@ jobs:
run:
working-directory: ./web
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
package_json_file: web/package.json
run_install: false
- name: Install Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: "pnpm"
Expand All @@ -46,35 +47,25 @@ jobs:
run: pnpm build

build-server:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
name: Build server on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
name: Build server
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./server
working-directory: ./server-new
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install Rust toolchain
run: rustup toolchain install ${{ env.RUST_VERSION }} --profile minimal --no-self-update && rustup default ${{ env.RUST_VERSION }}
- name: Setup build dependencies on macOS
if: startsWith(runner.os, 'macOS')
run: brew link --force libpq
- name: Setup rust-cache
uses: Swatinem/rust-cache@v2
with:
workspaces: ./server
# - name: Install nextest
# uses: taiki-e/install-action@v2
# with:
# tool: nextest@0.9
workspaces: ./server-new

- name: Run cargo check
run: cargo check --profile ci
run: cargo check

- name: Run cargo build
run: PQ_LIB_DIR="$(brew --prefix libpq)/lib" cargo build --profile ci
run: cargo build

# - name: Test crate
# run: cargo nextest run --all-features --profile ci
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Desktop.ini
*.pfx
secrets.json
config/secrets.yml
.docker/
.secrets/

# Dependency Directories
Expand Down
19 changes: 6 additions & 13 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,10 @@ RsChat uses a hybrid streaming architecture that provides both real-time perform

### Key Components

#### 1. LlmStreamWriter (`server/src/stream/llm_writer.rs`)
#### 1. LlmClientStreamer (`server/src/stream/streamer.rs`)

The core component that processes LLM provider streams and manages Redis stream output.

**Key Features:**
- **Batching**: Accumulates chunks from the provider stream, up to a max length or timeout, and adds them to the Redis stream
- **Background Pings**: Sends regular keepalive pings

#### 2. Redis and SSE Stream Structure

**Redis Key for Chat Streams**: `user:{user_id}:chat:{session_id}`
Expand Down Expand Up @@ -68,32 +64,29 @@ Stream End → Database Save → Redis DEL

#### Cross-Instance Support
- Redis streams provide shared state across server instances
- Background ping tasks maintain stream liveness
- Stream cancellation detected via Redis XADD failures
- Stream cancellation detection

## Data Flow

### 1. New Chat Request
```
Client → POST /api/chat/{session_id}
→ Send request to LLM Provider
→ LLM response received, streamed to Redis with the `LlmStreamWriter`
→ GET /api/chat/{session_id}/stream to connect to the stream and stream the response
→ LLM response received, streamed to Redis with the `LlmClientStreamer`
```

### 2. Stream Processing
```
LLM Chunk → Process text, tool calls, usage, and error chunks
→ Batching Logic
Redis XADD (if conditions met)
→ Client(s) receive the new chunks
Add chunks to Redis via `tinistream` service
→ Client(s) receive the new chunks from `tinistream`
```

### 3. Stream Completion
```
LLM End → Final Database Save
→ Redis Stream End Event
→ Redis Stream Cleanup
→ `tinistream` End Event
→ SSE Connection Close
```

Expand Down
48 changes: 32 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RsChat 🤖💬

A fast, secure, self-hostable chat application built with Rust, TypeScript, and React. Chat with multiple AI providers using your own API keys, with real-time streaming built-in.
A lightweight, secure, open-source, self-hostable chat application built with Rust, TypeScript, and React. Stream chats with multiple AI providers using your own API keys.

Demo link: https://rs-chat.fly.dev/ (⚠️ This is a demo - don't expect your account/chats to be there when you come back. It may intermittently delete all data. Please also don't enter any sensitive information or confidential data)

Expand All @@ -11,21 +11,20 @@ Demo link: https://rs-chat.fly.dev/ (⚠️ This is a demo - don't expect your a
- **Multiple AI Providers**: Chat with AI models from OpenAI, Anthropic, and OpenRouter
- **Streaming**: Streams responses using SSE (Server-Sent Events)
- **Concurrent Streaming**: Seamlessly switch between multiple AI conversations streamed at the same time
- **Resumable Conversations**: Resume the conversation if your connection is lost or the page is refreshed
- **Resilient Streams**: Streaming continues if your connection is lost or the page is refreshed
- **Code Highlighting**: Beautiful syntax highlighting for code blocks using [`rehype-highlight`](https://github.com/rehypejs/rehype-highlight)
- **Dark Mode**: Dark/light theme support
- **Responsive Design**: Mobile-friendly layout
- **Search Chats**: Full-text search of chat session titles and messages
- **Fast and Memory Efficient**: Rust backend using the [Rocket framework](https://rocket.rs/)
- **Users & Authentication**: Login via OAuth providers (Google, GitHub, etc.), custom OIDC, and SSO header authentication
- **API Key Access and OpenAPI Docs**: API key access and documentation at `/api/docs` for developers to integrate with RsChat
- **Documented API**: API key access and documentation at `/api/docs` for developers to integrate with RsChat
- **Fully Type-Safe**: End-to-end type safety with auto-generated client from OpenAPI spec

### ⚡ Convenience Features

- **Smart Titles**: Auto-generation of chat titles
- **Smart Scrolling**: Auto-scroll during streaming and when opening previous chats
- **Secure Key Storage**: Your API keys are saved and encrypted
- **Auto Scrolling**: Auto-scroll during streaming and when opening previous chats

## 🏗️ Architecture

Expand All @@ -45,14 +44,17 @@ rs-chat/
│ │ ├── auth/ # Authentication services
│ │ ├── db/ # Database models and services
│ │ ├── provider/ # AI provider integrations
│ │ ├── utils/ # Utility functions
│ │ ├── config.rs # Reading configuration / env variables
│ │ ├── storage/ # File storage services
│ │ ├── stream/ # Streaming utilities
│ │ ├── tools/ # AI chat tools
│ │ ├── utils/ # Other utilities
│ │ ├── config.rs # Configuration / environment variables
│ │ ├── lib.rs # Server setup
│ │ ├── main.rs # Server entry point
│ │ └── ... # Other modules
│ ├── migrations/ # Database migrations
│ └── Cargo.toml # Rust dependencies
├── web/ # Vite / React frontend
├── web/ # Vite / React / TanStack Router frontend
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── routes/ # TanStack Router routes
Expand Down Expand Up @@ -90,9 +92,9 @@ Your API keys are encrypted and stored in the database.
cd rs-chat
```

2. **Start development databases**
2. **Start development services**
```bash
docker compose up -d db redis
docker compose up -d db redis stream
```

3. **Set up the backend**
Expand Down Expand Up @@ -145,33 +147,47 @@ You'll need an environment with PostgreSQL and Redis (or Redis-compatible databa
services:
rschat:
image: ghcr.io/fa-sharp/rs-chat:latest
# ports:
# - "8080:8080"
ports:
- "8080:8080"
environment:
RUST_LOG: warn # 'info' or 'debug' for more logs
RS_CHAT_SERVER_ADDRESS: https://mydomain.com # where you're hosting the app
RS_CHAT_DATABASE_URL: postgres://user:pass@mypostgres/mydb # Your PostgreSQL URL
RS_CHAT_REDIS_URL: redis://myredis:6379 # Your Redis URL
RS_CHAT_SECRET_KEY: your-secret-key-for-encryption # 64-character hex string
RS_CHAT_TINISTREAM_URL: http://tinistream:8081
RS_CHAT_TINISTREAM_API_KEY: tinistream-api-key # API key for the tinistream service

## For GitHub login: callback URL should be {your_server_address}/api/auth/login/github/callback
# RS_CHAT_GITHUB_CLIENT_ID: your-github-client-id
# RS_CHAT_GITHUB_CLIENT_SECRET: your-github-client-secret
## Similar config for other OAuth providers - see server/src/auth/oauth/ folder
# RS_CHAT_DISCORD_CLIENT_ID: your-discord-client-id
# ...

## For SSO header auth - see server/src/auth/sso_header.rs for all config options
# RS_CHAT_SSO_HEADER_ENABLED: true
# RS_CHAT_SSO_USERNAME_HEADER: X-Remote-User
# ...

## For running code on a remote Docker host
# DOCKER_HOST: tcp://remote-docker-host:port
# DOCKER_TLS_VERIFY: 1
# DOCKER_CERT_PATH: /certs
volumes:
## For running code on local Docker host
# - /var/run/docker.sock:/var/run/docker.sock:ro
## Certificates for remote Docker host
# - ./path/to/certs:/certs
# - /var/run/docker.sock:/var/run/docker.sock:ro # To run code on local Docker
# - ./path/to/certs:/certs # Certificates for remote Docker host
tinistream:
image: ghcr.io/fa-sharp/tinistream:latest
container_name: tinistream
ports:
- "8081:8081"
environment:
STREAMER_PORT: 8081
STREAMER_SERVER_ADDRESS: http://localhost:8081
STREAMER_REDIS_URL: redis://myredis:6379 # Your Redis URL
STREAMER_API_KEY: tinistream-api-key # should match the RS_CHAT_TINISTREAM_API_KEY above
STREAMER_SECRET_KEY: your-secret-key-for-encryption # 64-character hex string
```

## 🔒 Security & Privacy
Expand Down
69 changes: 43 additions & 26 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,65 @@ services:
- postgres_data:/var/lib/postgresql/data

redis:
image: valkey/valkey:7-alpine
image: valkey/valkey:8-alpine
container_name: redis
ports:
- "6379:6379"
volumes:
- redis_data:/data

stream:
image: ghcr.io/fa-sharp/tinistream:0.1.9
platform: linux/amd64
image: tinistream
container_name: tinistream
ports:
- "8081:8081"
environment:
STREAMER_PORT: 8081
STREAMER_SERVER_ADDRESS: http://localhost:8081
STREAMER_API_KEY: dev-streamer-api-key
STREAMER_BASE_URL: http://localhost:8081
STREAMER_REDIS_URL: redis://redis:6379
STREAMER_TTL: 360
env_file: server/.env
STREAMER_KEY_PREFIX: "rs-chat:"
env_file: server-new/.env
depends_on:
- redis

rschat:
build:
context: .
ports:
- "8080:8080"
environment:
RUST_LOG: info
RS_CHAT_SERVER_ADDRESS: http://localhost:8080
RS_CHAT_DATABASE_URL: postgres://postgres:postgres@postgres/postgres
RS_CHAT_REDIS_URL: redis://redis:6379
RS_CHAT_DATA_DIR: /data
RS_CHAT_TINISTREAM_URL: http://tinistream:8081
env_file: server/.env
volumes:
- ./.docker:/certs
- rschat_data:/data
depends_on:
- db
- redis
- stream
# runner:
# image: ghcr.io/fa-sharp/tinirun:0.1.1
# container_name: tinirun
# ports:
# - "8082:8082"
# environment:
# RUNNER_HOST: 0.0.0.0
# RUNNER_PORT: 8082
# RUNNER_LOG_LEVEL: info
# RUNNER_REDIS_URL: redis://redis:6379
# env_file: server/.env
# depends_on:
# - redis
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock

# rschat:
# build:
# context: .
# ports:
# - "8080:8080"
# environment:
# RUST_LOG: info
# RS_CHAT_SERVER_ADDRESS: http://localhost:8080
# RS_CHAT_DATABASE_URL: postgres://postgres:postgres@postgres/postgres
# RS_CHAT_REDIS_URL: redis://redis:6379
# RS_CHAT_DATA_DIR: /data
# RS_CHAT_TINISTREAM_URL: http://tinistream:8081
# RS_CHAT_TINIRUN_URL: http://tinirun:8082
# env_file: server/.env
# volumes:
# - rschat_data:/data
# depends_on:
# - db
# - redis
# - stream
# - runner

volumes:
postgres_data:
Expand Down
1 change: 1 addition & 0 deletions examples/compose/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# TODO
40 changes: 40 additions & 0 deletions examples/compose/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[server]
host = "0.0.0.0"
port = 8080
base_url = "https://example.com" # set to domain & path where you're hosting
log_level = "info"
request_id_header = "x-request-id"

[database]
url = "postgres://user:pass@mydb:5432"

[redis]
url = "redis://pass@myredis:6379"
pool_size = 4
timeout = 10

[services]
streamer_url = "http://tinistream:8081"
streamer_api_key = "tinistream-api-key"

[auth]
cookie_name = "auth-rs-chat"
session_length = 604800 # seconds

# Configure GitHub / Discord / Google login
[auth.github]
client_id = ""
client_secret = ""

# Configure forward header authentication
# ⚠️ Only use with a secure proxy like Authelia or Tinyauth
[auth.proxy]
enabled = false
username_header = "Remote-User"
name_header = "Remote-Name"
groups_header = "Remote-Groups"

# Configure various security settings
[security]
body_limit = 2097152 # bytes
request_timeout = 120 # seconds
Loading