| .. | ||
| app | ||
| cmd | ||
| config | ||
| internal | ||
| migrations | ||
| pkg | ||
| static/blacklist | ||
| test/integration | ||
| .dockerignore | ||
| .env.sample | ||
| .gitignore | ||
| dev.Dockerfile | ||
| docker-compose.dev.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
| README.md | ||
| Taskfile.yml | ||
| test_tags_api.sh | ||
| tools.go | ||
🚀 MapleFile Backend
Secure, end-to-end encrypted file storage backend - Zero-knowledge architecture built with Go.
MapleFile provides military-grade file encryption with client-side E2EE (End-to-End Encryption). Features include collection-based organization, granular sharing permissions, JWT authentication, and S3-compatible object storage. Your files are encrypted on your device before reaching our servers - we never see your data.
📋 Prerequisites
⚠️ Required: You must have the infrastructure running first.
If you haven't set up the infrastructure yet:
- Go to
../infrastructure/README.md - Follow the setup instructions
- Come back here once infrastructure is running
Verify infrastructure is healthy:
cd cloud/infrastructure/development
task dev:status
# All services should show (healthy)
🏁 Getting Started
Installation
# From the monorepo root:
cd cloud/maplefile-backend
# Create environment file:
cp .env.sample .env
# Start the backend:
task dev
The backend runs at http://localhost:8000
Verify Installation
Open a new terminal (leave task dev running):
curl http://localhost:8000/health
# Should return: {"status":"healthy","service":"maplefile-backend","di":"Wire"}
Note: Your first terminal shows backend logs. Keep it running and use a second terminal for testing.
💻 Developing
Initial Configuration
Environment Files:
.env.sample- Template with defaults (committed to git).env- Your local configuration (git-ignored, created from.env.sample)- Use only
.envfor configuration (docker-compose loads this file)
The .env file defaults work for Docker development. Optional: Change BACKEND_APP_JWT_SECRET to a random string (use a password generator).
Running in Development Mode
# Start backend with hot-reload
task dev
# View logs (in another terminal)
docker logs -f maplefile-backend-dev
# Stop backend
task dev:down
# Or press Ctrl+C in the task dev terminal
What happens when you run task dev:
- Docker starts the backend container
- Auto-migrates database tables
- Starts HTTP server on port 8000
- Enables hot-reload (auto-restarts on code changes)
Wait for: ✅ Database migrations completed successfully in the logs
Daily Workflow
# Morning - check infrastructure (from monorepo root)
cd cloud/infrastructure/development && task dev:status
# Start backend (from monorepo root)
cd cloud/maplefile-backend && task dev
# Make code changes - backend auto-restarts
# Stop backend when done
# Press Ctrl+C
Testing
# Run all tests
task test
# Code quality checks
task format # Format code
task lint # Run linters
Database Operations
View database:
# From monorepo root
cd cloud/infrastructure/development
task cql
# Inside cqlsh:
USE maplefile;
DESCRIBE TABLES;
SELECT * FROM users_by_id;
Reset database (⚠️ deletes all data):
task db:clear
🔧 Usage
Testing the API
Create a test user to verify the backend works:
1. Register a user:
curl -X POST http://localhost:8000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"first_name": "Test",
"last_name": "User",
"phone": "+1234567890",
"country": "Canada",
"timezone": "America/Toronto",
"salt": "base64-encoded-salt",
"kdf_algorithm": "argon2id",
"kdf_iterations": 3,
"kdf_memory": 65536,
"kdf_parallelism": 4,
"kdf_salt_length": 16,
"kdf_key_length": 32,
"encryptedMasterKey": "base64-encoded-encrypted-master-key",
"publicKey": "base64-encoded-public-key",
"encryptedPrivateKey": "base64-encoded-encrypted-private-key",
"encryptedRecoveryKey": "base64-encoded-encrypted-recovery-key",
"masterKeyEncryptedWithRecoveryKey": "base64-encoded-master-key-encrypted-with-recovery",
"agree_terms_of_service": true,
"agree_promotions": false,
"agree_to_tracking_across_third_party_apps_and_services": false
}'
Note: MapleFile uses end-to-end encryption. The frontend (maplefile-frontend) handles all cryptographic operations. For manual API testing, you'll need to generate valid encryption keys using libsodium. See the frontend registration implementation for reference.
Response:
{
"message": "Registration successful. Please check your email to verify your account.",
"user_id": "uuid-here"
}
2. Verify email: Check your email for the verification code, then:
curl -X POST http://localhost:8000/api/v1/auth/verify-email \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"verification_code": "123456"
}'
3. Login:
curl -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com"
}'
Check your email for the OTP (One-Time Password), then complete login:
curl -X POST http://localhost:8000/api/v1/auth/login/verify-otp \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"otp": "your-otp-code",
"encrypted_challenge": "base64-encoded-challenge-response"
}'
Response:
{
"access_token": "eyJhbGci...",
"refresh_token": "eyJhbGci...",
"access_expiry": "2025-11-12T13:00:00Z",
"refresh_expiry": "2025-11-19T12:00:00Z"
}
Save the access_token from the response:
export TOKEN="eyJhbGci...your-access-token-here"
4. Get your profile:
curl http://localhost:8000/api/v1/me \
-H "Authorization: JWT $TOKEN"
5. Get dashboard:
curl http://localhost:8000/api/v1/dashboard \
-H "Authorization: JWT $TOKEN"
6. Create a collection (folder):
curl -X POST http://localhost:8000/api/v1/collections \
-H "Content-Type: application/json" \
-H "Authorization: JWT $TOKEN" \
-d '{
"name": "My Documents",
"description": "Personal documents",
"collection_type": "folder",
"encrypted_collection_key": "base64-encoded-encrypted-key"
}'
7. Upload a file:
# First, get a presigned URL
curl -X POST http://localhost:8000/api/v1/files/presigned-url \
-H "Content-Type: application/json" \
-H "Authorization: JWT $TOKEN" \
-d '{
"file_name": "document.pdf",
"file_size": 1024000,
"mime_type": "application/pdf",
"collection_id": "your-collection-id"
}'
# Upload the encrypted file to the presigned URL (using the URL from response)
curl -X PUT "presigned-url-here" \
--upload-file your-encrypted-file.enc
# Report upload completion
curl -X POST http://localhost:8000/api/v1/files/upload-complete \
-H "Content-Type: application/json" \
-H "Authorization: JWT $TOKEN" \
-d '{
"file_id": "file-id-from-presigned-response",
"status": "completed"
}'
Frontend Integration
Access the frontend:
- URL: http://localhost:5173
- The frontend handles all encryption/decryption automatically
- See
../../web/maplefile-frontend/README.md
Key Features:
- 🔐 Client-side encryption - Files encrypted before upload
- 🔑 E2EE Key Chain - Password → KEK → Master Key → Collection Keys → File Keys
- 📁 Collections - Organize files in encrypted folders
- 🤝 Sharing - Share collections with read-only, read-write, or admin permissions
- 🔄 Sync modes - Cloud-only, local-only, or hybrid storage
Next steps:
- Frontend setup:
../../web/maplefile-frontend/README.md - Complete API documentation: See API endpoints in code
⚙️ Configuration
Environment Variables
Key variables in .env:
| Variable | Default | Description |
|---|---|---|
BACKEND_APP_JWT_SECRET |
change-me-in-production |
Secret for JWT token signing |
BACKEND_APP_SERVER_PORT |
8000 |
HTTP server port |
BACKEND_DB_HOSTS |
cassandra-1,cassandra-2,cassandra-3 |
Cassandra cluster nodes |
BACKEND_CACHE_HOST |
redis |
Redis cache host |
BACKEND_MAPLEFILE_S3_ENDPOINT |
http://seaweedfs:8333 |
S3 storage URL |
BACKEND_MAPLEFILE_S3_BUCKET |
maplefile |
S3 bucket name |
Docker vs Local:
- Docker: Uses container names (
cassandra-1,redis,seaweedfs) - Local: Change to
localhost
See .env.sample for complete documentation.
Task Commands
| Command | Description |
|---|---|
task dev |
Start backend (auto-migrate + hot-reload) |
task dev:down |
Stop backend |
task test |
Run tests |
task format |
Format code |
task lint |
Run linters |
task db:clear |
Reset database (⚠️ deletes data) |
task migrate:up |
Manual migration |
task build |
Build binary |
🔍 Troubleshooting
Backend won't start - "connection refused"
Error: dial tcp 127.0.0.1:9042: connect: connection refused
Cause: .env file has localhost instead of container names.
Fix:
cd cloud/maplefile-backend
rm .env
cp .env.sample .env
task dev
Infrastructure not running
Error: Cassandra or Redis not available
Fix:
cd cloud/infrastructure/development
task dev:start
task dev:status # Wait until all show (healthy)
Port 8000 already in use
Fix:
lsof -i :8000 # Find what's using the port
# Stop the other service, or change BACKEND_APP_SERVER_PORT in .env
Token expired (401 errors)
JWT tokens expire after 60 minutes. Re-run the login steps to get a new token.
Database keyspace not found
Error: Keyspace 'maplefile' does not exist or failed to create user
Cause: The Cassandra keyspace hasn't been created yet. This is a one-time infrastructure setup.
Fix:
# Initialize the keyspace (one-time setup)
cd cloud/infrastructure/development
# Find Cassandra container
export CASSANDRA_CONTAINER=$(docker ps --filter "name=cassandra" -q | head -1)
# Create keyspace
docker exec -it $CASSANDRA_CONTAINER cqlsh -e "
CREATE KEYSPACE IF NOT EXISTS maplefile
WITH replication = {
'class': 'SimpleStrategy',
'replication_factor': 3
};"
# Verify keyspace exists
docker exec -it $CASSANDRA_CONTAINER cqlsh -e "DESCRIBE KEYSPACE maplefile;"
# Restart backend to retry migrations
cd ../../maplefile-backend
task dev:restart
Note: The backend auto-migrates tables on startup, but expects the keyspace to already exist. This is standard practice - keyspaces are infrastructure setup, not application migrations.
🛠️ Technology Stack
- Go 1.23+ - Programming language
- Clean Architecture - Code organization
- Wire - Dependency injection (Google's code generation)
- Cassandra 5.0.4 - Distributed database (3-node cluster)
- Redis 7 - Caching layer
- SeaweedFS - S3-compatible object storage
- JWT - User authentication
- ChaCha20-Poly1305 - Authenticated encryption (client-side)
- Argon2id - Password hashing / KDF
🌐 Services
When you run MapleFile, these services are available:
| Service | Port | Purpose | Access |
|---|---|---|---|
| MapleFile Backend | 8000 | HTTP API | http://localhost:8000 |
| MapleFile Frontend | 5173 | Web UI | http://localhost:5173 |
| Cassandra | 9042 | Database | task cql (from infrastructure dir) |
| Redis | 6379 | Cache | task redis (from infrastructure dir) |
| SeaweedFS S3 | 8333 | Object storage | http://localhost:8333 |
| SeaweedFS UI | 9333 | Storage admin | http://localhost:9333 |
🏗️ Architecture
Project Structure
maplefile-backend/
├── cmd/ # CLI commands (daemon, migrate, version)
├── config/ # Configuration loading
├── internal/ # Application code
│ ├── app/ # Wire application wiring
│ ├── domain/ # Domain entities
│ │ ├── collection/ # Collections (folders)
│ │ ├── crypto/ # Encryption types
│ │ ├── file/ # File metadata
│ │ ├── user/ # User accounts
│ │ └── ...
│ ├── repo/ # Repository implementations (Cassandra)
│ ├── usecase/ # Use cases / business logic
│ ├── service/ # Service layer
│ └── interface/ # HTTP handlers
│ └── http/ # REST API endpoints
├── pkg/ # Shared infrastructure
│ ├── storage/ # Database, cache, S3, memory
│ ├── security/ # JWT, encryption, password hashing
│ └── emailer/ # Email sending
├── migrations/ # Cassandra schema migrations
└── docs/ # Documentation
Key Features
- 🔐 Zero-Knowledge Architecture: Files encrypted on client, server never sees plaintext
- 🔑 E2EE Key Chain: User Password → KEK → Master Key → Collection Keys → File Keys
- 📦 Storage Modes:
encrypted_only,hybrid,decrypted_only - 🤝 Collection Sharing:
read_only,read_write,adminpermissions - 💾 Two-Tier Caching: Redis + Cassandra-based cache
- 📊 Storage Quotas: 10GB default per user
- 🔄 File Versioning: Soft delete with tombstone tracking
End-to-End Encryption Flow
1. User enters password → Frontend derives KEK (Key Encryption Key)
2. KEK → Encrypts/decrypts Master Key (stored encrypted on server)
3. Master Key → Encrypts/decrypts Collection Keys
4. Collection Key → Encrypts/decrypts File Keys
5. File Key → Encrypts/decrypts actual file content
Server only stores:
- Encrypted Master Key (encrypted with KEK from password)
- Encrypted Collection Keys (encrypted with Master Key)
- Encrypted File Keys (encrypted with Collection Key)
- Encrypted file content (encrypted with File Key)
Server NEVER has access to:
- User's password
- KEK (derived from password on client)
- Decrypted Master Key
- Decrypted Collection Keys
- Decrypted File Keys
- Plaintext file content
🔗 Links
- Frontend Application:
../../web/maplefile-frontend/README.md - CLI Tool:
../../native/desktop/maplefile/README.md - Architecture Details:
../../CLAUDE.md - Repository: Codeberg - mapleopentech/monorepo
🤝 Contributing
Found a bug? Want a feature to improve MapleFile? Please create an issue.
📝 License
This application is licensed under the GNU Affero General Public License v3.0. See LICENSE for more information.