A private, encrypted AI chat application running entirely on Val Town. Your conversations are encrypted client-side before being stored, and your password never leaves your device.
- 🔒 Client-side encryption - All chats encrypted with AES-256-GCM before storage
- 🔑 Master password protection - Single password protects all your conversations
- 💾 Self-hosted on Val Town - Your data stays in your own Val Town SQLite database
- 🚫 No tracking - No analytics, no third parties
- 🧠 Local AI - Uses WebLLM to run AI models directly in your browser
- 🎛️ Multiple AI models - Choose between light, medium, and large models
- 🍴 Fork-friendly - Easy to remix and run your own instance
- Set a master password on first use to protect all your chats
- Create unlimited chats with custom names
- Your messages are encrypted in the browser using your master password
- Encrypted data is stored in your Val Town SQLite database
- Only you can decrypt your chats with the correct password
Your password is never sent to the server. Encryption happens entirely in your browser.
- Click the "Remix" button on Val Town (or fork this repository)
- This creates your own private copy of the app
When you first run your forked val, check the console logs. You'll see a message like:
⚠️ STORAGE_API_KEY not set! Auto-generated key: a1b2c3d4-e5f6-7890-abcd-ef1234567890
⚠️ Save this key to your STORAGE_API_KEY environment variable!
Important: Save this API key! It protects your storage endpoint.
- Go to your Val Town settings
- Navigate to Environment Variables
- Add a new variable:
- Name:
STORAGE_API_KEY - Value: (paste the key from console logs)
- Name:
- Save and restart your val
In lib/api.ts, update line 3 with your storage val URL:
const STORAGE_URL = "https://your-username--your-storage-val-id.web.val.run";
You can find this URL from your forked storage val.
Visit your main val's URL:
- Set a strong master password on first visit
- Create your first chat with the "+ New Chat" button
- Select your preferred AI model from the dropdown
- Start chatting - all messages are automatically encrypted and saved!
✅ SQL Injection Prevention - All database queries use parameterized statements ✅ API Authentication - Storage endpoint requires API key ✅ Client-side Encryption - AES-256-GCM with PBKDF2 key derivation ✅ Master Password Protection - Single password encrypts all conversations ✅ Session Management - Password stored only in sessionStorage, cleared on lock
- Algorithm: AES-GCM (256-bit)
- Key Derivation: PBKDF2 with 100,000 iterations
- Random Salt & IV: Generated for each encryption
- Password: Never leaves your device
Your Val Town SQLite database stores:
- Chat ID (random UUID)
- Chat name (plain text)
- Encrypted chat data (ciphertext + salt + IV)
- Timestamps
Without your password, the encrypted data is unreadable.
| Variable | Required | Description |
|---|---|---|
STORAGE_API_KEY | Yes | Protects your storage API from unauthorized access |
FRONTEND_URL | No | Optional CORS restriction (e.g., https://yourapp.val.run) |
┌─────────────────┐
│ Browser │
│ (React App) │
│ │
│ • WebLLM AI │
│ • Encryption │
│ • UI │
└────────┬────────┘
│
│ HTTPS + API Key
│
┌────────▼────────┐
│ Val Town │
│ Storage API │
│ │
│ • SQLite DB │
│ • Auth Check │
└─────────────────┘
├── main.ts # Entry point, serves HTML
├── App.tsx # Main app with master password flow
├── api/
│ └── storage.ts # Storage API with authentication
├── lib/
│ ├── api.ts # Client-side API calls
│ ├── crypto.ts # Encryption/decryption logic
│ └── models.ts # AI model definitions
├── hooks/
│ ├── useEncryption.ts # Encryption state management
│ └── useWebLLM.ts # AI model integration
├── screens/
│ ├── MasterPasswordScreen.tsx # Password creation/verification
│ └── MainScreen.tsx # Main chat interface with sidebar
└── components/
├── MessageBubble.tsx # Message display
├── StatusBadge.tsx # AI status indicator
├── EmptyState.tsx # Empty state UI
├── ModelSelector.tsx # AI model selection dropdown
├── OptionsMenu.tsx # Lock/delete options menu
└── ChatNameModal.tsx # Create/rename chat modal
Use a strong, unique master password. Since encryption happens client-side, your master password is the only thing protecting ALL your data.
There is no password recovery. If you forget your master password, ALL your chat data is permanently inaccessible. This is by design - we don't have access to your password.
The app offers three AI models:
- Phi-3 Mini (2.3GB) - Fastest, best for quick responses
- Llama 3.2 3B (1.9GB) - Balanced performance and quality (default)
- Llama 3.1 8B (4.7GB) - Highest quality responses, slower
Models download to your browser once and persist. You can switch models anytime in the chat interface.
Since this is self-hosted, you're responsible for backups. Val Town SQLite data persists, but you may want to:
- Export your encrypted data periodically
- Save your master password securely (use a password manager)
- Consider backing up your Val Town val
Problem: API calls return 401 Unauthorized
Solution:
- Check that
STORAGE_API_KEYis set in environment variables - Make sure the key matches in both your main val and storage val
- Restart both vals after setting the variable
Problem: Can't unlock the app
Solution:
- Double-check your master password (passwords are case-sensitive)
- There is no password recovery - if you forgot it, all your chats are inaccessible
Problem: No chats appear on home screen
Solution:
- Check browser console for errors
- Verify
STORAGE_URLinlib/api.tspoints to your storage val - Ensure storage val is running and accessible
Problem: WebLLM status shows error
Solution:
- WebLLM downloads models to browser (can be 1-2 GB)
- Check your internet connection
- Try refreshing the page
- Check browser console for specific errors
For stronger encryption, increase iterations in lib/crypto.ts (line 25):
iterations: 600000, // Increased from 100,000
Trade-off: Higher security, slower encryption/decryption.
To only allow requests from your frontend:
- Set
FRONTEND_URLenvironment variable - Value:
https://your-main-val.val.run - Restart storage val
This prevents other websites from calling your API (browser-level protection).
To add additional AI models beyond the three included:
- Edit
lib/models.tsto add model definitions - Add to the
AVAILABLE_MODELSarray with model ID, name, size, and description - Find compatible model IDs in the WebLLM documentation
- Lock App: Click the options menu (•••) in the bottom-right to lock the app. This clears your password from the session and returns to the master password screen.
- Delete All Data: Use the options menu to permanently delete all chats from your database. This action cannot be undone.
None. This app doesn't collect, track, or transmit any data beyond what's necessary for functionality:
- Your chats are encrypted and stored in your Val Town SQLite database
- Your password never leaves your browser
- No analytics, no tracking, no third parties
Only you. When you fork this val:
- You own the Val Town vals
- You control the database
- You set the API key
- You hold the encryption passwords
No one else (including the original author) can access your chats.
Found a bug or want to improve the code? Feel free to:
- Fork this val
- Make your improvements
- Share your enhanced version!
This is open source - make it your own.
While this app implements strong encryption and security practices, please note:
- ⚠️ This is a personal project, not audited by security professionals
- ⚠️ API keys are visible in browser DevTools (use strong, unique keys)
- ⚠️ Val Town is a public platform - keep your vals private if needed
- ⚠️ No rate limiting implemented (could be added if needed)
For maximum security:
- Use strong, unique passwords
- Set a strong
STORAGE_API_KEY - Keep your Val Town account secure
- Consider enabling 2FA on your Val Town account
Built with:
- Val Town - Hosting & SQLite
- React - UI framework
- WebLLM - Browser-based AI
- Web Crypto API - Encryption
- Tailwind CSS - Styling
MIT License - Feel free to fork, remix, and make it your own!
Remember: Your master password is your key. Keep it safe, keep it secret, keep it strong.