Security & Privacy¶
How SynContext protects your data.
Encryption at Rest¶
All sensitive content is encrypted before being stored in the database using Fernet symmetric encryption (AES-128-CBC + HMAC-SHA256) from the cryptography library. Fernet uses a 256-bit key split into a 128-bit AES encryption key and a 128-bit HMAC signing key.
Encrypted fields:
- Context entry content (the body of your notes and documents)
- Decision text and rationale
- Version history content
Encrypted values are prefixed with enc: in the database, enabling backward-compatible migration from plaintext to encrypted storage. If the encryption key (ENCRYPTION_KEY) is not configured, content is stored in plaintext (development mode only).
Not encrypted (needed for full-text search and display):
- Titles, tags, categories, project names
- Timestamps and metadata
Titles and tags remain unencrypted because they power FTS5 full-text search indexes. Content is searched separately via TF-IDF over decrypted values in memory.
Password Security¶
Passwords are hashed with bcrypt, which includes a per-password random salt automatically. We never store plaintext passwords.
If your account was created before bcrypt was enabled, your password is automatically upgraded to bcrypt the next time you log in.
API Key Storage¶
Your API key is shown once at registration. After that, only a SHA-256 hash is stored in the database. We cannot retrieve your original API key.
If you lose your key, use the "Regenerate API Key" button in Settings. This creates a new key and invalidates the old one. Regenerating a key also invalidates all active sessions for your account.
Session Management¶
- Dashboard sessions use cryptographically random tokens (44 characters via
secrets.token_urlsafe) - Sessions expire after 24 hours
- Sessions are stored in the database, not in cookies
- Logging out invalidates the session immediately
- Suspended accounts have all sessions revoked
- A background task runs every 30 minutes to clean up expired sessions
Transport Security¶
All traffic to syncontext.dev is encrypted with TLS via Cloudflare. HTTP requests are automatically redirected to HTTPS.
The MCP endpoint validates the Host header to prevent DNS rebinding attacks.
Security Headers¶
Every response from SynContext includes the following security headers:
| Header | Value | Purpose |
|---|---|---|
X-Frame-Options |
DENY |
Prevents clickjacking by blocking iframe embedding |
X-Content-Type-Options |
nosniff |
Prevents MIME-type sniffing |
X-XSS-Protection |
1; mode=block |
Enables browser XSS filtering |
Strict-Transport-Security |
max-age=63072000; includeSubDomains; preload |
Enforces HTTPS for two years (preload-eligible) |
Content-Security-Policy |
Restrictive policy | Limits script and resource origins |
Referrer-Policy |
strict-origin-when-cross-origin |
Controls referrer information leakage |
X-Request-ID |
Unique per request | Enables request tracing for debugging |
Rate Limiting¶
Authentication Endpoints¶
Login, registration, password reset, and key regeneration are protected by IP-based rate limiting: 5 attempts per minute. After 5 failed attempts, you must wait 60 seconds before trying again.
API Endpoints¶
API requests are rate-limited per user, with limits based on your subscription tier:
| Tier | Requests per Minute |
|---|---|
| Free | 30 |
| Pro | 120 |
| Team | 300 |
When you exceed your rate limit, the API returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait.
Tenant Isolation¶
Every request is associated with a user ID via Python's contextvars module. This context variable is set by the authentication middleware before any handler code runs. Every database query filters by the current user's ID, ensuring users cannot see, modify, or delete other users' data.
This approach provides isolation at the database query level without requiring changes to function signatures throughout the codebase.
SSRF Protection¶
Webhook URLs are validated before delivery to prevent Server-Side Request Forgery (SSRF) attacks:
- Only
httpandhttpsschemes are allowed - Private and reserved IP ranges are blocked (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16)
- Localhost and link-local addresses are rejected
- DNS resolution is performed on hostnames and ALL resolved IP addresses are checked against private ranges, blocking DNS rebinding attacks where a domain resolves to an internal IP
Input Validation and Size Limits¶
All API inputs are validated for type, format, and size:
- Maximum request body: 500 KB (returns
413 Content Too Largeif exceeded) - String fields are length-limited (titles, tags, content)
- IDs and enum values are validated against allowed formats
- HTML and script injection is prevented via input sanitization
Audit Logging¶
Every data operation is recorded in an audit log:
- Create, update, delete operations on projects, entries, and decisions
- User registration and login events
- Admin actions (tier changes, suspensions)
- API key regeneration
The audit log is available in the dashboard (Pro and Team plans).
Email Verification¶
New accounts must verify their email address before accessing the dashboard. A verification link is sent to your email upon registration and expires after 24 hours.
- MCP API key access is immediate — you can connect Claude and ChatGPT before verifying your email
- Dashboard login is blocked until verification is complete
- A "Resend verification email" option is available on the login page (rate limited to 3 per hour)
- Verification tokens are stored as SHA-256 hashes (same pattern as password reset tokens)
PostgreSQL Transactions¶
Critical multi-step operations run inside atomic PostgreSQL transactions to prevent partial updates:
- Password reset — mark token used + update password + delete sessions
- API key rotation — update key hash + delete sessions + audit log
- User deletion — cascade delete across all tables
- User suspension — update status + delete sessions + audit log
On SQLite (local dev), these operations use implicit single-writer atomicity with commit().
Tier-Gated Features¶
Some features are restricted to paid plans to prevent abuse:
- Semantic and hybrid search — Pro and Team only
- Webhooks — Pro and Team only
- Audit log — Pro and Team only
Free-tier users attempting to access these features receive a 403 response with a clear upgrade message.
Data Ownership¶
Your data belongs to you:
- Export anytime: Use the dashboard or
hub_export_projecttool to download all your data as JSON - Delete anytime: Delete individual entries and entire projects from the dashboard
- No lock-in: The export format is standard JSON that you can use anywhere
Data Retention Policy¶
- Active accounts: Data is retained indefinitely while your account is active.
- Deleted entries: When you delete an entry, project, or decision, it is permanently removed from the database. Deletion is not reversible.
- Expired sessions: Cleaned up automatically every 30 minutes by a background task.
- Audit logs: Retained for the lifetime of the account.
- Account deletion: See below.
Account Deletion¶
Account deletion is available through administrator support. To request account deletion, contact the admin or use the support email. Upon deletion, the following data is permanently removed:
- All projects, context entries, and decisions
- All version history
- All webhooks
- All audit log entries
- All active sessions
- Your user record, including hashed password and hashed API key
We recommend exporting your data before requesting account deletion.
Responsible Disclosure¶
If you discover a security vulnerability in SynContext, please report it responsibly:
Email: [email protected]
- Please include a description of the vulnerability and steps to reproduce it.
- We will acknowledge your report within 48 hours.
- We will not take legal action against researchers who follow responsible disclosure practices.
- We ask that you do not publicly disclose the vulnerability until we have had a reasonable opportunity to address it.
What We Don't Do¶
- We don't sell or share your data with third parties
- We don't use your data to train AI models
- We don't read your content (it's encrypted -- we can't even if we wanted to)
- Admin tooling is metadata-only — administrator endpoints can manage accounts and view aggregate statistics, but never return content bodies, decision text, or rationale. This is enforced structurally via explicit SQL column lists and allowlist-based response helpers (
_admin_safe_entry,_admin_safe_decision) - We don't store your API key in plaintext
- We don't log API keys in server access logs (they are sanitized)