A secure, encrypted notes application with Go backend and Next.js frontend.
Scrypts is a production-ready full-stack web application for creating and managing encrypted notes. It features hardened security, JWT authentication, rate limiting, and a modern TypeScript frontend. Includes comprehensive security measures based on OWASP best practices and VAPT audit recommendations.
- Backend: Go with JWT auth, bcrypt password hashing, AES-GCM encryption, SQLite storage
- Frontend: Next.js 14 + TypeScript + React 18
- Security: Rate limiting, CORS whitelist, security headers, timing attack prevention
- Communication: REST API with strict CORS policy
- Encryption: Per-user AES-GCM encryption keys, server-side decryption
- Rate Limiting: 10 requests/minute per IP on auth endpoints
- CORS Whitelist: Strict origin validation with
ALLOWED_ORIGINSenv var - Security Headers: HSTS, CSP, X-Frame-Options, X-Content-Type-Options, etc.
- Entropy Validation: Enforces strong secrets (32+ chars, 4.0+ bits/byte)
- Timing Attack Prevention: Constant-time operations in authentication
- User Enumeration Prevention: Generic errors and random delays
- Username Validation: Regex whitelist (alphanumeric, underscore, hyphen only)
- Password Complexity: Enforces uppercase, lowercase, digits, special chars (min 8 chars)
- Bcrypt Cost 12: Increased from default for stronger password hashing
- User registration with bcrypt password hashing (cost: 12)
- JWT-based authentication with configurable expiry
- AES-GCM encryption for note content (server stores encrypted data)
- Server-side decryption for GET requests (plaintext response)
- Per-user encryption keys wrapped with master key
- SQLite persistence with WAL mode and foreign keys
- Rate-limited authentication endpoints (10 req/min per IP)
- CORS middleware with whitelist validation
- Security headers middleware (HSTS, CSP, X-Frame-Options, etc.)
- TLS/HTTPS support with configurable ports and HTTP→HTTPS redirect
- Input validation with regex patterns and ownership checks
- Efficient database queries with indexes
- TypeScript + React 18 with Next.js 14
- User registration and login UI
- Full CRUD interface for notes
- JWT token storage in localStorage
- Real-time note updates with edit mode
- Responsive design
- Error handling and user feedback
- No npm vulnerabilities (regularly updated)
- Go 1.20+
- Node.js 18+ and npm/yarn
- Terminal access
# From project root
cd /home/syko/go_stuff/scrypts
# Set required environment variables
export JWT_SECRET="$(openssl rand -base64 48)"
export MASTER_KEY="$(openssl rand -base64 48)"
export ALLOWED_ORIGINS="http://localhost:3000"
# Build the backend
go build -o scrypts ./cmd/scrypts
# Run the backend (HTTP mode for local dev)
./scryptsThe backend will start on http://localhost:8080.
In a new terminal:
# Navigate to frontend directory
cd /home/syko/go_stuff/scrypts/frontend
# Install dependencies (first time only)
npm install
# Run development server
npm run devThe frontend will start on http://localhost:3000.
- Open http://localhost:3000 in your browser
- Click Register to create a new account
- Enter a username and password
- Click Login to authenticate
- Create, edit, and delete encrypted notes!
-
POST /register— Register a new user- Body:
{"username": "user", "password": "pass"} - Response:
201 Createdor error message
- Body:
-
POST /login— Login and receive JWT token- Body:
{"username": "user", "password": "pass"} - Response:
{"token": "jwt_token_here"}
- Body:
-
POST /notes— Create a new encrypted note- Header:
Authorization: Bearer <token> - Body:
{"content": "note text"} - Response:
{"id": "note-uuid"}
- Header:
-
GET /notes— List all notes for authenticated user- Header:
Authorization: Bearer <token> - Response:
[{"id": "...", "content": "...", "created": ..., "modified": ...}]
- Header:
-
PUT /notes— Update a note- Header:
Authorization: Bearer <token> - Body:
{"id": "note-uuid", "content": "updated text"} - Response:
{"status": "updated"}
- Header:
-
DELETE /notes— Delete a note- Header:
Authorization: Bearer <token> - Body:
{"id": "note-uuid"} - Response:
{"status": "deleted"}
- Header:
Critical - Application will not start without these:
JWT_SECRET- JWT signing key (min 32 chars, high entropy required)export JWT_SECRET="$(openssl rand -base64 48)"
MASTER_KEY- Master encryption key for wrapping user keys (min 32 chars, high entropy required)export MASTER_KEY="$(openssl rand -base64 48)"
Recommended:
ALLOWED_ORIGINS- Comma-separated list of allowed CORS origins (default:http://localhost:3000,http://localhost:8080)export ALLOWED_ORIGINS="http://localhost:3000,https://yourdomain.com"
Optional:
SCRYPTS_DB_PATH- Database file path (default:./scrypts.db)SCRYPTS_TLS_CERT- Path to TLS certificate (optional)SCRYPTS_TLS_KEY- Path to TLS private key (optional)SCRYPTS_HTTPS_PORT- HTTPS port (default:8443)SCRYPTS_HTTP_PORT- HTTP port or redirector port (default:8080)
NEXT_PUBLIC_SCRYPTS_API- Backend API URL (default:http://localhost:8080)
- Bcrypt password hashing with cost factor 12 (increased from default)
- JWT tokens with configurable expiry
- Username validation with regex:
^[a-zA-Z0-9_-]{4,255}$ - Password complexity requirements: min 8 chars, uppercase, lowercase, digit, special char
- Rate limiting: 10 requests/minute per IP on
/registerand/login - Timing attack prevention: Constant-time operations, dummy hash for non-existent users
- User enumeration prevention: Generic error messages with random delays
- Ownership verification on all note operations
- AES-256-GCM authenticated encryption for all note content
- Per-user encryption keys derived from password using scrypt
- User keys wrapped with master key for secure storage
- Server-side decryption for GET requests (plaintext in response)
- Nonces stored per-note for GCM security
- Security Headers: HSTS, CSP, X-Frame-Options, X-Content-Type-Options, X-XSS-Protection
- CORS Whitelist: Strict origin validation (no permissive
*) - Secret Validation: Enforces 32+ character secrets with entropy checking (4.0+ bits/byte)
- SQLite with WAL mode for better concurrency
- Foreign key constraints for data integrity
- Input validation at HTTP layer with regex patterns
- UUID validation for note IDs
- TLS 1.2+ with secure cipher preferences
- HTTP to HTTPS redirect support
scrypts/
├── cmd/scrypts/
│ └── main.go # Application entry point with middleware chain
├── internal/
│ ├── auth/
│ │ ├── handler.go # Registration, login, JWT (with timing attack prevention)
│ │ └── password.go # Bcrypt password hashing (cost: 12)
│ ├── config/
│ │ └── config.go # Configuration with entropy validation
│ ├── middleware/
│ │ ├── cors.go # CORS whitelist middleware
│ │ ├── security.go # Security headers middleware (NEW)
│ │ └── ratelimit.go # Rate limiting middleware (NEW)
│ ├── notes/
│ │ └── handler.go # Notes CRUD handlers
│ ├── storage/
│ │ └── storage.go # SQLite database layer with regex validation
│ └── utils/
│ └── crypto.go # AES-GCM encryption utilities
├── frontend/
│ ├── pages/
│ │ ├── _app.tsx # Next.js app wrapper
│ │ ├── index.tsx # Login/register page
│ │ └── notes.tsx # Notes CRUD interface
│ ├── styles/
│ │ └── globals.css # Global styles
│ ├── package.json # Frontend dependencies (Next.js 14)
│ ├── tsconfig.json # TypeScript config
│ └── next.config.js # Next.js configuration
├── go.mod # Go dependencies
├── go.sum # Go dependency checksums
└── README.md # This file
-
Generate strong secrets using cryptographically secure random generators:
export JWT_SECRET="$(openssl rand -base64 48)" export MASTER_KEY="$(openssl rand -base64 48)"
-
Configure CORS whitelist for your production domains:
export ALLOWED_ORIGINS="https://yourdomain.com,https://app.yourdomain.com"
-
Use a reverse proxy (nginx/Caddy/Traefik) for TLS termination
-
Run as systemd service with limited privileges
-
Set up DB backups and monitoring
-
Enable logging and metrics collection
-
Use production-grade secrets manager (AWS Secrets Manager, Vault, etc.)
Example systemd service:
[Unit]
Description=Scrypts API Server
After=network.target
[Service]
Type=simple
User=scrypts
WorkingDirectory=/opt/scrypts
ExecStart=/opt/scrypts/scrypts
Environment=SCRYPTS_DB_PATH=/var/lib/scrypts/scrypts.db
Restart=on-failure
[Install]
WantedBy=multi-user.target- Build production bundle:
npm run build - Deploy to Vercel, Netlify, or serve with
npm start - Set
NEXT_PUBLIC_SCRYPTS_APIto your production backend URL - Configure CDN and caching for static assets
- Enable production optimizations in Next.js config
For local testing with self-signed certificates:
openssl req -x509 -newkey rsa:4096 -nodes -days 365 \
-keyout key.pem -out cert.pem \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
export SCRYPTS_TLS_CERT=/path/to/cert.pem
export SCRYPTS_TLS_KEY=/path/to/key.pem
export SCRYPTS_HTTPS_PORT=8443
./scryptsFor production, use Let's Encrypt via reverse proxy or certbot.
- Ensure backend is running with CORS middleware enabled
- Check that your origin is in the
ALLOWED_ORIGINSenvironment variable - For local dev:
export ALLOWED_ORIGINS="http://localhost:3000" - For production: Update
ALLOWED_ORIGINSwith your production domain - Check browser console for specific origin issues
- Verify backend is running on port 8080
- Check JWT token is stored in localStorage (browser dev tools → Application)
- Ensure user is logged in and token hasn't expired
- Run
npm installto ensure all dependencies are installed - Check
tsconfig.jsonis present in frontend directory - Clear Next.js cache:
rm -rf .next && npm run dev
- Ports < 1024 require root or special capabilities
- Use
sudo setcap 'cap_net_bind_service=+ep' ./scrypts - Or run behind a reverse proxy on privileged ports
Run the included E2E test script:
./test.zshThis will:
- Start a test server
- Register a user
- Login and obtain JWT
- Create, read, update, and delete notes
- Clean up test database
Run the security test suite:
./test_security.zshThis validates:
- ✅ Username validation (regex enforcement)
- ✅ Security headers (HSTS, CSP, X-Frame-Options, etc.)
- ✅ Rate limiting (10 req/min on auth endpoints)
- ✅ CORS whitelist policy
- ✅ Password complexity requirements
# Run in development mode (requires env vars)
export JWT_SECRET="$(openssl rand -base64 48)"
export MASTER_KEY="$(openssl rand -base64 48)"
export ALLOWED_ORIGINS="http://localhost:3000"
go run ./cmd/scrypts
# Build for production
go build -o scrypts ./cmd/scrypts
# Run tests
go test ./...
# Check for errors
go vet ./...cd frontend
# Development server with hot reload
npm run dev
# Production build
npm run build
npm start
# Type checking
npx tsc --noEmit- Rate limiting on auth endpoints (10 req/min per IP)
- Security headers middleware (HSTS, CSP, X-Frame-Options, etc.)
- CORS whitelist with environment variable configuration
- Entropy validation for secrets (32+ chars, 4.0+ bits/byte)
- Timing attack prevention in authentication
- User enumeration prevention
- Username regex validation
- Increased bcrypt cost to 12
- Comprehensive security test suite
- CSRF protection middleware
- Implement refresh tokens for session management
- Add audit logging for authentication events
- Add health check endpoint
- Set up monitoring and logging (Prometheus, ELK)
- Add end-to-end tests with Playwright
- Implement password reset flow
- Add two-factor authentication
- Migrate to PostgreSQL for production scale
- Add real-time collaboration with WebSockets
- Implement note sharing and permissions
Pull requests and issues are welcome! Please follow security best practices and code style guidelines.
- Write tests for new features
- Follow Go and TypeScript best practices
- Document API changes
- Update README when adding features
- Run formatters (gofmt, prettier) before committing
This project has undergone a comprehensive VAPT (Vulnerability Assessment and Penetration Testing) security audit. All critical and high-priority vulnerabilities have been addressed.
🔒 Security Features:
- Rate limiting (10 req/min per IP)
- CORS whitelist enforcement
- Security headers (HSTS, CSP, X-Frame-Options, etc.)
- Strong secret validation (32+ chars, entropy checking)
- Timing attack prevention
- User enumeration prevention
- Bcrypt cost 12
- Username regex validation