LocalNeural is designed for self-hosted deployment where security is a priority but the threat model assumes the deployment environment is controlled by the user. The following security measures are implemented.
- Sessions are signed with
SECRET_KEY(required env var) - Session lifetime: 72 hours (hardcoded, no "Remember Me" option)
- Cookies:
SameSite=Lax,HttpOnly - Session is regenerated on login to prevent session fixation
- Minimum length: 8 characters
- Hashed using werkzeug's
generate_password_hash(scrypt-based) - Passwords are never logged or exposed in responses
- Login always returns: "Invalid username or password" (whether the user exists or not)
- Registration always succeeds (ignoring duplicate errors)
- Forgot-password returns 404 with generic "user_not_found" whether or not the email exists
All POST, PUT, DELETE, PATCH requests to /api/ (except /api/auth/*) require:
X-Requested-With: XMLHttpRequest
This header is automatically set by jQuery's AJAX methods. Browsers enforce same-origin policy, preventing cross-origin requests from setting this header.
Flask-Limiter with in-memory storage:
| Route | Limit |
|---|---|
/api/auth/login |
10/minute |
/api/auth/register |
5/minute |
/api/auth/forgot-password |
3/minute |
/api/account/delete |
2/minute |
Default (all /api/) |
120/minute |
Note: Rate limits reset on server restart (memory storage).
- SQLite file at
.data/neural_memory.db - File is excluded from git via
.gitignore - Database import endpoint requires admin authentication
- Import validates that the file is a valid SQLite database
- Admin routes (
/admin,/admin_dashboard,/admin/user/<uid>) check for admin role - Admin API endpoints (
/api/admin/*) callrequire_admin()which returns 401/403 - Super admin is seeded on first run via
.envvariables - Admin role re-checked on every request (no caching)
- Internal exceptions never expose
str(e)details to users - API errors return structured JSON:
{"error": "message"} - Generic error messages for auth failures
- 500 errors return generic "An internal error occurred"
Admins can block users. Blocked users:
- Cannot log in (403 on login)
- Cannot send messages (error emitted via WebSocket)
- Their existing sessions remain but they cannot create new ones
- Username: trimmed, server-validated
- Email: optional, not validated format
- Password: minimum 8 characters
- API keys: stored as-is in database
- All inputs: Flask request.json parsing
For production deployment:
- Use a strong
SECRET_KEY(256-bit random) - Run behind a reverse proxy (Nginx) with HTTPS
- Restrict network access to the server
- Back up the database regularly
- Use Docker with read-only root filesystem
- Monitor access logs
- Keep Python dependencies updated