https://github.com/titansage02/pairqr
PairQR is an instant secure texts sharing app that uses QR code pairing for direct peer-to-peer transfers between devices with end-to-end encryption and no registration required.
https://github.com/titansage02/pairqr
instant-share privacy webrtc
Last synced: about 1 month ago
JSON representation
PairQR is an instant secure texts sharing app that uses QR code pairing for direct peer-to-peer transfers between devices with end-to-end encryption and no registration required.
- Host: GitHub
- URL: https://github.com/titansage02/pairqr
- Owner: TitanSage02
- License: other
- Created: 2025-08-15T12:48:16.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2025-08-19T20:53:54.000Z (11 months ago)
- Last Synced: 2025-08-19T22:26:31.413Z (11 months ago)
- Topics: instant-share, privacy, webrtc
- Language: TypeScript
- Homepage: https://pairqr.app
- Size: 535 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# PairQR β Instant, Encrypted Sharing
> π Share text and files peer-to-peer with end-to-end encryption and QR code pairing.
**PairQR** is a privacy-first platform that spins up ephemeral sessions to exchange text and files with ease. Pair via **QR code**, connect peers over **WebRTC**, and keep data **encrypted client-side**.
---
## β¨ Features
* **End-to-end encryption** β data is encrypted in the browser; the server canβt read it
* **QR code pairing** β create a session with a simple scan
* **Real-time** β secure P2P messaging and file transfer with WebRTC
* **Ephemeral sessions** β no persistence
* **Admin dashboard** β basic management & metrics
* **Cross-platform** β works on modern browsers
---
## π§± Monorepo Architecture
```
PairQR/
ββ client/ # React + Vite (TypeScript)
β ββ ...
ββ server/ # Node/Express (TypeScript)
β ββ src/*.ts
β ββ docker-compose.yml # prod stack: caddy, api, redis, postgres, coturn
β ββ Caddyfile # reverse proxy + TLS
β ββ ...
ββ shared/ # Shared types & schemas (TS/Zod)
ββ scripts/ # Scripts (e.g., start.sh)
```
**Client**: React 18, TypeScript, Vite, Tailwind, shadcn/ui, TanStack Query, QR (jsqr)
**Server**: Node.js + Express, Drizzle ORM + PostgreSQL, WebSocket (ws), JWT (admin), Helmet, CORS, rate-limit
**P2P**: WebRTC; **STUN/TURN** via **coturn** for NAT traversal
**Prod**: Docker Compose (Caddy TLS proxy, API, Redis, Postgres, coturn), GitHub Actions (CI/CD), DNS (Cloudflare/Vercel/Registrar)
---
## π Local Development
> Requirements: **Node.js 18+**, **npm** (or pnpm/yarn), and **PostgreSQL** if you use DB features.
```bash
# Install deps at the repo root (or per package)
npm install
# Frontend
cd client
npm run dev
# Backend
cd ../server
# configure server/.env (see below)
npm run dev
```
---
## π§ Configuration
Create **`server/.env`** (minimal example):
```env
# Runtime
NODE_ENV=production
PORT=3000
# Secrets (use strong values)
ADMIN_PASSWORD=change_me
HMAC_SECRET=change_me
JWT_SECRET=change_me
TURN_STATIC_SECRET=change_me
# CORS (comma-separated; ideally no spaces)
CORS_ORIGIN=https://pairqr.vercel.app,https://pairqr.app
# Internal services
REDIS_URL=redis://redis:6379
REDIS_TTL_SECONDS=300
DATABASE_URL=postgres://postgres:postgres@postgres:5432/pairqr
TURN_REALM=pairqr
TURN_URIS=turn:coturn:3478?transport=udp,turn:coturn:3478?transport=tcp
```
> **Heads-up**: if any secret contains **`$`**, escape it as **`$$`** in `.env` (Docker Compose treats `$VAR` as interpolation).
---
## π³ Production (Docker Compose)
The prod stack (in `server/docker-compose.yml`) runs:
* **caddy** β reverse proxy + Letβs Encrypt TLS (ports **80/443**) β proxies to `pairqr:3000`
* **pairqr** β your Node/Express API (**3000** internal only)
* **redis** β cache / sessions
* **postgres** β database
* **coturn** β STUN/TURN (**3478** UDP/TCP)
**Caddyfile** (example):
```caddyfile
# HTTPS
api.pairqr.app {
encode gzip
reverse_proxy pairqr:3000
}
# HTTP: /health => 200, everything else redirects to HTTPS (optional)
http://api.pairqr.app {
route /health {
respond "ok" 200
}
redir https://api.pairqr.app{uri}
}
```
---
## π€ CI/CD (Recommended)
Typical GitHub Actions pipeline:
1. **Build** the server Docker image (multi-stage, `npm ci`) and push to **GHCR**.
2. **SSH** into the server, write `server/.env` from GitHub Secrets, set `SERVER_IMAGE`.
3. `docker compose pull && up -d`, then perform an **HTTPS healthcheck** (expect **200**).
**GitHub Secrets** (Settings β Secrets and variables β Actions):
* SSH/Droplet: `DO_HOST`, `DO_SSH_USER`, `DO_SSH_KEY` (optionally `DO_SSH_PORT`, `DO_SSH_PASSPHRASE`), `PROJECT_DIR`
* App: `ADMIN_PASSWORD`, `HMAC_SECRET`, `JWT_SECRET`, `CORS_ORIGIN`, `TURN_STATIC_SECRET`
* Optional: `DATABASE_URL`, `REDIS_URL`, `TURN_REALM`, `TURN_URIS`, `RATE_LIMIT_*`, `SESSION_TTL_MINUTES`, etc.
> In the script that writes `.env`, **escape `$`** as `$$` to avoid interpolation warnings and corrupted values.
---
## π Security
* **E2EE**: encrypted client-side; server relays but canβt read content
* **Ephemeral**: messages are not persisted by default
* **CORS**: strict allowlist (production frontends only)
* **Rate limiting**: basic API abuse protection
* **Headers**: Helmet on the app + security headers on Caddy
* **Validation**: Zod on critical inputs
---
## π©Ί Health & Logs
* **Health**: `GET /health` β **200** (via `https://api.pairqr.app/health`)
* **Logs**:
* backend: `docker logs -f pairqr-app`
---
## π Troubleshooting (Quick FAQ)
* **Healthcheck returns 308**: you are probing HTTP while Caddy redirects to HTTPS. Probe **HTTPS** instead (`curl -L https://apiβ¦/health`), or keep the HTTP block that serves `/health` as 200.
* **`The "U" variable is not set`**: a `$` in `.env` wasnβt escaped β replace `$` with `$$`.
* **Node build OOM**: use `npm ci`, keep `package-lock.json`, multi-stage Docker, and/or build the image in CI (not on the VPS).
* **TURN behind Cloudflare**: donβt proxy UDP; keep **DNS only** for the TURN/API subdomain if you rely on UDP and ACME HTTP-01.
---
## π€ Contributing
1. Fork
2. Branch: `feat/my-feature`
3. Dev: `npm run dev` (client/server)
4. Checks: `npm run check`, tests if present
5. Open a PR with a clear description and docs updates if needed
---
## π License
See **LICENSE**.
---
## π Links
* **Demo**: [https://pairqr.app](https://pairqr.vercel.app)
* **Repo**: [https://github.com/TitanSage02/PairQR](https://github.com/TitanSage02/PairQR)
* **Issues**: [https://github.com/TitanSage02/PairQR/issues](https://github.com/TitanSage02/PairQR/issues)
---
*Built with β€οΈ for simple, private, and secure communication.*