Security
Tenants are kept apart
Section titled “Tenants are kept apart”- By key. A request acts for the tenant of its API key and no other. Asking for another tenant’s template, font or output gets the same answer as asking for one that exists nowhere, so nothing tells a tenant that another’s exists.
- By the database. Every tenant’s queries run under PostgreSQL row-level security, as the role
paprika_appwith the tenant set: the database shows and accepts that tenant’s rows only, whatever the query says. Work across tenants (looking up a key, the admin API, migrations, the sweep) runs as the user inPAPRIKA_DATABASE_URL. - By storage. Every file is kept under its tenant’s folder; tenants never share an upload. Only the fonts bundled with Paprika are shared.
- By key secrets. Only a hash of a key’s secret is kept. Keys can expire, are revoked at once, and are refused while their tenant is suspended or deleted.
What Paprika refuses
Section titled “What Paprika refuses”- Nothing is fetched from the internet. An image value names an image in the template or one of the tenant’s uploaded resources; Paprika never downloads a URL it is given, so it can’t be used to reach your internal network.
- Webhooks don’t reach private addresses. A webhook endpoint must be HTTPS on a public address: this host, private networks, link-local addresses (cloud metadata among them) and reserved ranges are refused when the endpoint is registered and again at every delivery, on the address its name resolves to then. For an install whose systems are all on a private network,
PAPRIKA_WEBHOOK_ALLOW_PRIVATE=trueallows private addresses and plain HTTP. - Unknown host names. Each service answers only the host names it is given (
PAPRIKA_HOSTS), and a browser request from another site that changes something is refused. - Oversized input. Packages, images, batches and queues have limits; fonts and images are checked before they are used.
Uploads from untrusted tenants
Section titled “Uploads from untrusted tenants”Uploaded fonts and images are parsed by the render process itself. Until it runs isolated from the rest of the service, accept uploads only from tenants you trust: in a self-hosted install, your own organisation.
Secrets
Section titled “Secrets”| Secret | What it guards | Rotating it |
|---|---|---|
PAPRIKA_ADMIN_TOKEN |
The admin API | List the new token next to the old, restart, then remove the old |
PAPRIKA_SIGNING_KEYS |
Signed download links to outputs | Put the new key first (it signs, every key listed verifies), later drop the old |
| The database password | Everything | Change it in PostgreSQL and in PAPRIKA_DATABASE_URL together |
| API keys | A tenant’s data, within the key’s scopes | Rotate: the old key works on for 24 hours |
| Webhook secrets | That a delivery came from Paprika | Rotated by the tenant; the old secret signs on for 24 hours |
.env.example holds development placeholders only: never run with them.
Keep off the internet
Section titled “Keep off the internet”- The admin API (
/v1/admin). Route it only from where you administer, or not through the proxy at all and reach it on the machine. - PostgreSQL and the data volume.
- The workers’ probes (port 4180).
The API (/v1, without /v1/admin) and the editor are meant to be reached, over HTTPS through your proxy.
The log has one line per API request: time, request id, method, path, status, duration, size and tenant, and an error’s code and message. It never holds a key’s secret or a file’s contents.