EU Directive 2019/1937 & HinSchG
Free, open source
whistleblowing
software
OpenWhistle is free, open source (GPL-3.0), self-hosted whistleblowing software. It is the internal reporting channel that EU Directive 2019/1937 and Germany's HinSchG require, run on your own server.
A reporting channel you run yourself
| Question | OpenWhistle |
|---|---|
| Price | Free. No paid tier, no subscription; you pay for your own server. |
| Licence | GPL-3.0, source on GitHub |
| Hosting | Self-hosted: Docker Compose or a Helm chart. No SaaS. |
| Stack | Python, FastAPI, PostgreSQL 18, Redis 8, nginx |
| Images | GHCR, Docker Hub, Quay.io; linux/amd64 and linux/arm64 |
| For | Companies and public bodies under EU Directive 2019/1937 and HinSchG |
| Organisations | One or many per installation, each with its own reporting link |
| UI languages | English, German, French, Brazilian Portuguese |
| Tor | Optional onion address, with a Tor daemon you run |
| Current release | 2.1.1 · Changelog |
Other open source options exist. Compared with GlobaLeaks, SecureDrop, Hush Line: licence, hosting, deadlines and languages, each with its source.
What each side gets
| Access | Case number and a UUID4 PIN, shown once. No account, no email address. |
| Mode | Anonymous, or confidential: name and contact encrypted, shown to the case handler only after an audited reason |
| Follow-up | Read replies and send messages at /status with case number and PIN |
| Lock-out | None: the correct PIN always works; wrong guesses only slow down |
| Timestamps | The day only (UTC) for the report, messages and files |
| Files | PDF, images, DOCX, XLSX, CSV, TXT; 5 × 10 MB; metadata removed; optional ClamAV scan that refuses when clamd is down |
| Deadlines | 7-day acknowledgement and 3-month feedback, per case, on dashboard and case page |
| Case work | Statuses, assignment, internal notes, linked cases, PDF export |
| Search | Case number or a word in the report; decrypted in memory, no index stored |
| Roles | Superadmin, admin, case manager (sees assigned cases only) |
| Audit | Immutable audit log with CSV export; deletion needs a second admin |
| Alerts | Opt-in email and webhook digests and deadline reminders; webhooks carry counts, never case numbers |
| Sign-in | Password, LDAP or OIDC, then always TOTP. Enrolment is mandatory. |
| Organisations | Optional multi-tenancy: each organisation gets /submit/<slug> |
| TLS | On by default: self-signed on first boot, your certificate once you add it |
| Outbound | Nothing until you switch it on. Update check and installation count are off by default. |
| Retention | Closed reports deleted after 1,095 days (HinSchG §11 Abs. 5), on by default |
| Images | Signed with Cosign on GHCR; also on Docker Hub and Quay.io |
From report to closed case
No IP address is stored, by design
| Layer | What it does |
|---|---|
| 1. nginx | Clears X-Forwarded-For and six other client-IP headers; writes no access log |
| 2. Middleware | Drops those headers and the peer address before any route runs |
| 3. Database | No table has an IP column |
| 4. Redis | A status session is a random 256-bit token holding only the report id; 2 hours |
Rate limits count wrong PINs per case number, not per address. nginx limits requests per client address in memory only, never logged. Details: Security Architecture.
What the law asks, and where it is in the software
| Requirement | Source | In OpenWhistle |
|---|---|---|
| An internal reporting channel from 50 employees | §12 HinSchG; Art. 8 Directive | The reporting link, one per organisation |
| Acknowledge receipt within 7 days | §17 Abs. 1 Nr. 1 | Day N/7 column; warning from day 5 |
| Keep in contact with the reporter | §17 Abs. 1 Nr. 3 | Message thread, opened with case number and PIN |
| Feedback within 3 months of acknowledgement | §17 Abs. 2 | Days-left column; opt-in reminders |
| Keep the reporter's identity confidential | §8 | Anonymous mode; confidential identity behind an audited reason |
| Access only for the responsible staff | §16 Abs. 2 | Roles; case managers see their own cases |
| Document every report | §11 | Immutable audit log |
| Delete the documentation 3 years after the procedure | §11 Abs. 5 | RETENTION_DAYS=1095, on by default |
| Accept oral reports too | §16 Abs. 3 | Not a software channel; /admin/telephone-channel is a guide for one |
| Collect only necessary data | Art. 5(1)(c) GDPR | No IP address, no fingerprint, day-only timestamps |
The Directive covers private employers from 50 workers and financial services at any size. The public sector is covered too; states may exempt municipalities under 10,000 residents. Software does not make an organisation compliant: it still needs an impartial reporting office.
Self-hosted with Docker Compose
One docker compose up -d starts the app, PostgreSQL, Redis and nginx. Migrations run at startup.
The /setup wizard asks for the one-time setup token from the log. It then creates the first account, a superadmin, and enrols its TOTP.
- Docker 24 and Docker Compose v2
- 1 vCPU, 512 MB RAM (1 GB recommended), 5 GB disk
- A domain, an HTTPS certificate, ports 80 and 443
- PostgreSQL 18 and Redis 8: bundled
$ cd OpenWhistle && cp .env.example .env
# .env: COMPOSE_FILE=docker-compose.prod.yml,
# SECRET_KEY, DATABASE_URL, REDIS_URL
$ nano .env
$ docker compose up -d
► Container openwhistle-db-1 Started
► Container openwhistle-redis-1 Started
► Container openwhistle-app-1 Started
► Container openwhistle-nginx-1 Started
# One-time token for the setup wizard
$ docker compose logs app | grep "Setup token"
# Then open https://localhost/setup