Skip to content

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:

  1. Client-Side Encryption: All encryption happens in the browser
  2. Base Key Generation: Random key generated for each note
  3. PBKDF2 Key Derivation: Master key derived from base key + optional password
  4. AES-GCM Encryption: Military-grade symmetric encryption
  5. Zero Server Knowledge: Server only stores encrypted data
  6. URL Fragment Storage: Encryption key stored in URL hash (never sent to server)
  7. No Logging: No tracking or logging of sensitive operations
  8. 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:

Terminal window
mkdir enclosed-klutch
cd enclosed-klutch
git init

Step 2: Create the Dockerfile

Enclosed provides an official Dockerfile. Create a Dockerfile in your project root:

# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy package files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY packages ./packages
# Install dependencies
RUN pnpm install --frozen-lockfile
# Build the application
RUN pnpm run build
# Production stage
FROM node:20-alpine
WORKDIR /app
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy built application
COPY --from=builder /app/packages/app-server/dist ./dist
COPY --from=builder /app/packages/app-client/dist ./public
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
# Create data directory for persistent storage
RUN mkdir -p /app/data && chmod 755 /app/data
# Expose port
EXPOSE 8787
# Set environment variables
ENV NODE_ENV=production
ENV PORT=8787
ENV STORAGE_DIR=/app/data
# Health check
HEALTHCHECK --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 application
CMD ["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 storage
RUN mkdir -p /app/data && chmod 755 /app/data
# Environment variables (can be overridden at runtime)
ENV PORT=8787
ENV STORAGE_DIR=/app/data
ENV NODE_ENV=production
# Expose port
EXPOSE 8787
# The official image already includes the start command

Option 3: Rootless Deployment (Enhanced Security)

For production environments requiring rootless containers:

FROM corentinth/enclosed:rootless
# The rootless image runs as non-root user by default
USER 1000:1000
# Create data directory
RUN mkdir -p /app/data && chmod 755 /app/data
# Environment variables
ENV PORT=8787
ENV STORAGE_DIR=/app/data
ENV NODE_ENV=production
# Expose port (non-privileged port)
EXPOSE 8787
# Health check
HEALTHCHECK --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 pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Install development dependencies
RUN apk add --no-cache git
# Copy package files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY packages ./packages
# Install all dependencies (including dev)
RUN pnpm install
# Expose port
EXPOSE 8787
# Set development environment
ENV NODE_ENV=development
ENV PORT=8787
# Start development server
CMD ["pnpm", "run", "dev"]

Step 3: Create Environment Configuration

Create a .env.example file to document available configuration options:

# Server Configuration
PORT=8787
NODE_ENV=production
HOST=0.0.0.0
# Storage Configuration
STORAGE_DIR=/app/data
STORAGE_PROVIDER=fs # Options: fs, redis, cloudflare-kv
# Application Configuration
APP_BASE_URL=https://your-app.klutch.sh
APP_NAME=Enclosed
# Security Configuration
MAX_NOTE_SIZE=10485760 # 10MB in bytes
MAX_FILE_SIZE=52428800 # 50MB in bytes
ALLOWED_FILE_TYPES=* # Comma-separated list or * for all
# Rate Limiting
RATE_LIMIT_ENABLED=true
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_WINDOW_MS=60000
# Note Expiration
DEFAULT_TTL=3600 # 1 hour in seconds
MAX_TTL=2592000 # 30 days in seconds
# Authentication (Optional)
AUTH_ENABLED=false
AUTH_JWT_SECRET=your-secure-secret-here
AUTH_SESSION_DURATION=86400 # 24 hours
# CORS Configuration
CORS_ENABLED=true
CORS_ORIGINS=*
# Logging
LOG_LEVEL=info # Options: debug, info, warn, error

Step 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: local

Step 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)
```env
STORAGE_PROVIDER=fs
STORAGE_DIR=/app/data

Redis

STORAGE_PROVIDER=redis
REDIS_URL=redis://redis:6379
REDIS_PREFIX=enclosed:

Cloudflare KV (for Cloudflare Pages)

STORAGE_PROVIDER=cloudflare-kv
KV_NAMESPACE_ID=your-kv-namespace-id

Security Best Practices

  1. Always use HTTPS in production
  2. Set strong AUTH_JWT_SECRET if using authentication
  3. Configure MAX_NOTE_SIZE and MAX_FILE_SIZE appropriately
  4. Enable rate limiting in production
  5. Restrict CORS_ORIGINS to your domain
  6. Use rootless Docker image for enhanced security
  7. 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:
```bash
git clone https://github.com/your-username/enclosed-klutch.git
cd enclosed-klutch
  1. Start with Docker Compose:
Terminal window
docker-compose up -d
  1. Access at http://localhost:8787

Production Deployment

  1. Push to GitHub
  2. Connect to Klutch.sh
  3. Configure environment variables
  4. Attach persistent volume at /app/data
  5. Deploy!

Configuration

Required environment variables:

Optional:

  • MAX_NOTE_SIZE: Maximum note size in bytes
  • DEFAULT_TTL: Default expiration time in seconds
  • AUTH_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

### Step 8: Initialize Git and Push to GitHub
```bash
# Initialize git repository
git init
# Add all files
git add .
# Commit
git 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 GitHub
git branch -M main
git push -u origin main

Deploying on Klutch.sh

Now that your repository is ready, let’s deploy Enclosed on Klutch.sh:

  1. Navigate to Klutch.sh Dashboard

    Go to klutch.sh/app and sign in to your account.

  2. Create a New Project

    • Click on “New Project”
    • Select your GitHub repository containing the Enclosed configuration
    • Choose the repository from the dropdown
  3. 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
  4. Configure Environment Variables

    Add the following environment variables in the Klutch.sh dashboard:

    Required Variables:

    Terminal window
    PORT=8787
    NODE_ENV=production
    STORAGE_DIR=/app/data

    Recommended Variables:

    Terminal window
    APP_BASE_URL=https://your-app-name.klutch.sh
    MAX_NOTE_SIZE=10485760
    MAX_FILE_SIZE=52428800
    DEFAULT_TTL=3600
    MAX_TTL=2592000
    LOG_LEVEL=info

    Security Variables:

    Terminal window
    RATE_LIMIT_ENABLED=true
    RATE_LIMIT_MAX_REQUESTS=100
    RATE_LIMIT_WINDOW_MS=60000

    Optional Authentication:

    Terminal window
    AUTH_ENABLED=false
    # If enabled, set:
    # AUTH_JWT_SECRET=your-random-secure-string-here
    # AUTH_SESSION_DURATION=86400
  5. 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
  6. Deploy the Application

    • Click “Deploy”
    • Klutch.sh will build your Docker image and start Enclosed
    • Monitor the deployment logs for any errors
  7. Verify Deployment

    Once deployed, your Enclosed instance will be accessible at:

    • URL: https://your-app-name.klutch.sh

    Test by:

    1. Opening the URL in your browser
    2. Creating a test note
    3. Verifying encryption (check network tab - content should be encrypted)
    4. Testing note retrieval with the generated link

Configuration

Environment Variables Reference

VariableDescriptionDefaultRequired
PORTHTTP server port8787Yes
NODE_ENVEnvironment modeproductionYes
STORAGE_DIRDirectory for encrypted notes/app/dataYes
HOSTBind address0.0.0.0No
APP_BASE_URLPublic URL of your instance-Recommended
APP_NAMEApplication nameEnclosedNo
STORAGE_PROVIDERStorage backendfsNo
MAX_NOTE_SIZEMaximum note size (bytes)10485760 (10MB)No
MAX_FILE_SIZEMaximum file size (bytes)52428800 (50MB)No
ALLOWED_FILE_TYPESAllowed file types*No
DEFAULT_TTLDefault expiration (seconds)3600 (1 hour)No
MAX_TTLMaximum expiration (seconds)2592000 (30 days)No
RATE_LIMIT_ENABLEDEnable rate limitingtrueNo
RATE_LIMIT_MAX_REQUESTSMax requests per window100No
RATE_LIMIT_WINDOW_MSRate limit window (ms)60000No
AUTH_ENABLEDEnable authenticationfalseNo
AUTH_JWT_SECRETJWT signing secret-If AUTH_ENABLED
AUTH_SESSION_DURATIONSession duration (seconds)86400No
CORS_ENABLEDEnable CORStrueNo
CORS_ORIGINSAllowed origins*No
LOG_LEVELLogging verbosityinfoNo

Storage Configuration

Enclosed supports multiple storage backends:

File System (Default)

Best for single-instance deployments:

Terminal window
STORAGE_PROVIDER=fs
STORAGE_DIR=/app/data

Redis

For high-availability deployments:

Terminal window
STORAGE_PROVIDER=redis
REDIS_URL=redis://your-redis-host:6379
REDIS_PREFIX=enclosed:
REDIS_PASSWORD=your-redis-password

Cloudflare KV

For Cloudflare Pages deployments:

Terminal window
STORAGE_PROVIDER=cloudflare-kv
KV_NAMESPACE_ID=your-kv-namespace-id

Security Configuration

Note Size Limits

Control maximum note and file sizes:

Terminal window
MAX_NOTE_SIZE=10485760 # 10MB
MAX_FILE_SIZE=52428800 # 50MB

File Type Restrictions

Restrict allowed file types:

Terminal window
# Allow all files
ALLOWED_FILE_TYPES=*
# Allow specific types
ALLOWED_FILE_TYPES=image/png,image/jpeg,application/pdf,text/plain

Rate Limiting

Protect against abuse:

Terminal window
RATE_LIMIT_ENABLED=true
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_WINDOW_MS=60000 # 1 minute

CORS Configuration

Restrict cross-origin access:

Terminal window
CORS_ENABLED=true
CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com

Authentication Setup

Enable optional authentication for note creation:

Terminal window
AUTH_ENABLED=true
AUTH_JWT_SECRET=your-very-secure-random-string
AUTH_SESSION_DURATION=86400 # 24 hours

Generate a secure secret:

Terminal window
openssl rand -base64 64

Using Enclosed

Creating a Note

  1. Write Your Note

    • Enter your sensitive text in the note editor
    • Add optional file attachments
  2. 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
  3. 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

  1. Open the Link

    • Click the shared encrypted note link
    • The app fetches the encrypted content
  2. Enter Password (if required)

    • If password-protected, enter the password
    • Wrong password = unable to decrypt
  3. 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:

Terminal window
# Install globally
npm install -g @enclosed/cli
# Or use with npx
npx @enclosed/cli create "Hello, World!"

CLI Commands

Create a note:

Terminal window
# Basic note
enclosed create "My secret message"
# From stdin
echo "Secret data" | enclosed create
# With options
enclosed create \
--password "mypassword" \
--deleteAfterReading \
--ttl 3600 \
"Confidential information"

View a note:

Terminal window
# Password will be prompted if needed
enclosed view https://your-app.klutch.sh/n/abc123#key
# With password inline
enclosed view --password "mypassword" https://your-app.klutch.sh/n/abc123#key

Configure instance:

Terminal window
# Set your custom instance
enclosed config set instance-url https://your-app.klutch.sh
# View current config
enclosed config list

API Integration

Enclosed provides a RESTful API for programmatic access:

Create a Note (cURL)

Terminal window
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;
}
// Usage
createEncryptedNote('My secret message', 'mypassword');

Python Example

import requests
import json
import base64
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes
from 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
# Usage
create_encrypted_note(
'My secret message',
'mypassword',
'https://your-app.klutch.sh'
)

Production Best Practices

Security Hardening

1. Use Strong Secrets

Generate cryptographically secure secrets:

Terminal window
# Generate JWT secret
openssl rand -base64 64
# Generate API keys
openssl rand -hex 32

2. Enable HTTPS Only

Always use HTTPS in production (Klutch.sh provides this automatically).

3. Restrict CORS

Limit CORS to your domain:

Terminal window
CORS_ENABLED=true
CORS_ORIGINS=https://yourdomain.com

4. Implement Rate Limiting

Protect against abuse:

Terminal window
RATE_LIMIT_ENABLED=true
RATE_LIMIT_MAX_REQUESTS=50
RATE_LIMIT_WINDOW_MS=60000

5. Set Reasonable Limits

Terminal window
MAX_NOTE_SIZE=5242880 # 5MB
MAX_FILE_SIZE=26214400 # 25MB
MAX_TTL=604800 # 7 days max

6. 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:

Terminal window
STORAGE_PROVIDER=redis
REDIS_URL=redis://your-redis-host:6379

2. Configure Caching

Enable browser caching for static assets (handled by Enclosed automatically).

3. Optimize Storage

Regularly clean up expired notes:

cleanup-expired.sh
# Create cleanup script
#!/bin/bash
STORAGE_DIR=/app/data/notes
# Remove notes older than MAX_TTL
find $STORAGE_DIR -type f -mtime +30 -delete
echo "Cleanup completed: $(date)"

Run via cron:

Terminal window
0 2 * * * /app/scripts/cleanup-expired.sh

4. Monitor Resources

Set up monitoring for:

  • CPU usage
  • Memory consumption
  • Storage capacity
  • Request latency
  • Error rates

Backup Strategy

1. Automated Backups

Create backup script:

backup-enclosed.sh
#!/bin/bash
BACKUP_DIR=/backups/enclosed
STORAGE_DIR=/app/data
DATE=$(date +%Y%m%d-%H%M%S)
# Create backup directory
mkdir -p $BACKUP_DIR
# Backup encrypted notes
tar czf $BACKUP_DIR/notes-$DATE.tar.gz $STORAGE_DIR
# Keep only last 30 days
find $BACKUP_DIR -name "notes-*.tar.gz" -mtime +30 -delete
echo "Backup completed: $BACKUP_DIR/notes-$DATE.tar.gz"

Schedule with cron:

Terminal window
0 3 * * * /app/scripts/backup-enclosed.sh

2. Offsite Backups

For critical deployments:

#!/bin/bash
# Upload to S3 or similar
aws s3 cp $BACKUP_DIR/notes-$DATE.tar.gz \
s3://your-bucket/enclosed-backups/

3. Test Restoration

Regularly test backup restoration:

Terminal window
# Test restore
tar xzf notes-20250117.tar.gz -C /tmp/restore-test
# Verify contents
ls -la /tmp/restore-test/app/data

Monitoring and Logging

1. Application Logs

Monitor application logs:

Terminal window
# View real-time logs
docker logs -f enclosed-container
# Search for errors
docker logs enclosed-container 2>&1 | grep ERROR
# View last 100 lines
docker logs --tail 100 enclosed-container

2. Health Checks

Enclosed includes a health endpoint:

Terminal window
# Check health status
curl 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:

  1. Use Redis Storage

    Terminal window
    STORAGE_PROVIDER=redis
    REDIS_URL=redis://redis-cluster:6379
  2. Deploy Multiple Instances

    • Multiple Enclosed containers
    • Load balancer distribution
    • Shared Redis backend
  3. CDN Integration

    • Cache static assets
    • Reduce origin load
    • Improve global performance

Disaster Recovery

Recovery Plan

  1. Data Loss Scenarios

    • Have recent backups available
    • Document restoration procedures
    • Test recovery regularly
  2. Service Outage

    • Deploy standby instance
    • Update DNS quickly
    • Monitor restoration
  3. 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

  1. Spin up new instance (15 min)
  2. Restore from backup (20 min)
  3. Verify functionality (15 min)
  4. 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_DIR environment variable
  • Check disk space availability
  • Review application logs for errors
Terminal window
# Check volume mount
docker exec -it container_name ls -la /app/data
# Check disk space
docker exec -it container_name df -h
# View logs
docker logs container_name | grep ERROR

2. 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
Terminal window
# Check if URL has hash fragment
echo "https://your-app.klutch.sh/n/abc123#key" | grep "#"
# Should show the full URL with # character

3. File Upload Failures

Problem: File attachments fail to upload

Solutions:

  • Check MAX_FILE_SIZE configuration
  • Verify ALLOWED_FILE_TYPES settings
  • Check storage space
  • Review client-side errors in browser console
Terminal window
# Check max file size
docker exec -it container_name printenv MAX_FILE_SIZE
# View browser console for detailed error

4. Rate Limit Errors

Problem: “Too many requests” error

Solutions:

  • Adjust rate limit settings
  • Implement IP whitelisting
  • Use authentication to bypass limits
Terminal window
# Increase rate limits
RATE_LIMIT_MAX_REQUESTS=200
RATE_LIMIT_WINDOW_MS=60000

5. Authentication Issues

Problem: Login fails or sessions expire immediately

Solutions:

  • Verify AUTH_JWT_SECRET is set
  • Check AUTH_SESSION_DURATION value
  • Clear browser cookies
  • Regenerate JWT secret
Terminal window
# Generate new JWT secret
openssl rand -base64 64
# Set in environment
AUTH_JWT_SECRET=your-new-secret

6. CORS Errors

Problem: Cross-origin request blocked

Solutions:

  • Configure CORS_ORIGINS properly
  • Verify protocol (HTTP vs HTTPS)
  • Check subdomain configuration
Terminal window
# Allow specific origins
CORS_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
Terminal window
# Switch to Redis
STORAGE_PROVIDER=redis
REDIS_URL=redis://fast-redis-server:6379
# Check resource usage
docker stats container_name

8. Storage Growing Rapidly

Problem: Disk space filling up quickly

Solutions:

  • Reduce DEFAULT_TTL and MAX_TTL
  • Implement aggressive cleanup
  • Enable “delete after reading” by default
  • Monitor note creation patterns
Terminal window
# Reduce TTL values
DEFAULT_TTL=1800 # 30 minutes
MAX_TTL=86400 # 1 day
# Manual cleanup
find /app/data -type f -mtime +7 -delete

Debugging Commands

View Application Logs

Terminal window
# Real-time logs
docker logs -f container_name
# Last 100 lines
docker logs --tail 100 container_name
# Logs from last hour
docker logs --since 1h container_name
# Filter for errors
docker logs container_name 2>&1 | grep ERROR

Check Environment Variables

Terminal window
# All environment variables
docker exec -it container_name env
# Specific variable
docker exec -it container_name printenv PORT

Verify Storage

Terminal window
# Check storage directory
docker exec -it container_name ls -la /app/data
# Count notes
docker exec -it container_name find /app/data -type f | wc -l
# Check disk usage
docker exec -it container_name du -sh /app/data

Test Endpoints

Terminal window
# Health check
curl 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

Terminal window
# Check container networking
docker exec -it container_name netstat -tlnp
# DNS resolution
docker exec -it container_name nslookup your-app.klutch.sh
# Port accessibility
nc -zv your-app.klutch.sh 443

Advanced Configuration

Custom Branding

Customize the appearance of your Enclosed instance:

Environment Variables

Terminal window
APP_NAME=SecureNotes
APP_BASE_URL=https://notes.yourcompany.com

Custom 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:

Terminal window
# Instance 1
docker run -d \
-e STORAGE_DIR=/app/data/tenant1 \
-v tenant1-data:/app/data/tenant1 \
-p 8787:8787 \
enclosed
# Instance 2
docker run -d \
-e STORAGE_DIR=/app/data/tenant2 \
-v tenant2-data:/app/data/tenant2 \
-p 8788:8787 \
enclosed

Integration with External Services

Webhook Notifications

Configure webhooks for note events (requires custom modification):

// In packages/app-server/src/webhooks.js
async 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

Terminal window
# Use Redis cluster for HA
STORAGE_PROVIDER=redis
REDIS_URL=redis://redis-cluster:6379
REDIS_CLUSTER_ENABLED=true
REDIS_NODES=redis1:6379,redis2:6379,redis3:6379

Load Balancer Configuration

# Nginx load balancer config
upstream 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

Similar Projects

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.