This project implements a secure service for sharing text-based secrets ephemerally. It follows a zero-knowledge principle, meaning the server never has access to the plaintext secrets or decryption keys. Secrets are encrypted client-side, stored temporarily on the server, and can only be viewed once within a 1-hour time limit.
- Client-Side Encryption: Secrets are encrypted/decrypted directly in the user's browser.
- Zero-Knowledge Server: The server only stores encrypted data and cannot decrypt it.
- View Once: Secrets are permanently deleted after being successfully viewed once.
- Time-Limited: Secrets expire automatically after 1 hour.
- Secure: Uses HTTPS, standard security headers (CSP, HSTS etc.), and API rate limiting.
- Backend: Node.js, Express.js
- Database: PostgreSQL
- Frontend: React, TailwindCSS, Web Crypto API
- Containerization: Docker, Docker Compose
- Testing: E2E Tests (Cypress)
- Docker Desktop (or Docker Engine + Docker Compose) installed.
- Node.js (for potential local development/testing outside Docker).
Create a .env file in the project root directory by copying .env.example:
cp .env.example .envKey environment variables to configure:
# PostgreSQL Settings
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your_strong_password
POSTGRES_DB=onetimesecretdb
# Backend Settings
NODE_ENV=production
PORT=9000
# NGINX Settings
NGINX_ENVIRONMENT=local # Options: local, production
VITE_API_URL=/api
For production, set:
NGINX_ENVIRONMENT=production
docker-compose up --build -d--build: Forces Docker to rebuild the images if changes were made.-d: Runs the containers in detached mode (in the background).
- In development:
http://localhost:80 - In production: Configure your domain in the Nginx configuration files
docker-compose downTo remove the persistent database volume as well (use with caution!):
docker-compose down -vPOST /api/secrets: Creates a new secret.- Request Body:
{ "encryptedSecret": "base64-encoded-encrypted-text" } - Response (Success 201):
{ "referenceId": "unique-secret-id" } - Response (Error): Standard HTTP error codes (e.g., 400, 500).
- Request Body:
POST /api/secrets/:id/consume: Attempts to validate and consume (view) a secret.- Request Body: (Empty)
- Response (Success 200):
{ "encryptedSecret": "base64-encoded-encrypted-text" }- Contains the encrypted secret needed for client-side decryption. - Response (Error 404/410): If the secret is not found, already viewed, or expired.
- Response (Error 429): If rate limit is exceeded.
The system relies on client-side encryption. The server receives only the encrypted data and a referenceId. The decryption key is kept solely within the URL fragment (#key) generated by the client, which is never sent to the server. Security depends on the user sharing the full URL securely. The server ensures secrets are view-once and expire after 1 hour.
For production deployment with HTTPS:
- Obtain SSL certificates for your domain:
sudo apt update
sudo apt install certbot
sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com- Ensure your Nginx configuration maps the certificates correctly:
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
- Mount the certificates in your docker-compose.yml:
nginx:
volumes:
- /etc/letsencrypt:/etc/letsencrypt:ro- Set the environment to production:
NGINX_ENVIRONMENT=production
- Rebuild and restart:
docker-compose down
docker-compose up --build -dMake sure ports 80 and 443 are open on your server:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcpTo work on the frontend independently:
cd frontend
npm install
npm run devTo work on the backend independently:
cd backend
npm install
npm run devThe project includes Cypress tests in the frontend/cypress directory:
cd frontend
npm run test:e2eThe project includes multiple Nginx configuration files:
nginx/nginx.local.conf: Used for local developmentnginx/nginx.conf: Used for production with SSL
The active configuration is determined by the NGINX_ENVIRONMENT variable in your .env file.
The API URL used by the frontend is configured through the VITE_API_URL environment variable. This is set at build time and passed to the frontend application.
- A basic threat model and mitigation analysis should be performed and documented separately.
- The project includes E2E tests using Cypress to verify core flows and security features.