Layer-2-webContent-Filter laeuft jetzt automatisch bei activateFamilyControls/ activate mit (Helper applyWebContentLayer). URLFilterExtension CFBundleVersion/ ShortVersion an die App angeglichen. Apple-DTS-Report einreichfertig. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PIR-Server — iOS NEURLFilter Backend
Status: LIVE (Staging) — https://pir.staging.rebreak.org · Stand 2026-05-21
Dieses Dokument beschreibt den PIR-Server vollständig, damit eine andere Session ihn verstehen, betreiben und weiterentwickeln kann.
1. Was & Warum
Rebreaks iOS-Gambling-Blocker nutzt Apples NEURLFilter (iOS 26). NEURLFilter
filtert URLs systemweit (Safari + alle WebKit-Browser + URLSession) und
verlangt dafür zwingend einen vom Vendor gehosteten PIR-Server (Private
Information Retrieval) — NEURLFilterManager.setConfiguration() akzeptiert keine
Konfiguration ohne pirServerURL. Dieser Server ist dieser PIR-Server.
Ablauf zur Laufzeit:
- Das Gerät prüft die URL gegen einen lokalen Bloom-Filter (Prefilter, in der
iOS-App-Extension
NEURLFilterControlProvider). - Bei einem Bloom-Treffer → PIR-Lookup gegen diesen Server, geroutet über Apples Oblivious-HTTP-Relay.
- Per Homomorphic Encryption sieht der Server die geprüfte URL nie im Klartext.
DSGVO/DiGA-Vorteil: Kein Beteiligter sieht URL + Identität zusammen — Rebreak erfährt das Surfverhalten der User nie. Datenminimierung per Krypto-Konstruktion.
2. Architektur-Bausteine
Basiert auf Apples Open-Source-Vorlagen:
apple/pir-service-example→PIRService(Swift/Hummingbird-HTTP-Server, bedient PIR-Queries + eingebauten PrivacyPass-Issuer) +ConstructDatabase.apple/swift-homomorphic-encryption→PIRProcessDatabase(wandeltinput.txtpbin PIR-optimierte Shards).
3. Deployment-Stand (Hetzner rebreak-server, 49.13.55.22)
| Komponente | Ort / Wert |
|---|---|
| Docker-Container | pir-service-staging, restart=unless-stopped, 127.0.0.1:8090->8090 |
| Docker-Image | pir-service-staging:latest (Multi-Stage Swift-Build, ~5 GB) |
| Build-Repos | /srv/pir-build/{pir-service-example,swift-homomorphic-encryption} + Dockerfile |
| Deploy-Verzeichnis | /srv/pir-server/ (build-and-deploy.sh, gen-test-input.sh, config/, data/) |
| PIR-DB-Artifacts | /srv/pir-server/data/ — input.txtpb + url-0..3.bin + url-0..3.params.txtpb (4 Shards) |
| Service-Config | /srv/pir-server/config/service-config.json (Token inline — NICHT im Repo) |
| nginx | /etc/nginx/sites-{available,enabled}/pir-staging.rebreak.org, server_name pir.staging.rebreak.org |
| TLS | Let's Encrypt für pir.staging.rebreak.org, läuft 2026-08-18 ab, Auto-Renew aktiv |
| Public-URL | https://pir.staging.rebreak.org |
4. Konfiguration
service-config.json (auf dem Server; Token nicht im Repo → siehe service-config.template.json):
- usecase-Name:
org.rebreak.app.url.filtering— muss EXAKT mit der iOS-setConfigurationmatchen, sonst HTTP 404. shardCount: 4 (215k Domains / 50k pro Shard).users[].tokens: enthält denPIR_AUTH_TOKEN.
PIR_AUTH_TOKEN — liegt in Infisical, Env staging, Key PIR_AUTH_TOKEN,
Project-ID 14b11b35-ef59-4b8a-a16b-398f0cc3ad93. App-weit halten (NICHT pro-User —
ein per-User-Token würde die PIR-Nicht-Verkettbarkeit schwächen).
process-config.json — Parameter für PIRProcessDatabase:
entryCountPerShard 50000, rlweParameters n_4096_logq_27_28_28_logt_5,
databaseType keyword.
PIR_ISSUER_REQUEST_URI — Env-Var am Container (docker run -e ...), gesetzt von
build-and-deploy.sh auf https://pir.staging.rebreak.org/issue. Zwingend absolut
(RFC 9578 §6): das Apple-pir-service-example advertised von Haus aus nur ein
relatives /issue im Issuer-Directory (PrivacyPassController.swift, hardcodiert) —
der NEURLFilter-Client kann damit keinen Privacy-Pass-Token holen und scheitert mit
serverSetupIncomplete. Behoben durch patches/0001-absolute-issuer-request-uri.patch,
das die URL aus dieser Env-Var liest.
5. iOS-Integrationswerte (NEURLFilterManager.setConfiguration)
| Parameter | Wert |
|---|---|
pirServerURL |
https://pir.staging.rebreak.org |
pirPrivacyPassIssuerURL |
https://pir.staging.rebreak.org (PIRService bedient beides) |
pirAuthenticationToken |
Infisical staging/PIR_AUTH_TOKEN |
| usecase-Name | org.rebreak.app.url.filtering |
controlProviderBundleIdentifier |
org.rebreak.app.URLFilterExtension (geplant — beim iOS-Scaffolding final festlegen) |
6. Daten-Pipeline (Blocklist → PIR-DB)
backend/scripts/generate-pir-input.tserzeugtinput.txtpbaus ~215k Gambling-Domains.- DB-Modus (Default): liest die
BlocklistDomain-Tabelle — brauchtinfisical runfürDATABASE_URL. - HaGeZi-Modus (
--source hagezi): direkt von HaGeZisgambling.txt, kein DB/Infisical nötig. Aktuell verwendet, weilinfisicalnicht auf dem Dev-Mac liegt. Äquivalent — dieBlocklistDomain-Tabelle wird ohnehin aus genau dieser HaGeZi-Liste gespeist. - Output-Format: pro Domain eine Zeile
rows: { keyword: "<domain>" value: "1" }.
- DB-Modus (Default): liest die
input.txtpb→ perscpnach/srv/pir-server/data/input.txtpb.PIRProcessDatabase /data/process-config.json(im Container) →url-N.bin+url-N.params.txtpb.
7. Betrieb / Runbook
Health-Check:
curl -s -o /dev/null -w '%{http_code}\n' \
https://pir.staging.rebreak.org/.well-known/private-token-issuer-directory # → 200
ssh rebreak-server 'docker ps | grep pir-service-staging'
Blocklist aktualisieren:
- Neue
input.txtpbgenerieren (backend/scripts/generate-pir-input.ts) + nach/srv/pir-server/data/input.txtpbscp'en. - Alte Shards löschen:
ssh rebreak-server 'rm /srv/pir-server/data/url-*.bin /srv/pir-server/data/url-*.params.txtpb'. PIRProcessDatabaseneu laufen lassen (siehebuild-and-deploy.shStep 6) — erzeugt neue Shards.shardCountinservice-config.jsonprüfen + Container neu starten.
Komplett neu bauen/deployen:
ssh rebreak-server 'bash /srv/pir-server/build-and-deploy.sh'
Swift-Build ~20–30 Min, RAM-intensiv (~2–3 GB → Swap nötig, off-peak bauen).
8. Bekannte Issues / TODOs
- ✅
build-and-deploy.shshardCount-Bug (BEHOBEN 2026-05-21): Step 3 leitet denshardCountjetzt aus den vorhandenenurl-*.bin-Artifacts ab statt ihn fix auf1zu setzen — kein Regress mehr von 4 Shards auf 1 bei Re-Runs. - ✅ Relatives
issuer-request-uri(BEHOBEN 2026-05-21): siehe §4PIR_ISSUER_REQUEST_URI. War die Ursache fürserverSetupIncompleteauf der iOS-Seite. Fix viapatches/0001-absolute-issuer-request-uri.patch, vonbuild-and-deploy.shStep 3b automatisch auf denpir-service-example-Clone angewandt. - Deploy-Skripte liegen sowohl auf dem Server (
/srv/pir-server/,/srv/pir-build/) als auch hier im Repo. Bei Änderungen synchron halten — oder das Repo als Single-Source-of-Truth etablieren (empfohlen). - App-Store-Distribution: braucht zusätzlich Apples OHTTP-Onboarding-Formular,
ein OHTTP-Gateway und einen DNS-TXT-Record
apple-url-filter=org.rebreak.app. Für TestFlight/Dev NICHT nötig. → Phase 2. - Entitlement
com.apple.developer.networking.networkextension.url-filter-provider: Dev-Builds ausgenommen; vor TestFlight/Store über den „Capability Requests"-Tab im Apple-Developer-Portal (Certificates, Identifiers & Profiles → Identifiers) beantragen. - Apple zu
pir-service-example: „example service, not for production" — Produktionshärtung (persistente PrivacyPass-Keys statt ephemerer, Monitoring, Ressourcen-Tuning) ist ein eigener späterer Schritt.
9. Dateien in diesem Verzeichnis
| Datei | Zweck |
|---|---|
README.md |
dieses Dokument |
Dockerfile |
Multi-Stage Swift-Build (Kopie von /srv/pir-build/Dockerfile) |
build-and-deploy.sh |
Deploy-Script (Kopie von /srv/pir-server/) |
gen-test-input.sh |
Test-DB-Generator (10 Domains, Fallback) |
process-config.json |
PIRProcessDatabase-Parameter |
service-config.template.json |
Service-Config-Vorlage (Token-Platzhalter) |
nginx-pir.staging.rebreak.org.conf |
nginx-Reverse-Proxy-Config (Referenz) |
patches/*.patch |
Quell-Patches für pir-service-example, von build-and-deploy.sh Step 3b angewandt |
Der input.txtpb-Generator selbst liegt bei backend/scripts/generate-pir-input.ts.