demo mode
deploy / deploy (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-27 23:38:37 +02:00
parent d28fd05bf6
commit 7caea3a1ee
44 changed files with 1682 additions and 460 deletions
+42
View File
@@ -2202,3 +2202,45 @@ tasks:
primary_paths:
- docs/api/openapi.yaml
- docs/architecture/adrs/0008-server-rendered-design.md
- id: VR-225
title: Hard publieke reverse-proxydeployment en gasttoegang uit
status: done
priority: P0
requirement_ids:
- NFR-001
- NFR-006
- NFR-009
depends_on:
- VR-224
summary: Maak de bestaande persoonlijke installatie veilig en reproduceerbaar bereikbaar achter Nginx Proxy Manager,
herstel publieke host- en CSRF-configuratie en voorkom dat een gedeeld demoaccount persistente data wijzigt.
acceptance_criteria:
- Eén publieke HTTPS-URL configureert hostvalidatie, CSRF, secure cookies, SSL-detectie en redirect zonder de interne
containerpoort in de externe origin op te nemen.
- Productie start fail-fast bij debugmodusgerelateerde onveiligheid, een zwakke secret, onveilige cookies, ontbrekende
HTTPS-origin of een schrijfbare publieke demo.
- Login- en importlimieten gebruiken in productie een gedeelde cache; forwarded client-IPs worden alleen vanaf
expliciet vertrouwde proxy-CIDRs geaccepteerd.
- Het gastaccount is server-side alleen-lezen, heeft een begrensde sessie, kan afmelden en toont de beperking zichtbaar.
- Unraid-envpad, Nginx Proxy Manager, healthchecks, noindex, heruitrol en terugdraaien zijn exact gedocumenteerd.
verification:
- PYTHONPATH=/mnt/data/vr_windows_site/site-packages pytest -q
- python manage.py check
- python manage.py makemigrations --check --dry-run
- python scripts/validate_repository.py
- bash -n scripts/deploy_docker.sh scripts/entrypoint.sh scripts/unraid_aio_entrypoint.sh
- Live Nginx-, TLS-, CSRF-, Dockerhealth- en restore-smoke op de doelserver
primary_paths:
- config/settings.py
- apps/core/middleware.py
- apps/core/network.py
- apps/core/health.py
- scripts/configure_public_url.py
- docker-compose.unraid.yml
- docs/operations/NGINX_PROXY_MANAGER.md
- docs/quality/PRODUCTION_AUDIT_2026-07-27.md
result:
completed_at: '2026-07-27'
note: Publieke host/CSRF/HTTPS-configuratie, gedeelde Redis-rate-limiting, proxy-IPvertrouwen, cache-readiness,
noindex en read-only gastdemo geïmplementeerd. 295 tests groen met 2 correcte Playwrightskips; Django check,
migratiecheck, repositoryvalidatie, YAML-, syntax- en packagechecks groen. Live servervalidatie blijft extern.
+14 -2
View File
@@ -1,7 +1,7 @@
# Projectstatus
- Laatst bijgewerkt: 2026-07-26
- Repositoryversie: 0.3.0 volledige Stitch Intelligence Cockpit
- Laatst bijgewerkt: 2026-07-27
- Repositoryversie: 0.3.12 productie- en reverse-proxyhardening
- Uitvoeringsmodus: autonome backlog
- Eerste uitvoerbare taak: controleer met `python scripts/backlog.py next`
@@ -19,6 +19,18 @@ De repository bevat een uitvoerbare Django-MVP met:
- Celeryworker/scheduler, healthchecks, Docker/Unraidconfiguratie en back-up-/herstelscripts;
- offline unit-, integratie- en securitytests.
## Laatste productiehardening
VR-225 op 2026-07-27:
- `PUBLIC_BASE_URL` stuurt publieke host-, origin- en HTTPS-instellingen aan en voorkomt de gemelde `DisallowedHost` achter Nginx Proxy Manager;
- publieke debugmodus, zwakke secrets, onveilige cookies, ontbrekende HTTPS-origin en een schrijfbare productiedemo worden fail-fast geweigerd;
- login- en importrate-limiting delen Redis over alle workers; forwarded client-IPs gelden alleen vanaf expliciet vertrouwde proxy-CIDRs;
- de gastdemo is server-side alleen-lezen, heeft een begrensde sessie en toont die beperking zichtbaar in de cockpit;
- interne healthchecks blijven HTTP-bruikbaar onder SSL redirect en readiness verifieert optioneel database plus cache;
- Unraid, publieke poort, env-pad, Nginx Proxy Manager en productie-env zijn als één reproduceerbaar deploymentcontract gedocumenteerd;
- de volledige offline suite is groen met 296 tests en 2 correcte Playwrightskips; Docker-, publieke TLS- en live Unraid-smokes blijven doelservervalidatie.
## Laatste frontendvervanging
VR-209 tot en met VR-213 vervangen de oude browserfrontend integraal:
+136
View File
@@ -0,0 +1,136 @@
# Nginx Proxy Manager en publiek HTTPS-domein
Deze handleiding publiceert VacatureRadar achter Nginx Proxy Manager zonder de Django-container rechtstreeks op internet bloot te stellen.
## Waarom `DisallowedHost` verschijnt
Django weigert onbekende `Host`-headers bewust. Een publieke hostnaam moet daarom expliciet overeenkomen met:
- `PUBLIC_BASE_URL`;
- `DJANGO_ALLOWED_HOSTS`;
- `DJANGO_CSRF_TRUSTED_ORIGINS`.
De applicatie leidt de host en CSRF-origin automatisch af uit `PUBLIC_BASE_URL`, maar de expliciete lijsten blijven ondersteund voor LAN- en beheerhosts.
Een publieke Django-debugpagina is een afzonderlijk beveiligingsprobleem. Gebruik achter Nginx altijd `DJANGO_DEBUG=0`; debugpagina's tonen interne paden en implementatiedetails.
## VacatureRadar configureren
Voer dit uit in de huidige bronmap op Unraid:
```bash
cd /mnt/user/appdata/vacatureradar/source
python scripts/configure_public_url.py \
https://vacatureradar.itworx.tech \
--env-file /mnt/user/appdata/vacatureradar/source/.env \
--cache-url redis://127.0.0.1:6379/1
```
De helper:
- bewaart bestaande secrets en databasewachtwoorden;
- zet `DJANGO_DEBUG=0`;
- voegt de host en CSRF-origin toe;
- activeert secure cookies en HTTPS-redirect;
- zet een conservatieve HSTS-periode;
- activeert reverse-proxyherkenning;
- maakt de gastdemo alleen-lezen;
- gebruikt Redis voor gedeelde rate limiting en readiness;
- schrijft het bestand atomair met modus `0600`.
Hermaak daarna de container. Een gewone restart laadt gewijzigde environmentvariabelen niet altijd opnieuw:
```bash
docker compose -f docker-compose.unraid.yml up -d --build --force-recreate
```
Voor een andere omgeving kan `deployment/production.env.example` als checklist worden gebruikt.
## Proxy Host in Nginx Proxy Manager
Gebruik voor de Proxy Host:
| Instelling | Waarde |
|---|---|
| Domain Names | `vacatureradar.itworx.tech` |
| Scheme | `http` |
| Forward Hostname / IP | het LAN-IP of de Docker-hostnaam van VacatureRadar |
| Forward Port | `1226` |
| Cache Assets | uit, tenzij expliciet getest |
| Block Common Exploits | aan |
| Websockets Support | niet vereist |
Op het tabblad **SSL**:
- vraag een geldig certificaat aan;
- activeer Force SSL;
- activeer HTTP/2;
- activeer HSTS pas nadat HTTPS correct werkt;
- activeer HSTS subdomains alleen wanneer alle subdomeinen HTTPS ondersteunen.
Nginx Proxy Manager stuurt de noodzakelijke proxyheaders normaal al door. Gebruik bij een aangepaste configuratie minstens:
```nginx
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
```
Overschrijf deze headers aan de proxyzijde; laat een internetclient ze niet vrij bepalen.
## Correcte client-IP-rate-limiting
VacatureRadar vertrouwt `X-Forwarded-For` alleen wanneer de directe afzender in `TRUSTED_PROXY_CIDRS` staat. Zonder deze instelling blijft rate limiting veilig op het proxy-IP werken, maar worden bezoekers niet individueel onderscheiden.
Bepaal het proxy-IP of Docker-netwerk en voeg alleen dat bereik toe, bijvoorbeeld:
```bash
python scripts/configure_public_url.py \
https://vacatureradar.itworx.tech \
--env-file /mnt/user/appdata/vacatureradar/source/.env \
--cache-url redis://127.0.0.1:6379/1 \
--trusted-proxy-cidr 172.18.0.0/16
```
Gebruik geen brede private range zoals `10.0.0.0/8` of `172.16.0.0/12` zonder aantoonbare noodzaak.
## Validatie
Controleer eerst intern:
```bash
curl -fsS http://127.0.0.1:1226/health/live/
curl -fsS http://127.0.0.1:1226/health/ready/
```
Controleer daarna publiek:
```bash
curl -I https://vacatureradar.itworx.tech/
curl -fsS https://vacatureradar.itworx.tech/robots.txt
```
Verwacht:
- geen `DisallowedHost`;
- geen Django-debugpagina;
- een redirect naar login of HTTP 200 na authenticatie;
- `Strict-Transport-Security` zodra HSTS actief is;
- `X-Content-Type-Options: nosniff`;
- `X-Frame-Options: DENY`;
- `Content-Security-Policy`;
- standaard `X-Robots-Tag: noindex, nofollow, noarchive` voor een persoonlijke installatie.
Controleer ook een echte login, logout en één CSRF-beschermde actie. Alleen een succesvolle GET bewijst niet dat proxy-HTTPS en CSRF correct zijn.
## Terugdraaien
Bij een probleem:
1. zet de Proxy Host tijdelijk op access-list/VPN-only of offline;
2. herstel de vorige `.env` uit de beheerde back-up;
3. hermaak de container;
4. controleer intern `/health/ready/`;
5. publiceer pas opnieuw nadat login en CSRF via HTTPS slagen.
+28 -29
View File
@@ -7,9 +7,10 @@ Deze handleiding gebruikt `docker-compose.unraid.yml` als declaratieve bron van
Maak deze directories:
```text
/mnt/user/appdata/vacatureradar/config
/mnt/user/appdata/vacatureradar/media
/mnt/user/appdata/vacatureradar/logs
/mnt/user/appdata/vacatureradar/source
/mnt/user/appdata/vacatureradar/source/local
/mnt/user/appdata/vacatureradar/source/local/media
/mnt/user/appdata/vacatureradar/source/local/logs
/mnt/user/appdata/vacatureradar/source/local/postgres-aio17
/mnt/user/appdata/vacatureradar/source/local/redis-aio
/mnt/user/appdata/vacatureradar/ollama # alleen bij lokale AI
@@ -46,48 +47,46 @@ Gebruik in productie geen zwevende `latest` zonder gecontroleerd rollbackpad.
## Configuratiebestand
Kopieer of genereer `.env` naar:
De deploy-helper `scripts/deploy_docker.sh` vult ontbrekende sleutels op basis van veilige defaults.
Zet voor publieke productie vooraf de gewenste variabelen:
De standaardlocatie die `docker-compose.unraid.yml` leest is:
```text
/mnt/user/appdata/vacatureradar/config/.env
APP_HOST=jobs.example.be
APP_SCHEME=https
DJANGO_DEBUG=0
SESSION_COOKIE_SECURE=1
CSRF_COOKIE_SECURE=1
SECURE_SSL_REDIRECT=1
/mnt/user/appdata/vacatureradar/source/.env
```
Gebruik daarna de helper (zie hieronder).
Een afwijkende locatie kan vóór Compose via `VACATURERADAR_ENV_FILE` worden ingesteld. Gebruik [`deployment/production.env.example`](../../deployment/production.env.example) alleen als checklist en kopieer nooit ongewijzigde `CHANGE_ME`-waarden.
Configureer een bestaand bestand voor de publieke URL zonder secrets te overschrijven:
```bash
cd /mnt/user/appdata/vacatureradar/source
python scripts/configure_public_url.py \
https://vacatureradar.example.be \
--env-file /mnt/user/appdata/vacatureradar/source/.env \
--cache-url redis://127.0.0.1:6379/1
```
Beperk de bestandsrechten van `.env` tot de beheerder. Voeg geen secrets toe aan de ZIP, Git, screenshots of supportlogs.
## Eerste uitrol
1. Controleer alle `CHANGE_ME`-waarden.
2. Gebruik de deploy-helper voor een snelle eerste start:
1. Controleer alle `CHANGE_ME`-waarden en maak een back-up van een bestaande `.env`.
2. Start of hermaak de Unraid-container vanuit de bronmap:
```bash
cd /app/VacatureRadar
APP_HOST=jobs.example.be APP_SCHEME=https DJANGO_DEBUG=0 \
SESSION_COOKIE_SECURE=1 CSRF_COOKIE_SECURE=1 SECURE_SSL_REDIRECT=1 \
bash scripts/deploy_docker.sh
cd /mnt/user/appdata/vacatureradar/source
docker compose -f docker-compose.unraid.yml up -d --build --force-recreate
```
3. Start de ene appcontainer; de entrypoint initialiseert PostgreSQL, voert migraties uit en start daarna alle processen onder Supervisor.
6. Controleer `GET /health/live/` en `GET /health/ready/`.
6. Voer eenmalig in de webcontainer uit:
3. De entrypoint valideert de productieconfiguratie, initialiseert PostgreSQL, voert migraties uit en start alle processen onder Supervisor.
4. Controleer intern `GET /health/live/` en `GET /health/ready/`.
5. Voer alleen wanneer bootstrap niet automatisch is ingeschakeld eenmalig uit:
```bash
python manage.py bootstrap_instance
python manage.py collectstatic --noinput
docker exec -it VacatureRadar python manage.py bootstrap_instance
```
7. Meld lokaal aan en wijzig het bootstrapwachtwoord.
8. Activeer nog geen live bron of mailbox voordat bronbeleid, retentie en back-up zijn gecontroleerd.
6. Meld lokaal aan en wijzig het bootstrapwachtwoord.
7. Activeer nog geen live bron of mailbox voordat bronbeleid, retentie en back-up zijn gecontroleerd.
## Beheerverbinding en heruitrol
@@ -135,7 +134,7 @@ Expose de applicatie niet rechtstreeks op internet. Plaats Nginx Proxy Manager,
- optioneel extra access control/VPN voor persoonlijke installatie;
- geen publieke toegang tot PostgreSQL, Redis of Ollama.
Wanneer een proxy op hetzelfde Docker-netwerk draait, publiceer poort 1226 alleen intern. Wanneer Unraid routing een hostpoort vereist, beperk die via firewall/VLAN tot de proxy of het beheernetwerk.
Wanneer een proxy op hetzelfde Docker-netwerk draait, publiceer poort 1226 alleen intern. Wanneer Unraid routing een hostpoort vereist, beperk die via firewall/VLAN tot de proxy of het beheernetwerk. Volg voor host-, CSRF-, proxyheader- en TLS-instellingen de [Nginx Proxy Manager-handleiding](NGINX_PROXY_MANAGER.md).
## Mailbox
@@ -0,0 +1,52 @@
# Productie-audit — 27 juli 2026
## Samenvatting
VacatureRadar heeft al een volwassen functionele en visuele basis. De audit vond geen noodzaak voor een nieuwe frontendarchitectuur; de belangrijkste professionaliseringswinst zat in de grens tussen Django, Nginx Proxy Manager en de Unraid-runtime.
De gerapporteerde `DisallowedHost` was een symptoom van ontbrekende publieke hostconfiguratie. De publiek zichtbare Django-debugpagina maakte tegelijk duidelijk dat de runtime nog ontwikkelinstellingen gebruikte. Deze release behandelt daarom hostvalidatie, HTTPS-detectie, CSRF, gedeelde rate limiting, readiness en gasttoegang als één productiecontract.
## Bevindingen en herstel
| Prioriteit | Bevinding | Risico | Herstel |
|---|---|---|---|
| Kritiek | Publieke host ontbrak in `ALLOWED_HOSTS` | onbeschikbare publieke route | `PUBLIC_BASE_URL` leidt host en CSRF-origin af; configuratiehelper toegevoegd |
| Kritiek | `DEBUG=True` was publiek zichtbaar | lek van interne paden en implementatiedetails | productiehelper zet debug uit; productiecheck stopt bij onveilige configuratie |
| Hoog | HTTPS-origin kon de interne poort `1226` bevatten | CSRF-fouten achter poort 443 | publieke en interne poort zijn gescheiden in deployscripts |
| Hoog | Secure redirect kon interne healthchecks omleiden | container blijft unhealthy na HTTPS-hardening | alleen `/health/live/` en `/health/ready/` zijn intern vrijgesteld |
| Hoog | Loginrate-limiting gebruikte proceslokale cache | limiet verschilde per Gunicornworker | Redis-cache als gedeelde productiestandaard en readinessdependency |
| Hoog | `X-Forwarded-For` werd zonder proxyvertrouwen gebruikt | IP-spoofing en omzeilbare rate limiting | forwarded chain alleen bij expliciet vertrouwde proxy-CIDR |
| Hoog | Gedeelde demo kon mutaties uitvoeren | bezoekers beïnvloeden elkaars demo en persistente data | server-side read-only middleware, begrensde sessie en zichtbare UI-status |
| Middel | Unraid-documentatie wees naar een andere `.env` dan Compose | wijzigingen werden niet geladen | pad gestandaardiseerd op `source/.env`; override blijft mogelijk |
| Middel | Deployhelper schreef een custom `.env`, maar Compose kon alsnog de standaardfile lezen | productie-instellingen werden stil genegeerd | geselecteerd env-bestand wordt nu voor Compose-interpolatie én containeromgeving gebruikt |
| Middel | AIO Compose interpoleerde databasewachtwoord buiten `env_file` | custom env-pad kon een kapotte URL leveren | database-URL wordt uitsluitend in de containerentrypoint opgebouwd |
| Middel | Persoonlijke installatie kon geïndexeerd worden | onbedoelde vindbaarheid van login en productmetadata | `robots.txt` en `X-Robots-Tag` standaard op noindex |
| Middel | Runtime startte zonder Django deployment check | foutieve securityconfiguratie werd laat ontdekt | `manage.py check --deploy` vóór migraties en processtart |
| Laag | Project- en assetversies liepen uiteen | cache- en release-identiteit onduidelijk | releaseversie gecentraliseerd op `0.3.12` |
| Laag | Overdrachtsarchief bevatte lokale Windows/runtimeartefacten | onnodige omvang en risico op secrets of stale state | bestaande schone packager en integriteitsmanifest verplicht gebruikt |
## Bewust niet gewijzigd
- De server-rendered Django-architectuur blijft behouden.
- De Stitch Intelligence Cockpit blijft de productiefrontend; er is geen SPA of extern runtime-CDN toegevoegd.
- De containerpoort blijft op Unraid bereikbaar voor Nginx Proxy Manager. Netwerksegmentatie of firewalling blijft een live infrastructuurtaak.
- HSTS start conservatief op 300 seconden. Verhoog pas na een geslaagde publieke login-, CSRF- en subdomeincontrole.
- Zoekmachine-indexering blijft standaard uit omdat dit een persoonlijke installatie is.
## Live controles na deployment
1. Hermaak de container met de bestaande `.env`.
2. Controleer intern beide health-endpoints.
3. Controleer publiek dat HTTP naar HTTPS gaat.
4. Controleer login en logout.
5. Voer één echte CSRF-beschermde actie uit met het beheerdersaccount.
6. Controleer dat het demoaccount geen mutatie kan uitvoeren.
7. Controleer securityheaders en afwezigheid van een debugpagina.
8. Controleer in Nginx Proxy Manager dat alleen de proxy de hostpoort kan bereiken.
9. Maak een back-up en voer een herstel-smoke uit vóór verdere bronautomatisering.
## Resterende externe punten
- De echte Nginx Proxy Manager-configuratie en het proxy-IP/CIDR zijn niet in de repository zichtbaar.
- De live Unraid-container, database en Redis zijn in deze auditomgeving niet bereikbaar.
- Een volledige Dockerbuild, publieke TLS-smoke en hersteltest moeten daarom op de doelserver worden uitgevoerd.
+3 -1
View File
@@ -81,6 +81,8 @@ Django/Celery
| T-39 | Mol-/Kempenbron vermengt een andere werkgever, campus of vestiging | foutieve afstand, phishing of buitenregionale resultaten | exacte HTTPS-host/pad/GUID-routes; same-host details; expliciete plaatsfilters voor regionale en gedeelde lijsten; postcodecentra blijven als benadering herkenbaar; waaklijstbronnen worden niet gescand | `tests/unit/test_regional_adapters.py`, `tests/integration/test_tasks_and_more_views.py`, `VR-126/201` |
| T-40 | Verouderde lokale CA-store of ongeldige serverketen lokt een TLS-bypass uit | onderschepte vacature- of mailboxdata en credentialdiefstal | expliciet gepinde `certifi`-CA-bundel voor HTTP, robots en IMAP; hostnaamcontrole en `CERT_REQUIRED`; nooit `verify=False`; dependency- en negatieve TLS-tests bij update | `tests/unit/test_tls.py`, `tests/security/test_robots.py`, `apps/sources/services/tls.py`, `VR-201` |
| T-41 | Gemodificeerde `next`-parameter na vacaturefeedback | open redirect naar phishingdomein na een geldige gebruikersactie | terugkeer-URL moet dezelfde host en het actuele HTTPS-beleid respecteren; anders veilige detailfallback | `tests/integration/test_tasks_and_more_views.py`, `VR-206` |
| T-42 | Gespoofte proxyheaders of ontbrekende publieke hostconfiguratie | host-headerfouten, onjuiste HTTPS-detectie of omzeilde IP-rate-limiting | `PUBLIC_BASE_URL`, exacte host/CSRF-validatie, proxy moet headers overschrijven, forwarded client-IP alleen vanaf `TRUSTED_PROXY_CIDRS` | `tests/unit/test_public_configuration.py`, `tests/unit/test_network.py`, `VR-225` |
| T-43 | Gedeeld demoaccount wijzigt persistente data | bezoekers beïnvloeden elkaars ervaring of echte data | server-side read-only middleware, korte sessie, geen bruikbaar wachtwoord, zichtbare UI-status en productiecheck | `tests/integration/test_auth_login.py`, `tests/unit/test_settings_security.py`, `VR-225` |
## Misbruikscenario's
@@ -114,7 +116,7 @@ Een wijziging aan netwerk, mail, AI, rendering, auth, exports of uploads vereist
## Resterende risico's
- De fetcher controleert het werkelijk verbonden peer-IP wanneer httpcore dat exposeert. Een custom transport dat geen peerinformatie aanbiedt valt terug op DNS-set-overlap; volledige IP-pinning blijft een aanvullend egress-hardeningniveau.
- Brute-force of herhaalde foutieve mutatiepogingen blijven mogelijk, maar zijn beperkt door in-memory rate limiting en blokkades op login/manual-import.
- Brute-force of herhaalde foutieve mutatiepogingen blijven mogelijk, maar zijn in productie begrensd via gedeelde Redis-rate-limiting en blokkades op login/manual-import. Correcte individuele client-IPherkenning vereist het exacte proxy-CIDR.
- Bronvoorwaarden vereisen menselijke/externe review per domein; automatisering kan dat niet juridisch beslissen.
- Fuzzy dedupe en featureheuristieken kunnen inhoudelijk verkeerd zijn; provenance, feedback en benchmarkevaluaties beperken maar elimineren dit niet.
- Een persoonlijke Unraid-host blijft afhankelijk van correcte netwerksegmentatie, patching en back-ups door de beheerder.
+4 -2
View File
@@ -56,7 +56,7 @@ Legenda: **I** = geïmplementeerd en getest in de huidige MVP; **P** = gedeeltel
| ID | Status | Bewijs | Vervolgtaak |
|---|---|---|---|
| NFR-001 | I | policy-, URL- en fetchersecuritytests + settings/rate-limit tests | `tests/unit/test_fetcher.py`, `tests/unit/test_rate_limit.py`, `tests/unit/test_settings_security.py`, `tests/integration/test_views.py`, `tests/integration/test_sources_manual_import.py` |
| NFR-001 | I | policy-, URL- en fetchersecuritytests plus publieke host-, proxyvertrouwen-, demo- en gedeelde rate-limit-hardening | `tests/unit/test_fetcher.py`, `tests/unit/test_rate_limit.py`, `tests/unit/test_settings_security.py`, `tests/unit/test_public_configuration.py`, `tests/unit/test_network.py`, `tests/integration/test_auth_login.py`, VR-225 |
| NFR-002 | P | raw-retentie, minimale modellen, privacyrunbooks | VR-113/115 |
| NFR-003 | I/P | replay-, mail- en outboxtests; per-bronfouten | VR-103/112 |
| NFR-004 | I | ScoreRun, provenance, skillvraag met expliciet veld-/tekstonderscheid en UI-copy | VR-109/208 uitgebreid evidence |
@@ -64,7 +64,7 @@ Legenda: **I** = geïmplementeerd en getest in de huidige MVP; **P** = gedeeltel
| NFR-006 | I | offline pytest + coveragegate | VR-114 verbreedt e2e |
| NFR-007 | P | `scripts/benchmark.py`, `tests/benchmark/test_benchmark.py` | VR-116 |
| NFR-008 | I | volledige negen-schermen-Stitch-cockpit, acht viewports, dark/light, toetsenborddrawer, premium visuele hiërarchie, zelfstandig VacatureRadar-productmerk, lokale ITWorx-ontwikkelaarscredit, toegankelijke interactieve radarillustratie en actuele user-first productdocumentatie | `tests/e2e/test_vr114_browser_and_accessibility.py`, `tests/integration/test_intelligence_views.py`, `tests/integration/test_views.py`, `artifacts/visual-validation/`, `docs/screenshots/readme/`, VR-209224 |
| NFR-009 | I | lokale SQLite, configureerbare eigenaarbranding, Docker/Unraidconfig, gevalideerde SSH-deployalias, release-smoke en restore | `tests/unit/test_deployment_configuration.py`, VR-117, VR-217/218 afgerond |
| NFR-009 | I | lokale SQLite, configureerbare eigenaarbranding, Docker/Unraidconfig, publieke URL-helper, Nginx Proxy Manager-runbook, gevalideerde SSH-deployalias, release-smoke en restore | `tests/unit/test_deployment_configuration.py`, `tests/unit/test_public_configuration.py`, VR-117, VR-217/218/225 afgerond |
| NFR-010 | I | `apps/sources/services/health.py`, `apps/sources/tasks.py`, `apps/core/views.py`, `config/settings.py` | `tests/integration/test_source_health.py` | VR-112 observability |
## Systeemacceptatie
@@ -111,3 +111,5 @@ Iedere backlogtaak die een requirement wijzigt, werkt in dezelfde commit deze ma
- VR-220 vervangt die tijdelijke tekstlock-up door de aangeleverde officiële ITWorx.tech-wordmark en een exact daaruit afgeleid cloud-check-appicoon voor browser, shell en Dockerman; VacatureRadar blijft de productnaam.
- VR-221 corrigeert de merkarchitectuur: VacatureRadar krijgt een zelfstandig radar-/kansbeeldmerk voor alle productcontactpunten en ITWorx.tech blijft uitsluitend zichtbaar als officiële secundaire ontwikkelaarscredit.
- VR-224 maakt de repository-ingang user-first: de README volgt de dagelijkse productflow, bevat gecontroleerde lokale quickstarts en gebruikt vier actuele dark/light- en desktop/mobielscreenshots zonder secrets of externe assets.
- VR-225 hardent de publieke installatie met `PUBLIC_BASE_URL`, exacte host/CSRF-afleiding, veilige proxy-HTTPS-detectie, vertrouwde client-IPchains, gedeelde Redis-rate-limiting, cache-readiness en een server-side alleen-lezen gastdemo. Bewijs staat in de productie-audit, Nginx-runbook en de settings-, network-, health-, auth- en deploymenttests.