patrick-im is a WebRTC-first peer-to-peer communication app with anonymous room entry, text chat, file transfer, audio/video calling, and screen sharing. The current main branch is built with Rust + Axum + React. The server is intentionally lightweight and is only responsible for anonymous session issuance, signaling, and ICE/TURN configuration.
- Current mainline: Rust signaling server on
main - Legacy Go version:
main-gobranch - Last Go snapshot:
go-legacy-finaltag
If you are coming from the older Go implementation, use main-go for historical reference and main for the actively maintained version.
- Anonymous room join with no account registration
- P2P text chat over WebRTC DataChannel
- File transfer with receiver-side acceptance, pause, resume, and cancel
- Audio/video calling with camera and microphone toggles
- Screen sharing during active calls
- LAN-first connectivity with STUN / TURN fallback for public networks
- Frontend console diagnostics for troubleshooting unstable sessions
- Local-first chat history stored in the browser
- Signs and renews anonymous session cookies
- Maintains room membership and WebSocket signaling
- Serves
/api/icefor WebRTC bootstrap
- It does not store chat message bodies
- It does not relay normal text messages
- It does not carry normal media streams
- It does not proxy normal file payloads
Under healthy network conditions, chat, files, and media stay on direct P2P channels. The server remains on the signaling and bootstrap path only.
- Rust
- Axum
- Tokio
- WebSocket signaling
- HMAC-signed anonymous session cookie
- React 18
- Vite
- Tailwind CSS
- WebRTC
.
├── src/ # Rust backend
├── frontend/ # React frontend
├── Dockerfile # Runtime image build file
├── docker-compose.yaml # Docker Compose entrypoint
└── .env.example # Runtime config template
Create a runtime config first:
cp .env.example .envRecommended minimum:
ALLOWED_ORIGINS=http://localhost:3456,http://127.0.0.1:3456
SESSION_SECRET=replace-with-a-fixed-random-string
ICE_PROVIDER=stun-onlyBuild the frontend and run the Rust server:
cd frontend
npm install
npm run build
cd ..
cargo runThen open:
http://127.0.0.1:3456http://localhost:3456
For public users, docker-compose.yaml is the main entrypoint.
Think of this repository as: pull the public image, then inject runtime config through .env.
Create a runtime config first:
cp .env.example .envThe default .env.example is already usable for local evaluation and includes values like:
APP_IMAGE=crpi-6yrxqnyn3y05zbgq.cn-qingdao.personal.cr.aliyuncs.com/patrickcmh/patrick-im:latest
APP_PORT_BIND=127.0.0.1:3456:3456
ALLOWED_ORIGINS=http://localhost:3456,http://127.0.0.1:3456
SESSION_SECRET=change-this-before-production
ICE_PROVIDER=stun-onlyThen just start it:
docker compose pull
docker compose up -dOpen:
http://127.0.0.1:3456http://localhost:3456
- Run
docker loginfirst if your registry requires authentication - Change
APP_PORT_BINDto3456:3456if you want LAN devices to access it - Change
ALLOWED_ORIGINSto yourhttps://your-domain.combefore real deployment - Change
ICE_PROVIDERtocloudflareorstaticif you want better public-network connectivity - Change
APP_IMAGEif you build and publish your own image
Useful checks:
docker compose ps
docker compose logs -f app
curl http://127.0.0.1:3456/healthzMaintainer-specific release scripts and private ops workflows are intentionally kept out of the public repository. The public repo only documents the Compose-based runtime path for users.
The project supports three ICE modes:
stun-onlystaticcloudflare
Use this when you only want direct connectivity and NAT traversal, with no TURN relay fallback.
Use this for a self-hosted TURN server such as coturn:
ICE_PROVIDER=static
STUN_URLS=stun:stun.cloudflare.com:3478
TURN_URLS=turn:turn.example.com:3478?transport=udp,turns:turn.example.com:5349?transport=tcp
TURN_USERNAME=your-username
TURN_CREDENTIAL=your-passwordUse this for Cloudflare Realtime TURN.
Important:
- The project does not use a Cloudflare management API token here
- It expects the values shown on the TURN key page:
Turn Token IDAPI Token
On every /api/ice request, the backend asks Cloudflare for a fresh short-lived TURN credential set and returns the resulting iceServers to the frontend session.
Available endpoints:
GET /healthzGET /api/sessionGET /api/iceGET /api/roomsGET /ws
- The Go backend has been removed from the active mainline
- Static frontend assets are embedded into the backend release artifact
- The runtime image is intentionally minimal and expects build artifacts to be prepared locally
- Frontend diagnostics stay in the browser console and are no longer uploaded to the server
MIT