- Desktop App: Stored in OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service)
- Web App: Must be configured via environment variables (less secure)
- Migration: Automatic migration from .env to secure storage on first launch
- Launch the Gramstr desktop app
- Navigate to Settings β NOSTR Configuration
- Either:
- Click "Generate New" to create a new key pair
- Enter your existing
nsecprivate key
- Click "Save Securely" to store in OS keychain
- The key is now encrypted and hardware-bound
If you previously had NOSTR_PRIVATE_KEY in your .env file:
- Launch the desktop app
- The app will automatically detect and migrate the key
- The key will be removed from the environment and stored securely
# Critical secrets that must NEVER be in code:
CLERK_SECRET_KEY
DATABASE_URL
STRIPE_SECRET_KEY
STRIPE_WEBHOOK_SECRET
SUPABASE_SERVICE_ROLE_KEY- HashiCorp Vault - Enterprise secret management
- AWS Secrets Manager - For AWS deployments
- Vercel Environment Variables - For Vercel deployments
- Docker Secrets - For containerized deployments
# Bad - credentials in URL
DATABASE_URL=postgresql://user:password@host:5432/db
# Good - use environment variable references
DATABASE_URL=${DB_PROTOCOL}://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}
# Best - use IAM/certificate authentication
DATABASE_URL=postgresql://host:5432/db?sslmode=require&sslcert=/path/to/cert# Set restrictive permissions on .env files
chmod 600 .env.local
chmod 600 .env.production.local# Ensure .gitignore includes all env files
.env
.env.*
!.env.example
!.env.production.secure
# Remove sensitive files from git history
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch .env*' \
--prune-empty --tag-name-filter cat -- --all- All credentials removed from code
- Environment variables configured in deployment platform
- NOSTR keys stored in OS keychain (desktop) or secure env (server)
- Database using connection pooling with SSL
- API rate limiting configured
- Authentication middleware enabled
- Debug endpoints removed from production
- Rotate credentials every 90 days
- Review access logs weekly
- Update dependencies monthly
- Security scan with
npm audit - Penetration testing quarterly
- Immediately rotate all affected credentials
- Check access logs for unauthorized use
- Update all deployed instances
- Notify affected users if applicable
- Document incident and remediation
- Security issues: security@gramstr.com
- Bug bounty program: bounty.gramstr.com
- Emergency hotline: [Configure emergency contact]
// NEVER do this
const PRIVATE_KEY = "nsec1abc..."
// Do this instead
const privateKey = await secureStorage.get('nostr_key')// Add rate limiting
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests
})
app.use('/api/', limiter)// Always verify authentication
export async function middleware(request: NextRequest) {
const { userId } = auth()
if (!userId && request.nextUrl.pathname.startsWith('/api/protected')) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
return NextResponse.next()
}- macOS: Uses Keychain Services API
- Windows: Uses Windows Credential Manager
- Linux: Uses Secret Service API (GNOME Keyring/KWallet)
If OS keychain is unavailable:
- Uses Electron's
safeStorageAPI - Derives key from hardware identifiers
- Encrypts with AES-256-GCM
- Stores encrypted blob locally
- Keys never stored in plain text
- Keys never transmitted over network
- Keys bound to user account and device
- Automatic cleanup on uninstall