diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml deleted file mode 100644 index 5d35281..0000000 --- a/.gitea/workflows/deploy.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: deploy - -on: - push: - branches: - - main - - master - -permissions: - contents: read - -concurrency: - group: deploy-${{ github.ref }} - cancel-in-progress: false - -jobs: - deploy: - runs-on: self-hosted - steps: - - uses: actions/checkout@v4 - - name: Push-to-deploy op server - run: | - set -euo pipefail - - if [ -z "${DEPLOY_COMPOSE_FILE:-}" ]; then - DEPLOY_COMPOSE_FILE="docker-compose.yml" - fi - - if [ -z "${APP_HOST:-}" ]; then - APP_HOST="$(hostname -f 2>/dev/null || hostname)" - fi - - if [ -z "${APP_SCHEME:-}" ]; then - APP_SCHEME="http" - fi - - if [ -z "${DJANGO_DEBUG:-}" ]; then - if [ "${APP_SCHEME}" = "https" ]; then - DJANGO_DEBUG="0" - else - DJANGO_DEBUG="1" - fi - fi - - export APP_HOST - export APP_SCHEME - export DJANGO_DEBUG - - export DEPLOY_COMPOSE_FILE - if [ -n "${APP_ORIGINS:-}" ]; then - export APP_ORIGINS - fi - - if [ -n "${APP_HOSTS:-}" ]; then - export APP_HOSTS - fi - - if [ -n "${APP_PORT:-}" ]; then - export APP_PORT - fi - - bash scripts/deploy_docker.sh "${DEPLOY_COMPOSE_FILE}" .env diff --git a/CHANGELOG.md b/CHANGELOG.md index 0964dd7..c39de54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ Alle betekenisvolle wijzigingen worden hier bijgehouden. Het project volgt voorl - De Gitea-releasebootstrap gebruikt een immutable `setup-uv`-commit en een exacte uv-versie; downloads lopen via de Astral-mirror en zijn niet langer afhankelijk van de gelimiteerde GitHub Releases-API op het gedeelde runner-IP. +- De achterhaalde `main`-push-to-deployworkflow is verwijderd; alleen een + semantische tag kan nog een immutable releaseimage publiceren. +- De Unraid Docker-host gebruikt een conflictvrije `/24`-address pool met ruimte + voor 256 tijdelijke en toekomstige netwerken. ## 0.3.16 - 2026-08-12 diff --git a/docs/ai/BACKLOG.yaml b/docs/ai/BACKLOG.yaml index d9beea2..8dc6e6e 100644 --- a/docs/ai/BACKLOG.yaml +++ b/docs/ai/BACKLOG.yaml @@ -2445,3 +2445,34 @@ tasks: note: Releasebootstrap gepind op immutable setup-uv v8.1.0-commit en uv 0.11.12 via Astral-mirror; Gitea Actions-run 3062 publiceerde digest sha256:98f4b33d met checksum-geldige SBOM/release-evidence; Unraid gepind op volledige digest en live health/readiness groen. +- id: VR-231 + title: Sluit de operationele 0.3.17-restpunten + status: done + priority: P0 + requirement_ids: + - NFR-009 + depends_on: + - VR-230 + summary: Verwijder de achterhaalde push-to-deployroute, herstel voldoende Docker-netwerkcapaciteit, valideer de + resterende mailboxfout en maak actuele versus historische deploymentstatus ondubbelzinnig. + acceptance_criteria: + - Main-pushes maken geen onbeperkt wachtende deployruns meer; alleen de immutable tagrelease bouwt en publiceert. + - Unraid heeft een persistente, conflictvrije default address pool met minstens 256 kleine bridge-netwerken. + - De Jobat-productiemailbox slaagt opnieuw via de bestaande OAuth/IMAP-taak of levert een exact extern restpunt. + - Projectstatus en deploymentdocumentatie beschrijven alleen het actuele releasepad als operationele waarheid. + verification: + - uv run pytest tests/unit/test_deployment_configuration.py + - ./scripts/codex_verify.sh + - Live Docker-netwerk-, mailbox-, container- en readinesssmokes + primary_paths: + - .gitea/workflows/release.yml + - docs/operations/GITEA_DEPLOYMENT.md + - docs/operations/UNRAID_DEPLOYMENT.md + - docs/ai/PROJECT_STATE.md + - tests/unit/test_deployment_configuration.py + result: + completed_at: '2026-08-12' + note: Oude main-pushdeploy verwijderd en run 3065 begrensd geannuleerd; Jobat OAuth/IMAP-herpoll groen met 61 + duplicates en 0 fouten; Unraid persistent op conflictvrije 10.200.0.0/16→/24-pool gezet en live proefnetwerk + bewezen; na Docker-herstart alle vooraf actieve containers hersteld en geen unhealthy containers, VacatureRadar + publiek/intern healthy. diff --git a/docs/ai/PROJECT_STATE.md b/docs/ai/PROJECT_STATE.md index 3b2d2aa..a13baf5 100644 --- a/docs/ai/PROJECT_STATE.md +++ b/docs/ai/PROJECT_STATE.md @@ -58,6 +58,16 @@ Visuele demo-hardening `VR-229` op 2026-08-12: `/mnt/user/appdata/VacatureRadar/local/backups/release-0.3.17-20260812T114510Z`; - Unraid is live op de immutable digest gepind: container healthy, nul restarts, interne liveness/readiness groen, publieke readiness 200 en runtimeversie 0.3.17. +- `VR-231` verwijderde de oude `main`-push-to-deployworkflow en annuleerde de + laatste onbeperkt wachtende run 3065; alleen semantische tags publiceren nog; +- Jobat is via de bestaande OAuth/IMAP-taak geforceerd herhaald: 61 duplicaten + correct herkend, nul fouten en de foutstatus volledig gewist; +- Unraid gebruikt persistent `10.200.0.0/16,size=24`; een live proefnetwerk kreeg + `10.200.1.0/24` en werd daarna verwijderd. Na de gecontroleerde Docker-herstart + zijn alle 127 vooraf actieve containers hersteld; VacatureRadar, Grimmory en + MeTube zijn healthy en er zijn geen unhealthy of herstartende containers; +- serverback-ups bewaren de eerdere `docker.cfg`, de running-containersnapshot, + de Grimmory-template en de Gitea-database vóór de begrensde runannulering. Volgende uitvoerbare backlogtaak: geen. README-update op 2026-07-30: @@ -454,7 +464,10 @@ VR-208-herverificatie op 2026-07-22: - De applicatie draait op de Unraid-server via `http://192.168.10.150:1226/` als één Dockerman-container; Supervisor bewaakt intern PostgreSQL 17, Redis, web, worker en scheduler. - De Dockerman-tegel gebruikt de lokale VacatureRadar-favicon en opent rechtstreeks de WebUI op poort 1226. - De server-side `.env` heeft rechten `0600`; gegenereerde secrets zijn niet naar Git of logs gekopieerd. -- De self-hosted Gitea Actions-runner is niet actief. Deze sessie gebruikte daarom een directe, commitgebonden SSH-deploy; push-to-deploy vereist nog runnerherstel/registratie. +- Historisch bewijs (2026-07-22, inmiddels vervangen): de self-hosted runner was + toen niet actief en die sessie gebruikte een directe SSH-deploy. Sinds 0.3.17 + is uitsluitend de taggestuurde immutable registryrelease operationeel; een + gewone push naar `main` deployt bewust niet. - Deze historische LAN-beperking is op 2026-07-29 opgeheven: het publieke domein, reverse proxy/TLS, `DJANGO_DEBUG=0` en secure cookies zijn live gevalideerd. ## Laatste uitgevoerde backlogtaak diff --git a/docs/operations/GITEA_DEPLOYMENT.md b/docs/operations/GITEA_DEPLOYMENT.md index 92364f6..80729fb 100644 --- a/docs/operations/GITEA_DEPLOYMENT.md +++ b/docs/operations/GITEA_DEPLOYMENT.md @@ -1,40 +1,39 @@ -# Gitea push-to-deploy (self-hosted, geen handmatige variabelenvulling) +# Gitea immutable releasepad -Push naar `main` of `master` triggert automatisch: +Een push naar `main` publiceert geen productiecode. Alleen een expliciete semantische +tag `v*.*.*` start `.gitea/workflows/release.yml`. De runner: -- checkout op de server runner -- `bash scripts/deploy_docker.sh` -- deploy met `docker compose up -d --build` +1. verifieert de volledige repository; +2. bouwt de Unraid-image met commit- en versielabels; +3. publiceert die image naar de private Gitea-containerregistry; +4. genereert een SPDX-SBOM, digestbestand, checksums en release notes. -Deze workflow gebruikt defaults en hoeft niet te wachten op repo-secrets voor host/ssh/credentials. +Productie wordt daarna expliciet op de volledige `versie@sha256:digest` gepind. +Hierdoor kan een gewone documentatie- of codepush nooit ongemerkt productie +overschrijven en bestaat geen parallel mutable push-to-deploypad meer. -## Vereiste eenmalige setup op de server +## Vereiste runnerconfiguratie -1. Start en registreer een self-hosted Gitea-runner voor deze repo. -2. Zorg dat die runner Docker/Compose kan draaien in de projectmap waar de runner draait. -3. Laat éénmaal dit script draaien op de server: +- De Gitea-runner accepteert label `ubuntu-latest` en kan jobcontainers starten. +- `REGISTRY_USER` en `REGISTRY_TOKEN` zijn repositorysecrets met uitsluitend de + noodzakelijke package-schrijfrechten. +- Unraid gebruikt een afzonderlijk `read:package`-token om gepubliceerde images te + pullen; dat token staat alleen in de root-Dockerconfig op de server. +- De runnerhost heeft voldoende vrije Docker-address-pools voor tijdelijke + jobnetwerken; volg daarvoor de netwerksectie in + [Unraid-deployment](UNRAID_DEPLOYMENT.md#docker-netwerkcapaciteit). -```bash -bash scripts/deploy_docker.sh -``` +## Publiceren en uitrollen -Daarna is het gedrag volledig push-only: +1. Maak en verifieer een database- en configuratieback-up. +2. Push de geverifieerde releasecommit naar `main`. +3. Maak en push de semantische tag. +4. Wacht tot de Gitea-releasejob groen is en verifieer de artifactchecksums. +5. Pull op Unraid de gepubliceerde volledige digest. +6. Zet `VACATURERADAR_IMAGE` op `versie@sha256:digest` en herstart Compose met + `--no-build --force-recreate`. +7. Controleer intern en publiek `/health/live/` en `/health/ready/`, runtimeversie, + containerhealth en restartteller. -- `git push` naar `main` of `master` → automatische deploy - -## Defaults - -- `APP_HOST`: hostname van de server (fallback: `hostname`) -- `APP_SCHEME`: `http` -- `DJANGO_DEBUG`: afgeleid uit `APP_SCHEME` - - `0` als `APP_SCHEME=https` - - `1` als `APP_SCHEME=http` -- `DEPLOY_COMPOSE_FILE`: `docker-compose.yml` -- `APP_ORIGINS`, `APP_HOSTS`, `APP_PORT`: optioneel en veilig overnemen als op runner gezet - -## Endpoint na deploy - -- `http://:1226/health/ready/` -- `http://:1226/health/live/` - -Voor productie: zet later op de runner de gewenste runtime-omgeving (bijv. reverse proxy + TLS) en herstart push. +PostgreSQL, Redis en Ollama blijven niet publiek bereikbaar. Bewaar de vorige +digest totdat minstens één volledige schedulercyclus groen is. diff --git a/docs/operations/UNRAID_DEPLOYMENT.md b/docs/operations/UNRAID_DEPLOYMENT.md index 8c80983..98d409a 100644 --- a/docs/operations/UNRAID_DEPLOYMENT.md +++ b/docs/operations/UNRAID_DEPLOYMENT.md @@ -168,6 +168,23 @@ Herstart web, worker en scheduler. Open daarna **Bronnen → Platformmailboxen** Voor een immutable registryrelease zet je `VACATURERADAR_IMAGE=gitea.itworx.tech/jens/vacatureradar:@sha256:` en start je met `docker compose -f docker-compose.unraid.yml up -d --no-build`. +## Docker-netwerkcapaciteit + +Deze Unraid-host draait veel geïsoleerde Compose-netwerken. De ingebouwde +Dockerpools delen grote `/16`- en `/20`-netwerken uit en kunnen daardoor opraken +terwijl er nog ruim voldoende private adresruimte bestaat. De live host gebruikt +daarom persistent in `/boot/config/docker.cfg`: + +```text +DOCKER_OPTS="--default-address-pool base=10.200.0.0/16,size=24" +``` + +Dat levert 256 nieuwe `/24`-netwerken en overlapt niet met LAN `192.168.10.0/24`, +WireGuard `10.253.0.0/16` of bestaande Docker-netwerken. De instelling geldt +alleen voor nieuw aangemaakte netwerken; bestaande subnets veranderen niet. +Maak vóór wijziging een kopie van `docker.cfg`, herstart Docker éénmaal en bewijs +de pool met een tijdelijk netwerk dat na inspectie meteen wordt verwijderd. + Voor sleutelrotatie zet je `nieuwe-sleutel,oude-sleutel` in `MAILBOX_CREDENTIAL_KEYS`, herstart je de processen en draai je: ```bash diff --git a/docs/quality/THREAT_MODEL.md b/docs/quality/THREAT_MODEL.md index 1b2cc36..3299fec 100644 --- a/docs/quality/THREAT_MODEL.md +++ b/docs/quality/THREAT_MODEL.md @@ -67,7 +67,7 @@ Django/Celery | T-19 | Automatisch solliciteren/massamail | reputatie/privacy/juridisch risico | geen submitendpoint/task, expliciete ADR en non-goal | codezoekreview, AC-043 | | T-20 | Kwaadaardig bestand in sollicitatiedossier | host/browsercompromis | uploads nog niet actief; vóór implementatie allowlist, sniffing, size limit, niet-uitvoerbare opslag vereist | `VR-113`, nieuwe threat review | | T-21 | Back-up bevat secrets/PII | langdurig datalek | encryptie, gescheiden opslag, korte retentie, herstelprocedure | runbook en hersteltest | -| T-22 | Supply-chainpackage/image | code execution | exacte versies/lockfile, beperkte dependencies, pinned baseimages/digests en scans gepland | CI, `VR-115` | +| T-22 | Supply-chainpackage/image | code execution | exacte versies/lockfile, beperkte dependencies, pinned baseimages/digests, taggestuurde immutable release als enig productiepad, gescheiden package read/write-tokens en SBOM/checksums | CI, `VR-115`, `VR-230`, `VR-231` | | T-23 | Onbevoegde admin op thuisnetwerk | volledige datatoegang | uniek wachtwoord, TLS/VPN, geen defaultcredentials, sessiebeveiliging | Unraidchecklist | | T-24 | Source terms wijzigen | ongewenste voortgezette crawling | reviewdatum, source health en policy expiry gepland | `VR-102`, `VR-112` | | T-31 | Sollicitatiedossier-export bevat niet-gewenste payload | data-lek of onbedoeld dossieroverdragen | export bevat alleen het gekozen dossier, ZIP-inhoud is beperkt tot snapshot/tijdlijn/print-HTML, bestandsnaam is geslugified zonder padseparators | `tests/integration/test_applications.py` | diff --git a/docs/quality/TRACEABILITY_MATRIX.md b/docs/quality/TRACEABILITY_MATRIX.md index ccca412..6e470e1 100644 --- a/docs/quality/TRACEABILITY_MATRIX.md +++ b/docs/quality/TRACEABILITY_MATRIX.md @@ -64,7 +64,7 @@ Legenda: **I** = geïmplementeerd en getest in de huidige MVP; **P** = gedeeltel | NFR-006 | I | offline pytest + coveragegate, inclusief platformbewuste productieconfiguratietests | VR-114 e2e; `tests/unit/test_public_configuration.py`; VR-225/226 | | 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/`, negen beelden in `docs/screenshots/readme/`, VR-209–224/226 | -| NFR-009 | I | lokale SQLite, configureerbare eigenaarbranding, Docker/Unraidconfig, publieke URL-helper, Nginx Proxy Manager-runbook, gevalideerde SSH-deployalias, API-onafhankelijke immutable releasebootstrap, release-smoke en restore | `tests/unit/test_deployment_configuration.py`, `tests/unit/test_public_configuration.py`, VR-117, VR-217/218/225/230 afgerond | +| NFR-009 | I | lokale SQLite, configureerbare eigenaarbranding, Docker/Unraidconfig, publieke URL-helper, Nginx Proxy Manager-runbook, gevalideerde SSH-deployalias, API-onafhankelijke immutable releasebootstrap zonder parallel push-to-deploypad, begrensde Docker-address pool, release-smoke en restore | `tests/unit/test_deployment_configuration.py`, `tests/unit/test_public_configuration.py`, VR-117, VR-217/218/225/230/231 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 @@ -87,6 +87,7 @@ Iedere backlogtaak die een requirement wijzigt, werkt in dezelfde commit deze ma - VR-228 levert release 0.3.14 met een fail-closed disposable PostgreSQL-benchmark, run-lokale importcaches, gebundelde veldprovenance en ongewijzigde score-snapshotcopy. De officiële drie iteraties bewijzen 3.264 queries per 250 items, parser 12/12, dedupeprecision/recall 1,0 en nul rankingchurn; bewijs staat in `docs/audit/IMPORT_PERFORMANCE_*.md` en het machineleesbare artifact. - VR-229 houdt de publieke demo representatief door fictieve vacatures, scores, mailboxkoppelingen en opvolgdatums bij iedere demosessie idempotent te herstellen. Mailboxingress is uitgesloten van URL-fetchplanning en operationele UI-copy onderscheidt een actief profiel van bron- en systeemgezondheid. - VR-230 pint `setup-uv` en uv exact, vermijdt de gelimiteerde GitHub Releases-API via de Astral-mirror en bewijst publicatie plus live pinning van de immutable 0.3.17-registrydigest. +- VR-231 verwijdert de hangende mutable deployroute, vergroot de Unraid-netwerkcapaciteit conflictvrij naar 256 `/24`-netwerken en bewijst na een volledige Docker-herstart dat alle vooraf actieve containers en VacatureRadar gezond zijn; de resterende Jobat-poll is opnieuw foutvrij geslaagd. - VR-227 professionaliseert release 0.3.13 integraal: gecentreerd ultrawideframe, mobiele gastbanner en verticale pipeline, begrensde vacaturepaginering, verplichte zes-viewport-Playwrightgate zonder skips, echte runtimeheartbeat, volledige generieke parserbenchmark en SBOM-generatie. Bewijs staat in `docs/audit/`, de E2E-/auth-/runtime-/adaptertests en `USER_INPUT_REQUIRED.md`. - VR-102 is gerealiseerd met auditbare policyreviews (`SourcePolicyReview`), robotscache met TTL/size/SSRF-controles en fail-closed policy-gating op verlopen of conflicterende reviews. - VR-107 is gerealiseerd met `apps/sources/views.py`, `apps/sources/services/manual_import.py`, `templates/sources/list.html`, `static/js/manual_import.js` en bijhorende unit/integration-tests. diff --git a/tests/unit/test_deployment_configuration.py b/tests/unit/test_deployment_configuration.py index 4aabea0..28433cf 100644 --- a/tests/unit/test_deployment_configuration.py +++ b/tests/unit/test_deployment_configuration.py @@ -136,3 +136,11 @@ def test_release_uv_install_is_immutable_and_does_not_require_github_api() -> No assert "astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0" in workflow assert 'version: "0.11.12"' in workflow assert "astral-sh/setup-uv@v6" not in workflow + + +def test_release_is_the_only_gitea_deployment_trigger() -> None: + workflows = ROOT / ".gitea" / "workflows" + + assert not (workflows / "deploy.yml").exists() + release = yaml.safe_load((workflows / "release.yml").read_text(encoding="utf-8")) + assert release[True] == {"push": {"tags": ["v*.*.*"]}}