Security & Secrets
Notipo stores credentials for every connected WordPress site and Notion workspace. Treat the host like you would any other auth-server: minimal access, encrypted backups, rotated keys.
ENCRYPTION_KEY
Every WordPress application password and Notion integration token in the database is encrypted with AES-256-GCM using the key set in ENCRYPTION_KEY. This is the single most important secret in your stack.
ENCRYPTION_KEYdoes not re-encrypt existing data. After rotation, every stored credential becomes unreadable and users must reconnect WordPress and Notion. There's currently no built-in re-encrypt-on-rotate flow — if you need one, it's a small migration script you'd have to run manually.Generate it once with:
openssl rand -hex 32Store the value somewhere safer than just the .envfile on the host. A password manager, a private vault, or an encrypted file on a different machine all work. The DB backup alone is useless without it; that's by design.
Per-user API keys
Each user gets a personal API key issued at signup, used for the REST API, the CLI (NOTIPO_API_KEY), and the MCP server. Keys are scoped to that user's tenant — they cannot read or write data from other tenants.
The frontend stores a hash of the key in localStoragerather than the raw value, so a stolen browser session can't leak the API key directly. The API does not currently support rotating a user's key from the UI — if a key is compromised, the user must delete and recreate their account, or you can reissue manually via the database.
The admin API_KEY
The API_KEY environment variable gates the /api/admin/* routes — listing all tenants, creating tenants, retrieving any tenant's decrypted WordPress credentials, and impersonating any user via the X-Impersonate-Tenant header. Treat it as a root credential.
On a single-user instance you'll rarely need it. On a multi-tenant instance, never paste it into a frontend, never put it in URLs, and rotate it via .env + a stack restart if it leaks.
Rate limiting
The API has built-in rate limits via @fastify/rate-limiton auth and password-reset routes. They're sensible defaults but were tuned for the hosted product's traffic profile. If you're running a private instance, you can leave them; if you're running a public-signup instance, consider tightening them via a fork.
Hardening checklist
- Lock down signups after creating your account:
ALLOW_SIGNUP=falsein.env, thendocker compose up -d. - Restrict the host firewall to inbound 80 + 443 only. Postgres should never be reachable from the internet — the bundled compose stack only exposes it on the internal Docker network.
- Pin to a specific minor tag (
ghcr.io/kfuras/notipo-api:1.2) instead of:latestso a release on main can't silently swap your container on the nextdocker compose pull. - Back up Postgres + the encryption key separately. A single-source compromise should never have both.
- Subscribe to release notifications on GitHub so you see security patches as they ship. The release notes flag CVE bumps explicitly.
- Watch the security advisories page for the project.
Reporting a vulnerability
Don't open a public issue for security problems. Use GitHub's private security advisory flow at github.com/kfuras/notipo-app/security/advisories/new or email security@notipo.com. See SECURITY.md for the full policy.
Related
Continue reading