Close 0.3.17 operational follow-ups

This commit is contained in:
Jens
2026-08-12 15:12:51 +02:00
parent 4822ee6880
commit 7b6f6d43fc
9 changed files with 108 additions and 97 deletions
-62
View File
@@ -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
+4
View File
@@ -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
+31
View File
@@ -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.
+14 -1
View File
@@ -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
+31 -32
View File
@@ -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://<runner-host>:1226/health/ready/`
- `http://<runner-host>: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.
+17
View File
@@ -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:<versie>@sha256:<digest>` 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
+1 -1
View File
@@ -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` |
+2 -1
View File
@@ -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-209224/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.
@@ -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*.*.*"]}}