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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,34 @@ Versions follow [Semantic Versioning](https://semver.org/).

## [Unreleased]

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

The public pages were checked claim by claim against the code:

- **Terms §8** no longer quotes subscription prices (they come from the
pricing page) and describes cancellation by email.
- **Privacy policy:** the rate-limit description is accurate (set per route,
from 5 contact messages per hour to 120 requests per minute); the list of
stored data adds the preferred language, the email-verification timestamp
and per-request usage rows; sub-processor locations are corrected (Hetzner:
a data centre in the EU; Zoho: Amsterdam and Dublin); paid plans are not
yet available; the date is refreshed. Logging out now also removes the
legacy `filemorph_api_key` browser entry (`auth.js`), as §6 says — a test
in `tests/test_cookie_notice.py` pins it.
- **/pricing and /enterprise:** API access without a key, v1.1.0 as the
tagged release, the audit log's scope, the veraPDF gate, "180+ format
pairs" (distinct pairs, pinned by a test), support response times agreed
per contract, and a PGP key on request.
- **/formats** limits exact target-size compression to JPEG, WebP and AVIF.
- **Dashboard:** account deletion names the 10-year tax-retention record.
- **/security** and `/.well-known/security.txt` drop the GitHub Security
Advisories option, which is not enabled on this repository.
- The redaction page no longer calls its engine AGPL open source (`app/ee`
is commercially licensed); the JSON-LD `featureList` adds the PDF tools
and PDF → PDF/A.
- `tests/test_pricing_centralized.py` fails if the terms page hardcodes a
euro price in either language.

### Security — the Docker image is built without a restored build cache

`docker.yml` restored BuildKit's GitHub Actions cache (`cache-from: type=gha`)
Expand Down
4 changes: 0 additions & 4 deletions app/api/routes/seo.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,5 @@ async def security_txt() -> str:
f"Canonical: {base}/.well-known/security.txt",
f"Policy: {base}/security",
"",
"# Reports about FileMorph itself (the open-source software at",
"# https://github.com/MrChengLen/FileMorph) are also welcome via",
"# GitHub Security Advisories on the repository.",
"",
]
return "\n".join(lines)
2 changes: 2 additions & 0 deletions app/core/jsonld.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ def build_site_jsonld(app_base_url: str) -> tuple[str, str]:
"Convert audio (MP3, WAV, FLAC, OGG, M4A, AAC, WMA, Opus)",
"Convert video (MP4, MOV, AVI, MKV, WebM, FLV, WMV)",
"Compress images to an exact target size",
"Split, extract and compress PDF files",
"Convert PDF to PDF/A-2b",
"No account required",
"Self-hostable via Docker",
"REST API for programmatic conversion",
Expand Down
8 changes: 5 additions & 3 deletions app/core/redact_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
"Processed on EU servers with no third-party AI — detection is deterministic "
"(regex + checksums), not a language model. Your file is held in memory and "
"deleted right after processing; detected values are never logged or stored. The "
"redaction engine is open source (AGPLv3) and auditable."
"redaction engine is commercially licensed, not part of the AGPLv3 core; the "
"source is published for audit but is not open source."
),
"legal_notice": (
"Assists with PII removal — review before sharing. Detects emails, IBANs, phone "
Expand Down Expand Up @@ -170,8 +171,9 @@
"Verarbeitung auf EU-Servern ohne Dritt-KI — die Erkennung ist deterministisch "
"(Regex + Prüfziffern), kein Sprachmodell. Deine Datei liegt nur im "
"Arbeitsspeicher und wird direkt nach der Verarbeitung gelöscht; erkannte Werte "
"werden nie protokolliert oder gespeichert. Die Schwärzungs-Engine ist Open "
"Source (AGPLv3) und auditierbar."
"werden nie protokolliert oder gespeichert. Die Schwärzungs-Engine ist kommerziell "
"lizenziert, nicht Teil des AGPLv3-Kerns; der Quellcode ist zur Prüfung "
"einsehbar, aber nicht Open Source."
),
"legal_notice": (
"Unterstützt beim Entfernen von PII — vor Weitergabe prüfen. Erkennt E-Mails, "
Expand Down
2 changes: 2 additions & 0 deletions app/static/js/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
function logout() {
localStorage.removeItem(ACCESS_KEY);
localStorage.removeItem(REFRESH_KEY);
// Legacy key older versions stored; privacy.html §6 promises it goes on logout.
localStorage.removeItem('filemorph_api_key');
window.location.href = '/login';
}

Expand Down
2 changes: 1 addition & 1 deletion app/templates/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ <h2 class="font-semibold">{{ _('Email language') }}</h2>
<div class="space-y-3">
<div>
<h2 class="font-semibold text-red-400">{{ _('Danger zone') }}</h2>
<p class="text-xs text-ink-faint mt-0.5">{{ _('Permanently delete your account, API keys, and cancel any active subscription. Conversion-job records are anonymised. This cannot be undone.') }}</p>
<p class="text-xs text-ink-faint mt-0.5">{{ _('Permanently delete your account, API keys, and cancel any active subscription. Conversion-job records are anonymised. If you\'ve made a payment, we keep a minimal invoice-link record for 10 years (German tax law). This cannot be undone.') }}</p>
</div>
{{ ub.button(_('Delete account'), id='delete-account-btn', size='sm', variant='danger') }}
<div id="delete-confirm-form" class="hidden space-y-3 pt-2">
Expand Down
14 changes: 7 additions & 7 deletions app/templates/enterprise.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ <h1 class="text-3xl sm:text-4xl font-bold text-white leading-tight">
<div class="grid grid-cols-1 sm:grid-cols-3 gap-5 text-sm">
<div class="space-y-1">
<p class="text-white font-semibold">{{ _('SHA-256 audit log') }}</p>
<p class="text-gray-400 text-xs leading-relaxed">{{ _('Every operation in a continuous hash chain. Tampering with an old row breaks every following one.') }}</p>
<p class="text-gray-400 text-xs leading-relaxed">{{ _('Single-file format conversions and image/video compressions, plus account, contact, billing and redaction events, go into a continuous hash chain. Tampering with an old row breaks every following one.') }}</p>
</div>
<div class="space-y-1">
<p class="text-white font-semibold">{{ _('Signed image + SBOM') }}</p>
<p class="text-gray-400 text-xs leading-relaxed">{{ _('cosign keyless OIDC, cryptographically signed git tags, CycloneDX-JSON SBOM in every GitHub release.') }}</p>
</div>
<div class="space-y-1">
<p class="text-white font-semibold">{{ _('PDF/A-2b veraPDF-validated') }}</p>
<p class="text-gray-400 text-xs leading-relaxed">{{ _('Conformance gate runs as CI workflow. No release without green veraPDF against a worst-case source PDF.') }}</p>
<p class="text-gray-400 text-xs leading-relaxed">{{ _('Conformance check runs in CI on every push to main and every pull request, against a worst-case source PDF.') }}</p>
</div>
</div>
</section>
Expand Down Expand Up @@ -109,7 +109,7 @@ <h2 class="text-2xl font-bold text-white">{{ _('Why a Compliance Edition?') }}</
{{ _('Public authorities, hospitals and law firms often may not process citizen and client data through public cloud conversion services — the GDPR data chain and the EVB-IT contract framework set tight limits here. At the same time, the typical IT department lacks the bandwidth to maintain a conversion backend on its own.') }}
</p>
<p>
{{ _('FileMorph closes this gap: the open-source engine covers 16+ format pairs and runs on your own Hetzner / on-premises / air-gap infrastructure. The Compliance Edition additionally provides the contracts, SLAs and roadmap guarantees that an EVB-IT-compliant procurement requires.') }}
{{ _('FileMorph closes this gap: the open-source engine covers 180+ format pairs and runs on your own Hetzner / on-premises / air-gap infrastructure. The Compliance Edition additionally provides the contracts, SLAs and roadmap guarantees that an EVB-IT-compliant procurement requires.') }}
</p>
</section>

Expand Down Expand Up @@ -143,7 +143,7 @@ <h3 class="text-base font-semibold text-white tracking-tight">{{ _('Signed relea
</div>
<div class="bg-gray-900 border border-gray-800 rounded-xl p-6 space-y-3">
<h3 class="text-base font-semibold text-white tracking-tight">{{ _('Support SLA') }}</h3>
<p class="text-gray-400 leading-relaxed">{{ _('Response-time targets by severity — critical 4 h, high 24 h, medium/low in the next regular release. Targets apply Mon–Fri 09:00–18:00 CET, excluding German public holidays.') }}</p>
<p class="text-gray-400 leading-relaxed">{{ _('Response-time targets by severity are individually agreed in the support contract.') }}</p>
</div>
</div>
</section>
Expand Down Expand Up @@ -356,9 +356,9 @@ <h2 class="text-2xl font-bold text-white">{{ _('Ready for a 15-minute pilot call
</a>
</div>
<p class="text-xs text-gray-500 pt-4">
{{ _('Confidential inquiries via') }}
<a href="/.well-known/security.txt" class="text-brand hover:underline">security.txt</a>
{{ _('are encrypted with the same key.') }}
{{ _('Confidential inquiries: email') }}
<a href="mailto:security@filemorph.io" class="text-brand hover:underline">security@filemorph.io</a>
{{ _('— a PGP key is available on request.') }}
</p>
</section>

Expand Down
4 changes: 2 additions & 2 deletions app/templates/formats.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<a href="{{ localized_url('/', current_prefix) }}" class="text-sm text-gray-500 hover:text-gray-300 transition-colors">{{ _('← Back to converter') }}</a>
<h1 class="text-2xl font-bold mt-4 text-white">{{ _('Supported file formats') }}</h1>
<p class="text-gray-400 mt-2 max-w-2xl">
{{ _('FileMorph converts between the formats below and compresses images to an exact target size (video too, by quality). Everything runs for free, with no account — or self-host it under AGPLv3. Pick any pair and start on the') }}
{{ _('FileMorph converts between the formats below and compresses JPEG, WebP and AVIF images to an exact target size — PNG, TIFF and video use quality-based compression instead. Everything runs for free, with no account — or self-host it under AGPLv3. Pick any pair and start on the') }}
<a href="{{ localized_url('/', current_prefix) }}" class="text-brand hover:underline">{{ _('converter') }}</a>.
{{ _('Prefer operations over formats?') }}
<a href="{{ localized_url('/tools', current_prefix) }}" class="text-brand hover:underline">{{ _('See all tools') }}</a>.
Expand Down Expand Up @@ -54,7 +54,7 @@ <h3 class="text-xs font-semibold uppercase tracking-wider text-gray-200">
<section class="space-y-3">
<h2 class="text-base font-semibold text-white border-b border-gray-800 pb-2">{{ _('Compress to a target size') }}</h2>
<p class="text-gray-400 max-w-2xl">
{{ _('FileMorph can shrink a JPEG, WebP or AVIF image to an exact target size (for example 5 MB) using a binary search — useful for upload limits and email attachments.') }}
{{ _('FileMorph can shrink a JPEG, WebP or AVIF image to an exact target size (for example 5 MB) using a binary search — useful for upload limits and email attachments. PNG, TIFF and video below compress by quality only, not to an exact size.') }}
<a href="{{ localized_url('/compress', current_prefix) }}" class="text-brand hover:underline">{{ _('Try the compressor →') }}</a>
</p>
<div class="grid sm:grid-cols-2 gap-x-8 gap-y-4">
Expand Down
6 changes: 3 additions & 3 deletions app/templates/pricing.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% import "_components/card.html" as uc %}
{% block title %}{{ _('Pricing — free converter, paid plans') }} | FileMorph{% endblock %}
{% block meta_description %}{{ _('FileMorph pricing: the open-source converter is free to self-host. Paid plans add volume, higher limits and API access.') }}{% endblock %}
{% block meta_description %}{{ _('FileMorph pricing: the open-source converter is free to self-host. Paid plans add volume, higher limits and larger API quotas.') }}{% endblock %}
{% block content %}
<main class="flex-1 max-w-5xl mx-auto w-full px-4 sm:px-6 py-12 space-y-10">

Expand All @@ -20,7 +20,7 @@ <h1 class="text-h-page text-ink">{{ _('Free engine. Pay only for hosting or a li
{% endif %}

<p class="text-center text-xs text-gray-500">
{{ _('Using without an account:') }} {{ _('up to') }} {{ anon_plan.max_file_size_mb }} MB {{ _('per file') }} · {{ _('single file at a time') }} · {{ _('10 requests/minute') }} · {{ _('no API access') }}
{{ _('Using without an account:') }} {{ _('up to') }} {{ anon_plan.max_file_size_mb }} MB {{ _('per file') }} · {{ _('single file at a time') }} · {{ _('10 requests/minute') }} · {{ _('API access without a key') }}
</p>

<p class="text-center text-xs text-gray-500 max-w-3xl mx-auto">
Expand Down Expand Up @@ -278,7 +278,7 @@ <h2 class="text-center text-h-sect text-ink">{{ _('Compare plans') }}</h2>
<li class="flex items-start gap-2"><span class="text-emerald-400" aria-hidden="true">✓</span> {{ _('Tamper-evident audit log') }}</li>
</ul>
<p class="text-xs text-gray-500">
{{ _('Being transparent about what is not done yet: no external ISO 27001 certification and no external penetration test yet (both planned once a paying pilot justifies the cost), and no tagged release cut yet — the signing/SBOM pipeline is wired in CI and produces those artefacts on the first tag. Everything else above is in the repository today and auditable.') }}
{{ _('Being transparent about what is not done yet: no external ISO 27001 certification and no external penetration test yet (both planned once a paying pilot justifies the cost). v1.1.0, tagged 2026-06-01, is the latest signed release with a CycloneDX SBOM and an image digest; changes since then ship in the continuously built image ahead of the next tag. Everything else above is in the repository today and auditable.') }}
</p>
</div>
{% endcall %}
Expand Down
Loading
Loading