At a glance
-
Same protection on every plan
Every plan gets the same encryption and isolation. Higher plans add control, not safety.
-
Encrypted at rest
PostgreSQL volumes, R2 objects and backups are encrypted at rest by the providers. Report payloads, HTML, PDFs and Markdown versions are additionally encrypted with a per-workspace data key before they are written to object storage; deleting a workspace destroys the key.
-
Encrypted in transit
TLS 1.2 or higher on every public endpoint; HSTS; internal traffic between the application and the render plane stays on Railway's private network and is authenticated with an HMAC.
-
EU hosting
Application, database, workers and render plane run in Railway's EU West region (Amsterdam); payloads, HTML, PDFs and Markdown versions are stored in Cloudflare R2 with the EU jurisdiction restriction. Region is a property of your workspace; only the EU region is live today.
-
Row-level security
Every tenant table carries a workspace id and PostgreSQL row-level security is forced on it. The web service connects as a role that cannot bypass it, and the whole test suite runs a second time as that role.
-
Immutable template versions
Publishing creates an append-only version pinned to a kit content hash; database triggers refuse updates to published versions. Reports bind to a version, and HTML and PDF are frozen at render with their hashes stored on the report.
-
No customer code executes
A template is JSON validated against a schema. The render plane interprets it with a fixed widget kit; Chromium runs with JavaScript off and every network request blocked. The render plane has no database, no bucket and no secrets other than the HMAC key.
-
A DPA for every customer
The processing description, technical and organisational measures and the subprocessor list are annexes. We commit to notifying you of a personal data breach within 48 hours of becoming aware of it.
-
Signed links and private reports on Pro
Signed URLs grant time-limited access without a shareable link; private reports open only for signed-in members of the workspace. Zero-retention mode, on Pro, deletes each report a set time after it is ready.
-
Every PDF can be checked
From Standard, the fingerprint of every live PDF is recorded when it is made. Anyone can check a copy on the verify page: the file is hashed in their browser and never uploaded. Records hold no report content and are kept 10 years.
-
No HIPAA BAA yet
Do not send protected health information under US law to Pressfox. GDPR health data (Art. 9) may be processed under the DPA when your own lawful basis covers it.
1. Architecture in one paragraph
Three deployables. A Django control plane owns accounts, workspaces, templates, reports, billing and the public API. A private Node render plane receives a validated document, a payload and a theme, renders HTML, prints it to PDF with Chromium, and returns both; it holds nothing and reaches nothing. A static React app is the dashboard and designer. Every tenant table carries a workspace id and PostgreSQL row-level security backs the application's own scoping, so a bug in one query cannot leak another workspace's rows.
2. Infrastructure and providers
The application, the PostgreSQL database, the background workers and the render plane run on Railway in its EU West region (Amsterdam). Cloudflare provides DNS, TLS, the CDN, custom hostnames for reports on your own domain, and R2 object storage with the EU jurisdiction restriction. Paddle handles payments as merchant of record. Resend sends email from its EU sending region (Ireland). Sentry receives error reports in its EU region (Frankfurt), with payload content scrubbed. Anthropic processes text for the optional AI features. Google is used only when you choose to sign in with Google. Production runs separately from staging.
The subprocessors page is the single, authoritative list: what each provider does, where, under which transfer mechanism, and the notice we give before adding one.
3. Encryption and keys
- Report payloads, HTML, PDFs and Markdown versions are encrypted with a per-workspace data key before they are written to object storage. Deleting a workspace destroys its key.
- TLS 1.2 or higher on every public endpoint, with HSTS. The application and the render plane talk over Railway's private network, and every request between them is authenticated with an HMAC.
- API keys are per workspace, prefixed
pf_live_orpf_test_, shown once, stored hashed and revocable at any time. Test keys produce watermarked, unmetered reports. - Report passwords are stored hashed, never in clear text.
- PDF passwords are used once, to encrypt the file, and deleted with the payload when the report is ready; we cannot recover one. The permissions password is random for each report and thrown away, so nobody can change a PDF's permissions later.
- Public identifiers only: internal keys never leave the database. Report links use a separate random token that is not derived from the report id.
4. Workspace isolation and roles
- Every tenant table carries a workspace id, and PostgreSQL row-level security is forced on it, so the database itself refuses rows from another workspace.
- The web service connects to the database as a role that cannot bypass row-level security.
- In continuous integration the full test suite runs a second time as that role, so a query that only works around the policies fails the build.
- Four roles (owner, admin, editor, viewer) map to one permission matrix; every API endpoint is checked by the same mechanism.
5. Sign-in and sessions
- No passwords. You sign in with a one-time code sent by email, or with Google. Three wrong attempts spend the code, and you ask for a new one.
- A new sign-up stays pending until its code is confirmed, so nobody can create an account with an address they do not control.
- Sign-in and code requests are throttled.
- Sessions are server-side. A session ends after 7 days without use and after 30 days at most.
- Your account lists your active sessions, so you can sign out other devices.
- We email you when your account signs in from a new device.
6. The render plane
No customer code runs. A template is JSON validated against a schema, interpreted by the render plane with a fixed widget kit. Chromium prints it with JavaScript off and every network request blocked. Each render has deadlines, so a stuck job is stopped rather than left running. The render plane is reachable only on the private network: it has no public address, no database, no bucket and no secrets other than the HMAC key.
7. Data you send
- Every payload is validated against the template's contract before anything is rendered; a payload that does not match is refused.
- What a report is made from, the payload and the images fetched from your URLs, is deleted as soon as the report is ready, or 24 hours after a failed render.
- Images are fetched from your URLs behind a guard against server-side request forgery: the address is checked, and the connection is pinned to the address that was checked.
- Photo metadata, including GPS location, is stripped from the print renditions of your images.
- The Markdown version of a report is built by code from the report itself, not by AI.
- A PDF's properties carry its title, the issuer's name and public ids only: never other data you sent, and never the names or versions of the tools that made it.
8. Report access
Per report, at creation: open link (unguessable token), password, expiry date, and on Pro signed URLs and private reports. From Standard, the PDF itself can also open only with a password: AES-256, set per report or taken from a field of the report's data; readers can still print, copy and use a screen reader. A password gate locks out after repeated wrong attempts. QR codes are signed per report. Hosted report pages set no tracking cookies; the only cookie is the password gate's, scoped to the report's hostname. Per-workspace branding of the error, expired and password pages is not finished yet; today they carry the Pressfox name.
9. Verifying a document
- From Standard, every live report's PDF gets a verification record when it is ready: its SHA-256 fingerprint, the issuer's name and the time of issue, and later whether a correction replaced it or the issuer revoked it. Test reports and zero-retention reports get none.
- Anyone holding the PDF can check it on the verify page, at app.pressfox.io/verify or at /verify on your own report address. The browser computes the fingerprint; the file is never uploaded. The answer is genuine (who issued it and when), replaced, revoked with the issuer's reason, or not found.
- Once your workspace has its own report address, each PDF prints a line saying where to check it: on that address, never ours. There, the page answers only for your workspace's documents.
- Not found means the file was not issued by Pressfox, or was changed after it was issued: annotating or saving a PDF again changes its fingerprint. A copy signed through your signing provider is a new file; check the copy Pressfox issued.
- A record holds no title, payload or labels, so it outlives the report: it is kept 10 years and deleted with the workspace. Checks are rate limited at the edge and per address, with no captcha and no cookie. Verification proves a file is unchanged since it was issued; it is not an electronic signature or seal.
10. Webhooks and the API
- Every webhook delivery is signed following the Standard Webhooks specification, so your endpoint can check that it came from us and was not changed; the signed timestamp lets you refuse old deliveries.
- Failed deliveries are retried on a ladder from 1 minute to 24 hours. An endpoint that fails for 3 days without a single success is disabled automatically; the app shows when and why next to it, and the audit log records it. Once the receiver is fixed, an owner or admin enables the endpoint again with one click, keeping its URL and signing secret. That clears the failure count, starts a fresh 3 days, and is recorded in the audit log; events missed while it was disabled can be redelivered from its deliveries.
- Webhook endpoints pass the same guard against server-side request forgery as image fetching, pinned to the checked address, so a webhook cannot be pointed at internal addresses.
- The API accepts idempotency keys, so a request retried after a timeout does not create a second report.
- The API is rate limited per API key and per workspace, in one-minute windows: 600 requests a minute per key, of which 120 may create reports, and 1,200 requests (240 report creations) a minute across all of a workspace's keys, test and live together. A request over a limit is refused with HTTP 429 and a
Retry-Afterheader; every counted response carriesRateLimit-Limit,RateLimit-RemainingandRateLimit-Reset. See rate_limited. - Sign-in, the AI assistant, the Playground, template imports and the password gate on hosted reports are rate limited as well.
11. AI features and AI agents
Pressfox's AI features are optional and work on text only, never images: drafting a template's translations, translating the report text fields you mark, summaries in reports, the designer assistant, and building a draft template from a PDF you upload (its text only; the PDF itself is deleted within 24 hours). A rendered report never calls a model: template translations are reviewed and frozen when you publish, and AI text that goes into a report is written once, before printing, and deleted with the report's other inputs when the report is ready. Only the marked fields of one report are sent; text is passed as data, never as instructions, and checked before use (placeholders, formatting and numbers must survive). This text is processed by Anthropic in the United States, under its DPA and the Standard Contractual Clauses, and is not used to train models; reports, payloads and PDFs stay in the EU. AI features are off for health workspaces.
AI agents can connect to Pressfox over MCP. You approve each one on an OAuth consent screen; a connected agent can find templates and create, read and share reports (it cannot delete reports, change templates or touch members, keys or billing), and you can disconnect it at any time. MCP is in preview: we turn it on for a workspace on request.
12. Staff access
There is no admin panel in production; operations run through the hosting provider's authenticated console and audited management commands. Today only the founder has production access.
13. Payments
Paddle is our merchant of record: it runs checkout, invoicing, tax and refunds. Card details go to Paddle; we never receive card numbers.
14. Logging, monitoring and audit
- Logs carry request ids, never payload content. An automated log-redaction test in the build proves it.
- Errors go to Sentry in the EU (Frankfurt), with payload content scrubbed.
- An append-only audit log records sign-ins, role changes, API key creation and revocation, template publishes and deletions. It is kept 1 year on every plan and 2 years on Pro and Enterprise, where owners and admins can also download it as CSV or NDJSON.
15. Backups and recovery
The database has point-in-time recovery: we can restore it to any minute in the last 4 weeks. Backups are encrypted at rest. We will rehearse a restore every quarter, the first before general availability. The target is to be back within 4 hours and to lose at most 15 minutes of changes; we publish measured times once a drill records them. Deletions and revocations (workspaces, reports, files, API keys, report links, shares, invitations and members) are recorded outside the database, and our restore procedure re-applies those recorded since the restore point before we reopen the service.
16. Retention and deletion
On paid plans, hosted reports, their PDFs and their links are kept while the subscription is active, and 30 days after it ends, then deleted. On Free they are kept 7 days; on Enterprise, as the contract says. An owner can set a shorter time for the workspace, and an integration for one report. Each report keeps the end it was created with: moving between paid plans never shortens it. Report storage has a limit per plan; at the limit new reports are refused, and nothing is deleted to make room. Test reports are kept 24 hours on every plan. What a report was made from goes sooner: its payload and the images fetched from your URLs are deleted as soon as it is ready, and 24 hours after a failed render. Deletion of a report or a workspace reaches the database, object storage, the CDN cache and the render plane's temporary files. A deleted workspace can be restored for 30 days; then we delete its data and destroy its encryption key. Database backups can still hold that key for at most 35 more days; the system is designed so that once they have aged out, the data cannot be decrypted, by us or anyone. Workspace export produces a machine-readable archive of templates, versions, brand profiles, reports and metadata.
17. Zero-retention mode
On Pro and Enterprise an owner can turn on zero-retention mode for the workspace, and an integration can ask for it on one report. A report made this way is deleted a set time after it is ready: between 5 minutes and 24 hours, 1 hour unless you choose otherwise.
The data you send is deleted as soon as the report is ready, and at once if the render fails. The pages and the PDF are kept in a separate store that deletes everything within two days, even if our own deletion job fails.
Collect the report inside that window: by webhook and then a download with your API key, or with a signed link. After it, the API answers that the window has ended. Deleting the report through the API ends the window early.
Features that keep data or send it elsewhere are off in this mode: open links (reports use signed links), sharing by email, AI translation and summaries, and forms left partly filled. The report's title comes from your request, or from the template's name and the date, never from the data.
If the workspace moves to a plan without zero-retention, the mode stays on until an owner turns it off. It limits what we keep; it does not change our role: while we render a report we still process its data, as your processor under the DPA.
18. Incident response
When something goes wrong, we work through five steps:
- Contain the problem and keep the evidence.
- Find the scope: which workspaces, which data, since when.
- Rotate the keys and secrets that may be affected.
- Notify affected customers within 48 hours of becoming aware of a personal data breach, as the DPA commits, and the authorities where the law requires it.
- Fix the cause and write it up.
19. Your part
Security is shared. On your side:
- Keep API keys in a secret store, never in code or in the browser, and use test keys in development.
- Pick the access policy that fits each report: a password, an expiry date, or signed URLs for anything sensitive.
- Pin template versions in production, so a publish never changes a report your systems send.
- Verify the signature on every webhook before you trust it.
- Remove members who leave your team, and rotate any API key they could see.
- Do not send US protected health information: we do not sign a HIPAA business associate agreement.
20. Responsible disclosure
If you find a vulnerability, tell us. Write to hello@pressfox.io with the subject "Security report", with enough detail for us to reproduce it. We acknowledge within 2 business days. There is no paid bounty; if you want, we thank you by name on this page.
In scope: pressfox.io, app.pressfox.io, api.pressfox.io, reports.pressfox.io, *.pressfox.app, the API and the MCP server.
Out of scope: Paddle, Cloudflare and Google themselves (report to them); denial of service; spam; social engineering; physical attacks; and other customers' data.
Rules: test only in your own workspace. If you reach data that belongs to someone else, stop, do not keep it, and report it to us.
Safe harbour: we do not pursue or report researchers who act in good faith under these rules. Our contact details are also published at /.well-known/security.txt (RFC 9116).
21. Certifications
None yet. Pressfox has no SOC 2 report, no ISO 27001 certificate and no HIPAA business associate agreement. Our providers hold certifications of their own; those are theirs, not ours, and we do not claim them.
22. Not yet, and said plainly
- No second factor for sign-in yet.
- No CAPTCHA on public forms; throttles and pending sign-ups handle abuse today.
- No Content-Security-Policy header yet.
- No external penetration test completed yet; one is planned before general availability and we will publish the summary.
- No public status page yet.
- No SOC 2 report and no ISO 27001 certificate.
- No dedicated storage, no customer-managed encryption keys and no storage in your own bucket yet. They are Enterprise options we discuss on request.
- No region other than the EU. Other regions come when a customer needs one, with per-region storage.
- No HIPAA business associate agreement and no dedicated health region.
- No single sign-on (SAML) yet; email code and Google only.
- EU-located, not EU-sovereign: our hosting providers are US companies with global control planes, bound by Standard Contractual Clauses.
23. Version history
| Date | Change |
|---|---|
| 16 October 2026 | Added: document verification, a verify page and a verify line on PDFs, and the section that explains it. |
| 16 October 2026 | Added: a password on the PDF itself, on Standard and up. A PDF's properties carry its title, the issuer and public ids only, and from Standard do not name Pressfox. |
| 13 October 2026 | Added: the same protection on every plan, said first; zero-retention mode on Pro and Enterprise (no longer listed under “Not yet”); the audit log download and two years of audit history on Pro and Enterprise. Dedicated storage, customer-managed keys and your own bucket are listed as Enterprise options on request. |
| 13 October 2026 | Backups and recovery corrected: point-in-time recovery for about four weeks replaces the earlier description of daily backups; the deletion timeline now states how long backups hold a deleted workspace's key; a restore re-applies every deletion and revocation first. |
| 10 October 2026 | The API now has request rate limits per API key and per workspace (no longer listed under “Not yet”); a webhook endpoint disabled after 3 days of failures can be enabled again in the app instead of being created again. |
| 10 October 2026 | Rewritten: numbered sections; new sections on infrastructure, workspace isolation, sign-in and sessions, the render plane, data you send, webhooks and the API, staff access, payments, backups, your part and responsible disclosure; the provider table replaced by a link to the subprocessors page; backups kept 7 days. |