Deploying Enclosed
Introduction
Enclosed is a minimalistic web application designed for sending private and secure notes with end-to-end encryption. Built with modern web technologies, Enclosed ensures that all shared content is encrypted on the client side, guaranteeing zero knowledge on the server side. Whether you need to share sensitive passwords, confidential information, or private messages, Enclosed provides a secure and ephemeral way to transmit data.
All notes are encrypted using AES-GCM with a 256-bit key derived via PBKDF2, ensuring that even if the server storage is compromised, your data remains completely secure. The encryption key never leaves the client browser, and the server has no ability to decrypt or read the contents of your notes.
Key Features
- 🔐 End-to-End Encryption: AES-GCM 256-bit encryption with PBKDF2 key derivation
- 🎯 Zero Knowledge Architecture: Server has no access to note contents or decryption keys
- 📎 File Attachments: Securely share files alongside your encrypted notes
- 🔒 Password Protection: Optional password layer for additional security
- ⏰ Expiration Control: Set custom TTL (Time To Live) for automatic deletion
- 🔥 Self-Destructing Notes: Automatically delete notes after first read
- 🎨 Minimalistic UI: Clean, intuitive interface for quick note sharing
- 🌍 i18n Support: Available in multiple languages
- 🔑 Optional Authentication: Email/password auth for note creation control
- 🌙 Dark Mode: Built-in dark theme for comfortable viewing
- 📱 Responsive Design: Works seamlessly on desktop and mobile devices
- ⚡ Lightning Fast: Built with SolidJS and HonoJS for optimal performance
- 🌿 Eco-Friendly: A+ rating on websitecarbon.com for minimal environmental impact
- 🛠️ CLI Support: Command-line interface for terminal-based note creation
- 🔓 Open Source: Apache 2.0 licensed with active community
Tech Stack
- Frontend: SolidJS with Shadcn Solid UI components
- Backend: HonoJS (lightweight web framework)
- Storage: Unstorage (unified key-value storage)
- Styling: UnoCSS (atomic CSS engine)
- Icons: Tabler Icons
- Validation: Zod (TypeScript schema validation)
- Encryption: Web Crypto API (AES-GCM, PBKDF2)
- Runtime: Node.js 18+ or Bun
Use Cases
- Password Sharing: Safely share credentials with team members
- Confidential Documents: Send sensitive business information
- API Keys & Tokens: Share development credentials securely
- Personal Information: Transmit SSN, credit cards, or private data
- Medical Records: Share health information with privacy compliance
- Legal Documents: Exchange attorney-client privileged information
- Security Audits: Share vulnerability reports safely
- One-Time Secrets: Temporary access codes or passwords
- Whistleblowing: Anonymous tip submission with encryption
- Temporary File Sharing: Send files that automatically delete
Security Model
Enclosed implements a robust security architecture:
- Client-Side Encryption: All encryption happens in the browser
- Base Key Generation: Random key generated for each note
- PBKDF2 Key Derivation: Master key derived from base key + optional password
- AES-GCM Encryption: Military-grade symmetric encryption
- Zero Server Knowledge: Server only stores encrypted data
- URL Fragment Storage: Encryption key stored in URL hash (never sent to server)
- No Logging: No tracking or logging of sensitive operations
- Secure Transmission: HTTPS/TLS for all communications
Why Deploy Enclosed on Klutch.sh?
- Instant Deployment: Deploy from GitHub with automatic Dockerfile detection
- Persistent Storage: Built-in volume support for encrypted note storage
- Environment Variables: Secure configuration management
- Auto-Scaling: Handle traffic spikes during high usage
- Custom Domains: Use your own domain with automatic SSL
- Zero Downtime: Rolling deployments for seamless updates
- Monitoring: Built-in application monitoring and logging
- Cost-Effective: Transparent pricing with no hidden fees
- Global CDN: Fast note access from anywhere in the world
- Backup Support: Easy data backup and disaster recovery
Prerequisites
Before deploying Enclosed, ensure you have:
- A Klutch.sh account
- A GitHub account for repository hosting
- Basic understanding of Docker and Node.js
- (Optional) Custom domain name for branded deployment
Preparing Your Repository
Step 1: Create Your Project Directory
Start by creating a new directory for your Enclosed deployment:
mkdir enclosed-klutchcd enclosed-klutchgit initStep 2: Create the Dockerfile
Enclosed provides an official Dockerfile. Create a Dockerfile in your project root:
Option 1: Standard Deployment (Recommended)
# Build stageFROM node:20-alpine AS builder
WORKDIR /app
# Install pnpmRUN corepack enable && corepack prepare pnpm@latest --activate
# Copy package filesCOPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./COPY packages ./packages
# Install dependenciesRUN pnpm install --frozen-lockfile
# Build the applicationRUN pnpm run build
# Production stageFROM node:20-alpine
WORKDIR /app
# Install pnpmRUN corepack enable && corepack prepare pnpm@latest --activate
# Copy built applicationCOPY --from=builder /app/packages/app-server/dist ./distCOPY --from=builder /app/packages/app-client/dist ./publicCOPY --from=builder /app/node_modules ./node_modulesCOPY --from=builder /app/package.json ./
# Create data directory for persistent storageRUN mkdir -p /app/data && chmod 755 /app/data
# Expose portEXPOSE 8787
# Set environment variablesENV NODE_ENV=productionENV PORT=8787ENV STORAGE_DIR=/app/data
# Health checkHEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD node -e "require('http').get('http://localhost:8787/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# Start the applicationCMD ["node", "dist/index.js"]Option 2: Official Docker Image
For the simplest deployment, you can use the official Enclosed Docker image. Create a simple Dockerfile:
FROM corentinth/enclosed:latest
# Create data directory for persistent storageRUN mkdir -p /app/data && chmod 755 /app/data
# Environment variables (can be overridden at runtime)ENV PORT=8787ENV STORAGE_DIR=/app/dataENV NODE_ENV=production
# Expose portEXPOSE 8787
# The official image already includes the start commandOption 3: Rootless Deployment (Enhanced Security)
For production environments requiring rootless containers:
FROM corentinth/enclosed:rootless
# The rootless image runs as non-root user by defaultUSER 1000:1000
# Create data directoryRUN mkdir -p /app/data && chmod 755 /app/data
# Environment variablesENV PORT=8787ENV STORAGE_DIR=/app/dataENV NODE_ENV=production
# Expose port (non-privileged port)EXPOSE 8787
# Health checkHEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD node -e "require('http').get('http://localhost:8787/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"Option 4: Development Build with Hot Reload
For development and testing:
FROM node:20-alpine
WORKDIR /app
# Install pnpmRUN corepack enable && corepack prepare pnpm@latest --activate
# Install development dependenciesRUN apk add --no-cache git
# Copy package filesCOPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./COPY packages ./packages
# Install all dependencies (including dev)RUN pnpm install
# Expose portEXPOSE 8787
# Set development environmentENV NODE_ENV=developmentENV PORT=8787
# Start development serverCMD ["pnpm", "run", "dev"]Step 3: Create Environment Configuration
Create a .env.example file to document available configuration options:
# Server ConfigurationPORT=8787NODE_ENV=productionHOST=0.0.0.0
# Storage ConfigurationSTORAGE_DIR=/app/dataSTORAGE_PROVIDER=fs # Options: fs, redis, cloudflare-kv
# Application ConfigurationAPP_BASE_URL=https://your-app.klutch.shAPP_NAME=Enclosed
# Security ConfigurationMAX_NOTE_SIZE=10485760 # 10MB in bytesMAX_FILE_SIZE=52428800 # 50MB in bytesALLOWED_FILE_TYPES=* # Comma-separated list or * for all
# Rate LimitingRATE_LIMIT_ENABLED=trueRATE_LIMIT_MAX_REQUESTS=100RATE_LIMIT_WINDOW_MS=60000
# Note ExpirationDEFAULT_TTL=3600 # 1 hour in secondsMAX_TTL=2592000 # 30 days in seconds
# Authentication (Optional)AUTH_ENABLED=falseAUTH_JWT_SECRET=your-secure-secret-hereAUTH_SESSION_DURATION=86400 # 24 hours
# CORS ConfigurationCORS_ENABLED=trueCORS_ORIGINS=*
# LoggingLOG_LEVEL=info # Options: debug, info, warn, errorStep 4: Create Docker Compose (Optional for Local Development)
Create docker-compose.yml for local testing:
version: '3.8'
services: enclosed: build: context: . dockerfile: Dockerfile ports: - "8787:8787" volumes: - enclosed-data:/app/data environment: - NODE_ENV=production - PORT=8787 - STORAGE_DIR=/app/data - APP_BASE_URL=http://localhost:8787 - LOG_LEVEL=debug restart: unless-stopped healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8787/api/health"] interval: 30s timeout: 10s retries: 3 start_period: 10s
volumes: enclosed-data: driver: localStep 5: Create Configuration Documentation
Create CONFIG.md to document your deployment:
# Enclosed Configuration Guide
## Environment Variables
### Required Variables
- `PORT`: HTTP port (default: 8787)- `STORAGE_DIR`: Directory for persistent storage (default: /app/data)
### Storage Configuration
Enclosed supports multiple storage backends:
#### File System (Default)```envSTORAGE_PROVIDER=fsSTORAGE_DIR=/app/dataRedis
STORAGE_PROVIDER=redisREDIS_URL=redis://redis:6379REDIS_PREFIX=enclosed:Cloudflare KV (for Cloudflare Pages)
STORAGE_PROVIDER=cloudflare-kvKV_NAMESPACE_ID=your-kv-namespace-idSecurity Best Practices
- Always use HTTPS in production
- Set strong
AUTH_JWT_SECRETif using authentication - Configure
MAX_NOTE_SIZEandMAX_FILE_SIZEappropriately - Enable rate limiting in production
- Restrict
CORS_ORIGINSto your domain - Use rootless Docker image for enhanced security
- Regular backups of storage directory
Performance Tuning
- Use Redis for high-traffic deployments
- Enable CDN for static assets
- Configure appropriate TTL values
- Monitor storage usage and cleanup expired notes
### Step 6: Create .gitignore
Create a `.gitignore` file:Dependencies
node_modules/ .pnpm-store/
Build outputs
dist/ build/ .next/ out/
Environment files
.env .env.local .env.production .env.development
Logs
logs/ .log npm-debug.log pnpm-debug.log*
Data directory
data/
OS files
.DS_Store Thumbs.db
Editor files
*.swp *.swo *~ .vscode/ .idea/
Docker
.dockerignore
Testing
coverage/ .nyc_output/
### Step 7: Create README.md
Document your deployment:
```markdown# Enclosed Deployment on Klutch.sh
Minimalistic end-to-end encrypted note sharing platform.
## Features
- End-to-end encryption (AES-GCM 256-bit)- Zero-knowledge architecture- Self-destructing notes- File attachments- Password protection- Custom expiration times- Dark mode- Multi-language support
## Quick Start
### Local Development
1. Clone the repository:```bashgit clone https://github.com/your-username/enclosed-klutch.gitcd enclosed-klutch- Start with Docker Compose:
docker-compose up -d- Access at http://localhost:8787
Production Deployment
- Push to GitHub
- Connect to Klutch.sh
- Configure environment variables
- Attach persistent volume at
/app/data - Deploy!
Configuration
Required environment variables:
PORT: 8787STORAGE_DIR: /app/dataAPP_BASE_URL: https://your-app.klutch.sh
Optional:
MAX_NOTE_SIZE: Maximum note size in bytesDEFAULT_TTL: Default expiration time in secondsAUTH_ENABLED: Enable authentication
Persistent Storage
Attach a volume at /app/data with recommended size: 10GB
This stores:
- Encrypted notes
- File attachments
- Application state
Security
All notes are encrypted with:
- AES-GCM 256-bit encryption
- PBKDF2 key derivation
- Client-side encryption only
- Zero server knowledge
Resources
- Official Docs: https://docs.enclosed.cc/
- GitHub: https://github.com/CorentinTh/enclosed
- Live Demo: https://enclosed.cc
### Step 8: Initialize Git and Push to GitHub
```bash# Initialize git repositorygit init
# Add all filesgit add .
# Commitgit commit -m "Initial Enclosed deployment setup"
# Add remote repository (replace with your repo URL)git remote add origin https://github.com/yourusername/enclosed-klutch.git
# Push to GitHubgit branch -M maingit push -u origin mainDeploying on Klutch.sh
Now that your repository is ready, let’s deploy Enclosed on Klutch.sh:
-
Navigate to Klutch.sh Dashboard
Go to klutch.sh/app and sign in to your account.
-
Create a New Project
- Click on “New Project”
- Select your GitHub repository containing the Enclosed configuration
- Choose the repository from the dropdown
-
Configure Deployment Settings
Klutch.sh will automatically detect your Dockerfile. Configure the following:
Traffic Type:
- Select HTTP traffic
- The application will be accessible via HTTPS
-
Configure Environment Variables
Add the following environment variables in the Klutch.sh dashboard:
Required Variables:
Terminal window PORT=8787NODE_ENV=productionSTORAGE_DIR=/app/dataRecommended Variables:
Terminal window APP_BASE_URL=https://your-app-name.klutch.shMAX_NOTE_SIZE=10485760MAX_FILE_SIZE=52428800DEFAULT_TTL=3600MAX_TTL=2592000LOG_LEVEL=infoSecurity Variables:
Terminal window RATE_LIMIT_ENABLED=trueRATE_LIMIT_MAX_REQUESTS=100RATE_LIMIT_WINDOW_MS=60000Optional Authentication:
Terminal window AUTH_ENABLED=false# If enabled, set:# AUTH_JWT_SECRET=your-random-secure-string-here# AUTH_SESSION_DURATION=86400 -
Attach Persistent Volume
For reliable note storage, attach a persistent volume:
- Click “Add Volume”
- Mount path:
/app/data - Recommended size: 10GB (adjust based on expected usage)
This volume stores:
- Encrypted notes
- File attachments
- Application metadata
-
Deploy the Application
- Click “Deploy”
- Klutch.sh will build your Docker image and start Enclosed
- Monitor the deployment logs for any errors
-
Verify Deployment
Once deployed, your Enclosed instance will be accessible at:
- URL:
https://your-app-name.klutch.sh
Test by:
- Opening the URL in your browser
- Creating a test note
- Verifying encryption (check network tab - content should be encrypted)
- Testing note retrieval with the generated link
- URL:
Configuration
Environment Variables Reference
| Variable | Description | Default | Required |
|---|---|---|---|
PORT | HTTP server port | 8787 | Yes |
NODE_ENV | Environment mode | production | Yes |
STORAGE_DIR | Directory for encrypted notes | /app/data | Yes |
HOST | Bind address | 0.0.0.0 | No |
APP_BASE_URL | Public URL of your instance | - | Recommended |
APP_NAME | Application name | Enclosed | No |
STORAGE_PROVIDER | Storage backend | fs | No |
MAX_NOTE_SIZE | Maximum note size (bytes) | 10485760 (10MB) | No |
MAX_FILE_SIZE | Maximum file size (bytes) | 52428800 (50MB) | No |
ALLOWED_FILE_TYPES | Allowed file types | * | No |
DEFAULT_TTL | Default expiration (seconds) | 3600 (1 hour) | No |
MAX_TTL | Maximum expiration (seconds) | 2592000 (30 days) | No |
RATE_LIMIT_ENABLED | Enable rate limiting | true | No |
RATE_LIMIT_MAX_REQUESTS | Max requests per window | 100 | No |
RATE_LIMIT_WINDOW_MS | Rate limit window (ms) | 60000 | No |
AUTH_ENABLED | Enable authentication | false | No |
AUTH_JWT_SECRET | JWT signing secret | - | If AUTH_ENABLED |
AUTH_SESSION_DURATION | Session duration (seconds) | 86400 | No |
CORS_ENABLED | Enable CORS | true | No |
CORS_ORIGINS | Allowed origins | * | No |
LOG_LEVEL | Logging verbosity | info | No |
Storage Configuration
Enclosed supports multiple storage backends:
File System (Default)
Best for single-instance deployments:
STORAGE_PROVIDER=fsSTORAGE_DIR=/app/dataRedis
For high-availability deployments:
STORAGE_PROVIDER=redisREDIS_URL=redis://your-redis-host:6379REDIS_PREFIX=enclosed:REDIS_PASSWORD=your-redis-passwordCloudflare KV
For Cloudflare Pages deployments:
STORAGE_PROVIDER=cloudflare-kvKV_NAMESPACE_ID=your-kv-namespace-idSecurity Configuration
Note Size Limits
Control maximum note and file sizes:
MAX_NOTE_SIZE=10485760 # 10MBMAX_FILE_SIZE=52428800 # 50MBFile Type Restrictions
Restrict allowed file types:
# Allow all filesALLOWED_FILE_TYPES=*
# Allow specific typesALLOWED_FILE_TYPES=image/png,image/jpeg,application/pdf,text/plainRate Limiting
Protect against abuse:
RATE_LIMIT_ENABLED=trueRATE_LIMIT_MAX_REQUESTS=100RATE_LIMIT_WINDOW_MS=60000 # 1 minuteCORS Configuration
Restrict cross-origin access:
CORS_ENABLED=trueCORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.comAuthentication Setup
Enable optional authentication for note creation:
AUTH_ENABLED=trueAUTH_JWT_SECRET=your-very-secure-random-stringAUTH_SESSION_DURATION=86400 # 24 hoursGenerate a secure secret:
openssl rand -base64 64Using Enclosed
Creating a Note
-
Write Your Note
- Enter your sensitive text in the note editor
- Add optional file attachments
-
Configure Security
- Set a password (optional but recommended)
- Choose expiration time (1 hour, 1 day, 1 week, 1 month)
- Enable “Delete after reading” for self-destructing notes
-
Create and Share
- Click “Create note”
- Copy the generated secure link
- Share the link via your preferred channel
- (Optional) Share the password separately
Reading a Note
-
Open the Link
- Click the shared encrypted note link
- The app fetches the encrypted content
-
Enter Password (if required)
- If password-protected, enter the password
- Wrong password = unable to decrypt
-
View Content
- Note is decrypted client-side
- View text and download attachments
- If “delete after reading” was enabled, note is immediately deleted
Using the CLI
Install the Enclosed CLI for terminal-based note creation:
# Install globallynpm install -g @enclosed/cli
# Or use with npxnpx @enclosed/cli create "Hello, World!"CLI Commands
Create a note:
# Basic noteenclosed create "My secret message"
# From stdinecho "Secret data" | enclosed create
# With optionsenclosed create \ --password "mypassword" \ --deleteAfterReading \ --ttl 3600 \ "Confidential information"View a note:
# Password will be prompted if neededenclosed view https://your-app.klutch.sh/n/abc123#key
# With password inlineenclosed view --password "mypassword" https://your-app.klutch.sh/n/abc123#keyConfigure instance:
# Set your custom instanceenclosed config set instance-url https://your-app.klutch.sh
# View current configenclosed config listAPI Integration
Enclosed provides a RESTful API for programmatic access:
Create a Note (cURL)
curl -X POST https://your-app.klutch.sh/api/notes \ -H "Content-Type: application/json" \ -d '{ "encryptedContent": "base64-encrypted-content", "encryptedTitle": "base64-encrypted-title", "ttl": 3600, "deleteAfterReading": true, "isPasswordProtected": true }'Node.js Example
const axios = require('axios');const crypto = require('crypto');
async function createEncryptedNote(content, password) { // Generate encryption key const baseKey = crypto.randomBytes(32);
// Derive master key from base key and password const salt = crypto.randomBytes(16); const masterKey = crypto.pbkdf2Sync( Buffer.concat([baseKey, Buffer.from(password)]), salt, 100000, 32, 'sha256' );
// Encrypt content with AES-GCM const iv = crypto.randomBytes(12); const cipher = crypto.createCipheriv('aes-256-gcm', masterKey, iv);
let encrypted = cipher.update(content, 'utf8', 'base64'); encrypted += cipher.final('base64'); const authTag = cipher.getAuthTag().toString('base64');
// Create note via API const response = await axios.post('https://your-app.klutch.sh/api/notes', { encryptedContent: encrypted, iv: iv.toString('base64'), authTag: authTag, salt: salt.toString('base64'), ttl: 3600, deleteAfterReading: true, isPasswordProtected: true });
// Generate shareable link const noteId = response.data.id; const baseKeyHex = baseKey.toString('hex'); const link = `https://your-app.klutch.sh/n/${noteId}#${baseKeyHex}`;
console.log('Note created:', link); return link;}
// UsagecreateEncryptedNote('My secret message', 'mypassword');Python Example
import requestsimport jsonimport base64import osfrom cryptography.hazmat.primitives.ciphers.aead import AESGCMfrom cryptography.hazmat.primitives import hashesfrom cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2
def create_encrypted_note(content, password, instance_url): # Generate base key base_key = os.urandom(32)
# Derive master key kdf = PBKDF2( algorithm=hashes.SHA256(), length=32, salt=os.urandom(16), iterations=100000, ) master_key = kdf.derive(base_key + password.encode())
# Encrypt with AES-GCM aesgcm = AESGCM(master_key) iv = os.urandom(12) encrypted = aesgcm.encrypt(iv, content.encode(), None)
# Create note response = requests.post(f'{instance_url}/api/notes', json={ 'encryptedContent': base64.b64encode(encrypted).decode(), 'iv': base64.b64encode(iv).decode(), 'ttl': 3600, 'deleteAfterReading': True, 'isPasswordProtected': True })
note_id = response.json()['id'] base_key_hex = base_key.hex() link = f'{instance_url}/n/{note_id}#{base_key_hex}'
print(f'Note created: {link}') return link
# Usagecreate_encrypted_note( 'My secret message', 'mypassword', 'https://your-app.klutch.sh')Production Best Practices
Security Hardening
1. Use Strong Secrets
Generate cryptographically secure secrets:
# Generate JWT secretopenssl rand -base64 64
# Generate API keysopenssl rand -hex 322. Enable HTTPS Only
Always use HTTPS in production (Klutch.sh provides this automatically).
3. Restrict CORS
Limit CORS to your domain:
CORS_ENABLED=trueCORS_ORIGINS=https://yourdomain.com4. Implement Rate Limiting
Protect against abuse:
RATE_LIMIT_ENABLED=trueRATE_LIMIT_MAX_REQUESTS=50RATE_LIMIT_WINDOW_MS=600005. Set Reasonable Limits
MAX_NOTE_SIZE=5242880 # 5MBMAX_FILE_SIZE=26214400 # 25MBMAX_TTL=604800 # 7 days max6. Regular Security Audits
- Monitor access logs
- Review failed authentication attempts
- Check for unusual storage patterns
- Update dependencies regularly
Performance Optimization
1. Use Redis for High Traffic
For deployments with >1000 notes/day:
STORAGE_PROVIDER=redisREDIS_URL=redis://your-redis-host:63792. Configure Caching
Enable browser caching for static assets (handled by Enclosed automatically).
3. Optimize Storage
Regularly clean up expired notes:
# Create cleanup script#!/bin/bashSTORAGE_DIR=/app/data/notes
# Remove notes older than MAX_TTLfind $STORAGE_DIR -type f -mtime +30 -delete
echo "Cleanup completed: $(date)"Run via cron:
0 2 * * * /app/scripts/cleanup-expired.sh4. Monitor Resources
Set up monitoring for:
- CPU usage
- Memory consumption
- Storage capacity
- Request latency
- Error rates
Backup Strategy
1. Automated Backups
Create backup script:
#!/bin/bashBACKUP_DIR=/backups/enclosedSTORAGE_DIR=/app/dataDATE=$(date +%Y%m%d-%H%M%S)
# Create backup directorymkdir -p $BACKUP_DIR
# Backup encrypted notestar czf $BACKUP_DIR/notes-$DATE.tar.gz $STORAGE_DIR
# Keep only last 30 daysfind $BACKUP_DIR -name "notes-*.tar.gz" -mtime +30 -delete
echo "Backup completed: $BACKUP_DIR/notes-$DATE.tar.gz"Schedule with cron:
0 3 * * * /app/scripts/backup-enclosed.sh2. Offsite Backups
For critical deployments:
#!/bin/bash# Upload to S3 or similaraws s3 cp $BACKUP_DIR/notes-$DATE.tar.gz \ s3://your-bucket/enclosed-backups/3. Test Restoration
Regularly test backup restoration:
# Test restoretar xzf notes-20250117.tar.gz -C /tmp/restore-test# Verify contentsls -la /tmp/restore-test/app/dataMonitoring and Logging
1. Application Logs
Monitor application logs:
# View real-time logsdocker logs -f enclosed-container
# Search for errorsdocker logs enclosed-container 2>&1 | grep ERROR
# View last 100 linesdocker logs --tail 100 enclosed-container2. Health Checks
Enclosed includes a health endpoint:
# Check health statuscurl https://your-app.klutch.sh/api/health
# Expected response:{ "status": "ok", "timestamp": "2025-12-17T12:00:00.000Z"}3. Metrics Collection
Monitor key metrics:
- Request Rate: Requests per minute
- Error Rate: Failed requests percentage
- Response Time: Average latency
- Storage Usage: Disk space consumed
- Active Notes: Current note count
4. Alerting
Set up alerts for:
- High error rates (>5%)
- Slow response times (>1s)
- Storage capacity (>80%)
- Failed health checks
- Unusual traffic patterns
Scaling Strategies
Vertical Scaling
Increase resources via Klutch.sh dashboard:
- More CPU for encryption operations
- More RAM for caching
- More storage for note volume
Horizontal Scaling
For high-traffic deployments:
-
Use Redis Storage
Terminal window STORAGE_PROVIDER=redisREDIS_URL=redis://redis-cluster:6379 -
Deploy Multiple Instances
- Multiple Enclosed containers
- Load balancer distribution
- Shared Redis backend
-
CDN Integration
- Cache static assets
- Reduce origin load
- Improve global performance
Disaster Recovery
Recovery Plan
-
Data Loss Scenarios
- Have recent backups available
- Document restoration procedures
- Test recovery regularly
-
Service Outage
- Deploy standby instance
- Update DNS quickly
- Monitor restoration
-
Security Breach
- Rotate all secrets immediately
- Force user re-authentication
- Audit access logs
- Notify affected users
Recovery Time Objective (RTO)
Target: < 1 hour for full service restoration
- Spin up new instance (15 min)
- Restore from backup (20 min)
- Verify functionality (15 min)
- Update DNS/routing (10 min)
Troubleshooting
Common Issues and Solutions
1. Notes Not Saving
Problem: Notes fail to save or return errors
Solutions:
- Check persistent volume is mounted correctly
- Verify
STORAGE_DIRenvironment variable - Check disk space availability
- Review application logs for errors
# Check volume mountdocker exec -it container_name ls -la /app/data
# Check disk spacedocker exec -it container_name df -h
# View logsdocker logs container_name | grep ERROR2. Encryption/Decryption Failures
Problem: Unable to decrypt notes or “Invalid key” errors
Solutions:
- Verify URL fragment (hash) is not being stripped
- Check browser JavaScript is enabled
- Ensure HTTPS is used (required for Web Crypto API)
- Verify password is correct
# Check if URL has hash fragmentecho "https://your-app.klutch.sh/n/abc123#key" | grep "#"
# Should show the full URL with # character3. File Upload Failures
Problem: File attachments fail to upload
Solutions:
- Check
MAX_FILE_SIZEconfiguration - Verify
ALLOWED_FILE_TYPESsettings - Check storage space
- Review client-side errors in browser console
# Check max file sizedocker exec -it container_name printenv MAX_FILE_SIZE
# View browser console for detailed error4. Rate Limit Errors
Problem: “Too many requests” error
Solutions:
- Adjust rate limit settings
- Implement IP whitelisting
- Use authentication to bypass limits
# Increase rate limitsRATE_LIMIT_MAX_REQUESTS=200RATE_LIMIT_WINDOW_MS=600005. Authentication Issues
Problem: Login fails or sessions expire immediately
Solutions:
- Verify
AUTH_JWT_SECRETis set - Check
AUTH_SESSION_DURATIONvalue - Clear browser cookies
- Regenerate JWT secret
# Generate new JWT secretopenssl rand -base64 64
# Set in environmentAUTH_JWT_SECRET=your-new-secret6. CORS Errors
Problem: Cross-origin request blocked
Solutions:
- Configure
CORS_ORIGINSproperly - Verify protocol (HTTP vs HTTPS)
- Check subdomain configuration
# Allow specific originsCORS_ORIGINS=https://app.yourdomain.com,https://www.yourdomain.com
# Or allow all (development only)CORS_ORIGINS=*7. Performance Issues
Problem: Slow response times or timeouts
Solutions:
- Increase container resources
- Switch to Redis storage
- Enable caching
- Optimize storage cleanup
# Switch to RedisSTORAGE_PROVIDER=redisREDIS_URL=redis://fast-redis-server:6379
# Check resource usagedocker stats container_name8. Storage Growing Rapidly
Problem: Disk space filling up quickly
Solutions:
- Reduce
DEFAULT_TTLandMAX_TTL - Implement aggressive cleanup
- Enable “delete after reading” by default
- Monitor note creation patterns
# Reduce TTL valuesDEFAULT_TTL=1800 # 30 minutesMAX_TTL=86400 # 1 day
# Manual cleanupfind /app/data -type f -mtime +7 -deleteDebugging Commands
View Application Logs
# Real-time logsdocker logs -f container_name
# Last 100 linesdocker logs --tail 100 container_name
# Logs from last hourdocker logs --since 1h container_name
# Filter for errorsdocker logs container_name 2>&1 | grep ERRORCheck Environment Variables
# All environment variablesdocker exec -it container_name env
# Specific variabledocker exec -it container_name printenv PORTVerify Storage
# Check storage directorydocker exec -it container_name ls -la /app/data
# Count notesdocker exec -it container_name find /app/data -type f | wc -l
# Check disk usagedocker exec -it container_name du -sh /app/dataTest Endpoints
# Health checkcurl https://your-app.klutch.sh/api/health
# Create test note (requires proper encryption)curl -X POST https://your-app.klutch.sh/api/notes \ -H "Content-Type: application/json" \ -d '{"encryptedContent":"test","ttl":3600}'Network Diagnostics
# Check container networkingdocker exec -it container_name netstat -tlnp
# DNS resolutiondocker exec -it container_name nslookup your-app.klutch.sh
# Port accessibilitync -zv your-app.klutch.sh 443Advanced Configuration
Custom Branding
Customize the appearance of your Enclosed instance:
Environment Variables
APP_NAME=SecureNotesAPP_BASE_URL=https://notes.yourcompany.comCustom Theme (requires rebuild)
Modify CSS variables in the source:
/* In packages/app-client/src/styles/theme.css */:root { --primary-color: #your-brand-color; --background-color: #your-bg-color; /* Add more custom variables */}Multi-Tenancy
For multiple isolated instances:
# Instance 1docker run -d \ -e STORAGE_DIR=/app/data/tenant1 \ -v tenant1-data:/app/data/tenant1 \ -p 8787:8787 \ enclosed
# Instance 2docker run -d \ -e STORAGE_DIR=/app/data/tenant2 \ -v tenant2-data:/app/data/tenant2 \ -p 8788:8787 \ enclosedIntegration with External Services
Webhook Notifications
Configure webhooks for note events (requires custom modification):
// In packages/app-server/src/webhooks.jsasync function notifyNoteCreated(noteId) { await fetch('https://your-webhook-url.com/note-created', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ event: 'note_created', noteId: noteId, timestamp: new Date().toISOString() }) });}Analytics Integration
Add privacy-respecting analytics:
<!-- In packages/app-client/index.html --><script defer data-domain="your-app.klutch.sh" src="https://plausible.io/js/script.js"></script>High Availability Setup
Redis Cluster
# Use Redis cluster for HASTORAGE_PROVIDER=redisREDIS_URL=redis://redis-cluster:6379REDIS_CLUSTER_ENABLED=trueREDIS_NODES=redis1:6379,redis2:6379,redis3:6379Load Balancer Configuration
# Nginx load balancer configupstream enclosed { server enclosed1:8787; server enclosed2:8787; server enclosed3:8787;}
server { listen 443 ssl http2; server_name your-app.klutch.sh;
location / { proxy_pass http://enclosed; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }}Additional Resources
Official Documentation
Source Code and Community
Security Resources
- Web Crypto API Specification
- OWASP Cryptographic Storage Guide
- PBKDF2 RFC 5869
- NIST AES-GCM Specification
Related Guides
Similar Projects
- PrivateBin - Zero-knowledge pastebin
- Bitwarden Send - Ephemeral secret sharing
- One-Time Secret - Self-destructing messages
CLI Tools
Conclusion
Enclosed provides a secure, minimalistic solution for sharing encrypted notes and files with end-to-end encryption and zero-knowledge architecture. By deploying on Klutch.sh, you get automatic HTTPS, persistent storage, easy scaling, and simplified operations.
With client-side AES-GCM encryption, PBKDF2 key derivation, and zero server knowledge, your sensitive data remains completely private. Whether you’re sharing passwords with team members, transmitting confidential documents, or sending self-destructing messages, Enclosed ensures your data stays secure.
The combination of Enclosed’s security-first design and Klutch.sh’s deployment simplicity makes it easy to run your own private note-sharing platform without compromising security or privacy.
Ready to deploy? Head over to klutch.sh/app and get your Enclosed instance running in minutes!
For questions or support, visit the Enclosed community discussions or check the official documentation.