Clipboard is a secure online clipboard for moving text between devices. A user pastes text, creates a temporary room, and shares the generated room ID or QR code with another device. The receiving device joins the room, reads the text, copies it, updates it, or deletes the room when finished.
- Create private clipboard rooms with unguessable IDs.
- Join rooms by room ID or by scanning a QR code.
- Copy shared text, room IDs, and room links from the browser.
- Update existing room text while the room is active.
- Delete a room manually after use.
- Auto-expire rooms after a selected time.
- Responsive, simple React UI built for desktop and mobile use.
- Express API with security headers, CORS allow-listing, rate limits, payload limits, and encrypted in-memory room storage.
- Frontend: React 19, Vite, Tailwind CSS 4, lucide-react, qrcode
- Backend: Node.js, Express 5, Helmet, express-rate-limit, CORS
- Storage: encrypted in-memory storage with automatic expiry
- The user pastes text into the create-room panel.
- The backend generates a random room ID and encrypts the text with AES-256-GCM before storing it in memory.
- The frontend displays the room ID, share link, and QR code.
- Another device opens the link or enters the room ID.
- The backend decrypts and returns the text only for that specific room ID.
- The room expires automatically, or the user can delete it manually.
Rooms are intentionally temporary. Restarting the backend clears active rooms because no database is used.
This project keeps the security model simple and practical:
- Room IDs are random and hard to guess.
- There is no public room listing endpoint.
- Clipboard text is encrypted in server memory using AES-256-GCM.
- A room has an expiry time and expired rooms are removed automatically.
- Requests are rate-limited to reduce brute-force and spam attempts.
- JSON body size is limited to prevent oversized payload abuse.
- CORS is restricted to configured frontend origins.
- Helmet adds standard HTTP security headers.
- Server errors are normalized so internal details are not leaked.
Important note: this is secure enough for a portfolio/demo temporary clipboard, but it is not a replacement for a full secret manager. Do not use it for long-term credential storage.
clipboard/
client/ React + Vite frontend
server/ Express API
Readme.md Project documentation
- Node.js 18.19 or newer
- npm
Node 20 is recommended.
cd server
npm install
cp .env.example .env
npm run devThe API runs on http://localhost:5000 by default.
Create server/.env from server/.env.example.
PORT=5000
CLIENT_URL=http://localhost:5173
CORS_ORIGIN=http://localhost:5173
CLIPBOARD_SECRET=replace-with-a-long-random-secret
DEFAULT_TTL_MINUTES=60
MAX_TTL_MINUTES=1440
MAX_TEXT_CHARS=20000PORT: backend port.CLIENT_URL: frontend URL used as the default allowed origin.CORS_ORIGIN: comma-separated allowed browser origins.CLIPBOARD_SECRET: encryption secret used to derive the AES key.DEFAULT_TTL_MINUTES: default room lifetime.MAX_TTL_MINUTES: maximum allowed room lifetime.MAX_TEXT_CHARS: maximum text length per room.
Use a real secret in production. If CLIPBOARD_SECRET is missing, the server creates a random secret on startup, which means existing rooms cannot be decrypted after restart.
cd client
npm install
cp .env.example .env
npm run devThe frontend runs on http://localhost:5173 by default.
Create client/.env from client/.env.example.
VITE_API_URL=http://localhost:5000/apiGET /api/healthReturns server status and basic limits.
POST /api/rooms
Content-Type: application/json{
"text": "Text to share",
"ttlMinutes": 60
}Returns:
{
"roomId": "A1b2C3d4E5f6",
"charCount": 13,
"createdAt": "2026-05-27T10:00:00.000Z",
"updatedAt": "2026-05-27T10:00:00.000Z",
"expiresAt": "2026-05-27T11:00:00.000Z"
}GET /api/rooms/:roomIdReturns room metadata plus decrypted text.
PUT /api/rooms/:roomId
Content-Type: application/json{
"text": "Updated text"
}Updates the encrypted room text while keeping the same room ID and expiry.
DELETE /api/rooms/:roomIdDeletes the room immediately.
npm run dev # start API with node --watch
npm start # start API
npm test # syntax check server.jsnpm run dev # start Vite dev server
npm run build # production build
npm run lint # eslint
npm run preview # preview production build- Deploy the backend on a Node.js host.
- Set a strong
CLIPBOARD_SECRET. - Set
CORS_ORIGINto the deployed frontend URL. - Deploy the frontend as a static Vite app.
- Set
VITE_API_URLto the deployed API URL ending in/api. - Use HTTPS in production so room IDs and clipboard text are protected in transit.
Because storage is in-memory, rooms disappear when the backend restarts. For persistent or multi-instance deployments, add Redis or a database and keep the same expiry and encryption approach.
The current implementation was checked with:
cd server && npm test
cd server && npm audit --omit=dev
cd client && npm run lint
cd client && npm run buildRun the commands with Node 18.19+ or Node 20.