Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,41 @@ Versions follow [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Fixed — compliance templates describe what the code does

The DPA template and its TOM annex, the records-of-processing template, the
vendor security questionnaire, the sub-processor list, the support framework,
both GDPR documents, the AGPL guide for public bodies and the commercial
licence agreement template claimed more than the code does in places. They
now say that the audit log names actors by account ID (an email hash only for
failed logins, duplicate registrations, reset requests and contact-form
messages), stores its payload as JSON rather than a digest, and does not record
API-key management, admin changes, batch jobs or the `/pdf/*` tools; that
`X-Output-SHA256` comes only from single-file `/convert` and `/compress`; that
the upload check is a magic-byte deny-list and the converter is chosen by file
extension, not from content; that `app/ee/` holds only PII redaction, switched
on by `AI_OPERATIONS_ENABLED` rather than a licence key; that veraPDF runs in
CI, not per request; that a
leftover temp directory can last about 70 minutes, not 10; that uvicorn's
access log is on and a TLS-terminating edge proxy sees uploads; and that the
SMTP relay also carries contact-form messages but no receipts. Hosting and
email locations, the Python version, CSP, CORS, disclosure targets and code
anchors are updated, and vulnerability reports go to `security@filemorph.io`
only (PGP key on request); v1.1.0 is named as the only release so far, along
with what its SBOM lacks, and support response times as set per agreement. The
documents no longer name an `AUDIT_RETENTION_DAYS` setting, which never
existed: the audit log has no built-in retention period, and the operator
states theirs. Data-subject requests go to `privacy@filemorph.io`, as in the
privacy policy. The TOM annex and the questionnaire gain the rate limits and
failed-key budget, the two-job release workflow with its hash-pinned SBOM
generator and `.dockerignore`; the questionnaire also covers error messages
that no longer echo library internals, and the records of processing gain the
contact form. The account-deletion design and the questionnaire note that the
code takes the paid path for any account with a Stripe customer id; audit
events lose their actor ID only on a hard delete. In the agreement template,
no VAT is charged only while §19 UStG applies; it and the AGPL guide exclude
`app/ee/` from the AGPL.

### Fixed — website texts match what the service does

The public pages were checked claim by claim against the code:
Expand Down
21 changes: 15 additions & 6 deletions docs/agpl-fuer-behoerden.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# AGPLv3 für Behörden, Krankenhäuser und Kanzleien

FileMorph steht unter der **GNU Affero General Public License v3** (AGPLv3).
Diese Lizenz wird in Beschaffungsabteilungen gelegentlich als
Ausgenommen sind die Module unter `app/ee/` (PII-Schwärzung); sie sind
nur kommerziell lizenziert.
Die AGPLv3 wird in Beschaffungsabteilungen gelegentlich als
"problematisch" wahrgenommen, weil das Wort *Affero* den Eindruck einer
Veröffentlichungspflicht erweckt. Dieses Dokument räumt das auf und
erklärt, was die AGPLv3 für eine deutsche Verwaltungs-, Kranken- oder
Expand Down Expand Up @@ -104,12 +106,13 @@ Die Compliance-Edition (kommerzielle Lizenz) lohnt sich, wenn …
ausstatten, die unter eigener Lizenz bleiben sollen,
- Sie **vertraglich abgesicherte Support-SLAs** und einen festen
Ansprechpartner für sicherheitskritische Updates benötigen,
- Sie eine **Air-Gap- oder KRITIS-Variante** mit garantierten
Reaktionszeiten und Patch-Backports einsetzen wollen.
- Sie eine **Air-Gap- oder KRITIS-Variante** mit individuell
vereinbarten Reaktionszeiten und Patch-Backports einsetzen wollen.

Für die rein interne Verwaltungs- oder Klinik-Nutzung ist dagegen die
**AGPLv3-Edition kostenfrei und vollumfänglich nutzbar**. Die meisten
unserer Behörden-Deployments laufen unter AGPLv3.
**AGPLv3-Edition kostenfrei und vollumfänglich nutzbar**. Ausgenommen
sind die Module unter `app/ee/` (PII-Schwärzung); sie sind nur
kommerziell lizenziert.

## Was im EVB-IT-Vertragswerk zu beachten ist

Expand All @@ -122,7 +125,13 @@ Vertragsabschluss ein **Software Bill of Materials (SBOM)** verlangt
werden — ein fehlendes oder unvollständiges SBOM kann künftig einen
Mangel darstellen. FileMorph liefert dieses Artefakt im
CycloneDX-Format mit jedem Release als `filemorph-{version}.cdx.json`
(siehe [`patch-policy.md`](./patch-policy.md)).
(siehe [`patch-policy.md`](./patch-policy.md)). Es erfasst die
Python-Abhängigkeiten des Images, nicht dessen Betriebssystem-Pakete
wie FFmpeg oder Ghostscript. Das SBOM zum bisher einzigen Release,
v1.1.0 vom 1. Juni 2026, entstand noch mit dem früheren Verfahren: Es
führt zusätzlich die Pakete des SBOM-Generators auf, und Komponenten,
die ihre Lizenz nur als `License-Expression` angeben, stehen darin
ohne Lizenzangabe.

> **Wichtig für die Vertragswahl:** Die Reform betraf 8 der 11
> EVB-IT-Vertragstypen. **EVB-IT Cloud** und **EVB-IT Überlassung
Expand Down
33 changes: 22 additions & 11 deletions docs/commercial-license-agreement-template.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Commercial License Agreement — Template

**Status:** Skeleton template — **not a binding contract as it stands.**
**Last reviewed:** 2026-05-12
**Last reviewed:** 2026-09-28

This document is the starting point for the **Commercial License
Agreement** between a FileMorph Compliance-Edition customer (*Licensee*)
Expand Down Expand Up @@ -46,7 +46,10 @@ Read that first; this document is the contractual form of it.
`https://github.com/MrChengLen/FileMorph`, at the version line stated
in Schedule A, together with its Documentation.
- **"AGPL"** — the GNU Affero General Public License v3.0 under which the
Software is also published (see `LICENSE` in the repository).
Software, except the modules under `app/ee/`, is also published (see
`LICENSE` in the repository). The modules under `app/ee/` are
published source-available under the commercial licence only (see
`COMMERCIAL-LICENSE.md`).
- **"Licensed Scope"** — the deployment scope licensed under this
Agreement: the tier, number of servers, and employee band stated in
Schedule A.
Expand Down Expand Up @@ -78,7 +81,9 @@ Scope.
the band in Schedule A, or use by a different legal entity — AGPL-3.0
governs unless and until the Licensed Scope is extended by a written
amendment (a "true-up", typically a move to a higher tier per
[`COMMERCIAL-LICENSE.md`](../COMMERCIAL-LICENSE.md)).
[`COMMERCIAL-LICENSE.md`](../COMMERCIAL-LICENSE.md)). This does not
apply to the modules under `app/ee/`: they are not available under
AGPL-3.0, so they may be used only within the Licensed Scope.

3.3 This Agreement does not remove AGPL-3.0 from the public repository
and does not affect any other party's rights under AGPL-3.0. The
Expand Down Expand Up @@ -114,9 +119,11 @@ Licensee relies on it for the Licensee's own compliance.
invoiced annually in advance, due within thirty (30) days of the invoice
date.

6.2 Fees are exclusive of VAT. For cross-border supplies within the EU
to a VAT-registered business, the reverse-charge mechanism applies and
the Licensee provides a valid VAT-ID.
6.2 As long as the Licensor applies the small-business scheme under
§19 UStG (Kleinunternehmerregelung), no VAT is charged on the Fees. For
cross-border supplies within the EU to a VAT-registered business, the
reverse-charge mechanism applies and the Licensee provides a valid
VAT-ID.

6.3 Overdue amounts bear interest at the statutory rate (§288 BGB) from
the due date.
Expand Down Expand Up @@ -253,7 +260,8 @@ disclosure, is independently developed without use of the disclosing
party's information, or must be disclosed by law or court order (with
prior notice to the other party where lawful).

15.3 The Software itself is published under AGPL-3.0 and is not
15.3 The Software's source code is public — under AGPL-3.0, and
source-available for the modules under `app/ee/` — and is not
confidential.

15.4 This §15 survives termination for three (3) years.
Expand All @@ -262,9 +270,12 @@ confidential.

16.1 On termination or expiry of this Agreement, the licence in §3 (and
§4, if applicable) ends. The Licensee's continued use of the Software is
thereafter governed by AGPL-3.0.
thereafter governed by AGPL-3.0, except for the modules under `app/ee/`:
they are not available under AGPL-3.0, so the right to use them ends
with this Agreement.

16.2 Existing installations may continue to run; the Licensor does not
16.2 Existing installations may continue to run, with the modules under
`app/ee/` disabled (§16.1); the Licensor does not
disable or force-update deployed instances. Continued updates after
termination require a current commercial licence or compliance with
AGPL-3.0; the Documentation as a contractual deliverable, the Support
Expand Down Expand Up @@ -314,7 +325,7 @@ Germany, to the extent permitted by law.
| Tier | `[Starter / Standard / Enterprise / KRITIS–air-gap / OEM]` |
| Number of servers licensed | `[N]` |
| Employee band | `[≤ 50 / ≤ 2 000 / unlimited / as agreed]` |
| Annual Fee (excl. VAT) | `[€ … — per COMMERCIAL-LICENSE.md, or as negotiated for Enterprise / KRITIS / OEM]` |
| Annual Fee (no VAT charged while the Licensor applies §19 UStG — see §6.2) | `[€ … — per COMMERCIAL-LICENSE.md, or as negotiated for Enterprise / KRITIS / OEM]` |
| Multi-year discount, if any | `[…]` |
| OEM redistribution terms (OEM tier only) | `[…]` |
| Licensed version line | `[e.g. v1.x — see docs/patch-policy.md]` |
Expand All @@ -327,7 +338,7 @@ filled at finalisation:

| Item | Value |
|---|---|
| Severity response windows (P1 / P2 / P3 / P4) | `[per the tier — see docs/support-sla.md]` |
| Severity response windows (P1 / P2 / P3 / P4) | `[as agreed for this Agreement — docs/support-sla.md gives the framework, not figures]` |
| Coverage hours | `[business hours Europe/Berlin / extended / 24×7 — as agreed]` |
| Named support contact | `[…]` |
| Escalation path | `[…]` |
Expand Down
41 changes: 28 additions & 13 deletions docs/dpa-template.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Data Processing Agreement (DPA) — Template

**Status:** Skeleton template, finalised individually in pilot conversations.
**Last reviewed:** 2026-05-08
**Last reviewed:** 2026-09-28

This document is the starting point for a Data Processing Agreement (DPA)
under Article 28 GDPR between a FileMorph Compliance-Edition customer
Expand Down Expand Up @@ -58,8 +58,11 @@ users. Processing operations include:
- Returning the converted output and a SHA-256 integrity header
- Writing structured logs (no file content; only metadata: tier, format
pair, byte counts, duration, success flag)
- Recording audit events for actions affecting accounts or entitlements
(registration, login, key creation, deletion, billing changes)
- Recording audit events for account and billing actions (registration,
login, email verification, password reset, account deletion,
subscription changes) and for single-file conversions and
compressions — not for API-key management, admin changes in the
cockpit, batch jobs, or the `/pdf/*` tools

The Service does **not** perform any analytics, profiling, advertising,
or data sale.
Expand All @@ -78,8 +81,10 @@ or data sale.
identifiers
- File contents during processing — deleted from memory and disk
immediately after the converted output is returned (typical
retention: seconds; absolute upper bound: 10 minutes via startup
sweep, see `app/main.py`)
retention: seconds). A temp directory left behind, e.g. by a crashed
worker, is removed by the startup sweep or the hourly background
sweep once it is older than 10 minutes — with the default settings
within about 70 minutes (see `app/main.py`)
- Audit-event records (see §5 below) — retained per the controller's
configured retention policy

Expand All @@ -89,19 +94,27 @@ Every Compliance-Edition deployment writes a tamper-evident audit log
(SHA-256 hash chain, see `app/core/audit.py` and Migration 005). Each
entry contains:

- Event type, timestamp, actor identifier, actor IP, payload digest
- Event type, timestamp, actor identifier (the account ID, where there
is one), actor IP, and the event payload as canonical JSON (operation
metadata such as format pair, byte counts and output SHA-256; a
SHA-256 hash of the email address for events such as a failed login;
never file content)
- Hash of the previous event (chain integrity)

The audit log is a tamper-evident record of processing *operations* on
the controller's behalf — useful evidence for, but distinct from, the
controller's Article 30 *Verzeichnis von Verarbeitungstätigkeiten*
(Records of Processing Activities), for which see
[`docs/records-of-processing-template.md`](records-of-processing-template.md).
The audit-log retention period defaults to `[RETENTION DAYS]` and is
configurable via the `AUDIT_RETENTION_DAYS` environment variable.
The application has no built-in retention period for the audit log: rows
are append-only and are not pruned automatically. Pruning them takes a
privileged database role that bypasses the append-only trigger; the
retention period and procedure are `[RETENTION PERIOD + PROCEDURE]`.

Each converted output carries an `X-Output-SHA256` response header so
the controller can independently verify integrity.
Each output of the single-file `/convert` and `/compress` endpoints
carries an `X-Output-SHA256` response header so the controller can
independently verify integrity; batch ZIPs and the `/pdf/*` tools do
not.

## 6. Sub-processors

Expand All @@ -128,8 +141,9 @@ The processor implements the measures documented in:
- [`docs/release-signing.md`](release-signing.md)

These cover: encryption in transit (TLS 1.2+, HSTS), at-rest scope (no
persistent file storage by design), access control (timing-safe API key
validation, JWT-bound roles, admin role with database recheck per
persistent file storage by design), access control (hashed API keys,
compared in constant time for the key file and looked up by hash for
per-user keys; JWT-bound roles; admin role with database recheck per
request), key management, software-supply-chain hardening (cosign-signed
images, signed Git tags, CycloneDX SBOM), and incident-response
timelines — structured along the Article 32 GDPR categories
Expand Down Expand Up @@ -202,7 +216,8 @@ Place of jurisdiction is Hamburg, Germany.

1. Review the bracketed placeholders in §1 and §2 and fill them with
the deployment context.
2. Replace `[RETENTION DAYS]` in §5 with the configured value.
2. Replace `[RETENTION PERIOD + PROCEDURE]` in §5 with the agreed
audit-log retention period and the pruning procedure.
3. Start from the [`docs/dpa-tom-annex.md`](dpa-tom-annex.md) template,
fill its `[operator: …]` placeholders with the measures specific to
the deployment (instance location, network segmentation, on-call,
Expand Down
Loading
Loading