diff --git a/README.md b/README.md index 58c5084..41053bc 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ The [continuous analysis worker](docs/analysis-worker.md) consumes that inbox, routes cases to configured projects and resumes checkpointed analysis after restart. The [web console](docs/web-console.md) shows active workers, case progress and token charts, with corrections accounted for separately, and accepts new/return submissions. +The [Linux deployment guide](docs/linux-deployment.md) covers systemd services, +protected credentials, HTTPS and non-billable readiness checks. Requires Python 3.11 or newer. diff --git a/deploy/install.py b/deploy/install.py new file mode 100644 index 0000000..1d8a8ec --- /dev/null +++ b/deploy/install.py @@ -0,0 +1,104 @@ +"""Install the fixed Linux service layout. Never enables or starts services.""" +import argparse +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import tomllib + +ROOT = Path(__file__).resolve().parents[1] +CONFIG = Path('/etc/viano-bug-analysis') +STATE = Path('/var/lib/viano-bug-analysis') +SOURCE = Path('/srv/viano-bug-analysis') + + +def project_template(root=ROOT): + text=(root/'config/project.example.toml').read_text(encoding='utf-8') + replacements={ + 'path = "../service-data/research/viano-core"':'path = "/srv/viano-bug-analysis/viano-core"', + 'port = 33079':'port = 3306', + 'password_file = "../service-data/research/world-db/reader.local.txt"':'password_env = "VIANO_DB_PASSWORD"', + } + for old,new in replacements.items(): + if text.count(old)!=1:raise ValueError('Deployment template changed; review replacements') + text=text.replace(old,new) + import re + text=re.sub(r'^commit = "[0-9a-f]{40}"$', 'commit = "'+'0'*40+'"',text,flags=re.M) + text=re.sub(r'^snapshot_id = .*$', 'snapshot_id = "CONFIGURE_ME"',text,flags=re.M) + tomllib.loads(text) + return text + + +def templates(root=ROOT): + files={name:(root/'deploy/linux'/name).read_text(encoding='utf-8') + for name in ('api.toml','worker.toml','gitconfig','nginx.conf.example')} + for target,source in { + 'llm.toml':'llm.gemini-cache.example.toml', + 'investigation.bounded.toml':'investigation.bounded.toml', + 'budget.human-tests.toml':'budget.human-tests.toml', + 'project-relations.vcore.toml':'project-relations.vcore.toml', + 'telemetry.toml':'telemetry.example.toml', + }.items():files[target]=(root/'config'/source).read_text(encoding='utf-8') + files['project.toml']=project_template(root) + for name in ('api','worker'): + files[name+'.env']=(root/'deploy/linux'/(name+'.env.example')).read_text(encoding='utf-8') + return files + + +def main(argv=None): + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('--dry-run',action='store_true') + args=parser.parse_args(argv) + files=templates() + if args.dry_run: + print(json.dumps({'status':'PLAN','code':'/opt/viano-bug-analysis', + 'config':str(CONFIG),'state':str(STATE),'source':str(SOURCE), + 'config_files':sorted(files),'existing_files':'preserved','services_started':False})) + return 0 + if sys.platform!='linux' or os.geteuid()!=0: + parser.error('Installation requires Linux and root; use --dry-run elsewhere') + if ROOT!=Path('/opt/viano-bug-analysis') or ROOT.is_symlink(): + parser.error('Place a root-owned checkout at /opt/viano-bug-analysis first') + # Root executes packaging code: reject an untrusted checkout/venv. + for base in (ROOT,ROOT/'src',ROOT/'deploy',ROOT/'config',ROOT/'pyproject.toml',ROOT/'.venv'): + if not base.exists():continue + paths=[base,*base.rglob('*')] if base.is_dir() and base!=ROOT else [base] + for path in paths: + stat=path.stat() + if path.is_symlink() and base!=ROOT/'.venv':raise RuntimeError('Symlink in installation sources') + if stat.st_uid!=0 or stat.st_mode&0o022:raise RuntimeError('Installation sources must be root-owned and not group/world writable') + def run(*command):subprocess.run(command,check=True) + import grp + import pwd + try:group=grp.getgrnam('buganalysis') + except KeyError: + run('groupadd','--system','buganalysis');group=grp.getgrnam('buganalysis') + try:user=pwd.getpwnam('buganalysis') + except KeyError: + run('useradd','--system','--gid','buganalysis','--home-dir',str(STATE),'--no-create-home','--shell','/usr/sbin/nologin','buganalysis') + user=pwd.getpwnam('buganalysis') + if user.pw_uid==0 or user.pw_gid!=group.gr_gid:raise RuntimeError('Unexpected service account identity') + for path,uid in ((CONFIG,0),(CONFIG/'auth',0),(STATE,user.pw_uid),(SOURCE,0)): + if path.is_symlink():raise RuntimeError('Symlink in deployment directories') + path.mkdir(parents=True,exist_ok=True) + os.chown(path,uid,group.gr_gid);os.chmod(path,0o750) + def install(path,text,mode): + if path.is_symlink():raise RuntimeError('Symlink at deployment target') + if path.exists():return + with path.open('x',encoding='utf-8',newline='\n') as stream:stream.write(text) + os.chown(path,0,group.gr_gid);os.chmod(path,mode) + for name,text in files.items():install(CONFIG/name,text,0o600 if name.endswith('.env') else 0o640) + for role in ('api','worker'): + name=f'viano-bug-analysis-{role}.service' + install(Path('/etc/systemd/system')/name,(ROOT/'deploy'/name).read_text(encoding='utf-8'),0o644) + if not (ROOT/'.venv/bin/python').exists():run(sys.executable,'-m','venv',str(ROOT/'.venv')) + run(str(ROOT/'.venv/bin/python'),'-m','pip','install',str(ROOT)+'[api,research]') + run('systemctl','daemon-reload') + print(json.dumps({'status':'INSTALLED','existing_files':'preserved','services_started':False, + 'next':'Configure project, secrets, source checkout, web user and HTTPS; run readiness checks before enabling services'})) + return 0 + + +if __name__=='__main__':raise SystemExit(main()) diff --git a/deploy/linux/api.env.example b/deploy/linux/api.env.example new file mode 100644 index 0000000..844cbdd --- /dev/null +++ b/deploy/linux/api.env.example @@ -0,0 +1,3 @@ +# Read by systemd as root, not by the web user. chmod 0600, owner root. +# Configure once; never commit a filled copy. +# VBA_INTAKE_TOKEN="replace-with-a-random-32-to-256-character-base64url-token" diff --git a/deploy/linux/api.toml b/deploy/linux/api.toml new file mode 100644 index 0000000..395def6 --- /dev/null +++ b/deploy/linux/api.toml @@ -0,0 +1,25 @@ +[api] +workspace = "/var/lib/viano-bug-analysis" +host = "127.0.0.1" +port = 8080 + +[auth] +users_database = "/etc/viano-bug-analysis/auth/users.sqlite3" +session_seconds = 3600 + +[[projects]] +id = "viano-core" +type = "wow" +profile = "vcore" +adapter = "vcore" +planning_mode = "joint" +[projects.settings] +project_config = "project.toml" +llm_config = "llm.toml" +investigation_config = "investigation.bounded.toml" +budget_config = "budget.human-tests.toml" + +[[clients]] +id = "wow-capture-worker" +token_env = "VBA_INTAKE_TOKEN" +projects = ["viano-core"] diff --git a/deploy/linux/gitconfig b/deploy/linux/gitconfig new file mode 100644 index 0000000..0c68b07 --- /dev/null +++ b/deploy/linux/gitconfig @@ -0,0 +1,2 @@ +[safe] + directory = /srv/viano-bug-analysis/viano-core diff --git a/deploy/linux/nginx.conf.example b/deploy/linux/nginx.conf.example new file mode 100644 index 0000000..f4e5ea0 --- /dev/null +++ b/deploy/linux/nginx.conf.example @@ -0,0 +1,29 @@ +# Replace domain and certificate paths. Install manually after nginx -t. +# Root path on its own HTTPS hostname; upstream must remain loopback-only. +server { + listen 80; + server_name buganalysis.example.org; + return 308 https://buganalysis.example.org$request_uri; +} +server { + listen 443 ssl; + server_name buganalysis.example.org; + ssl_certificate /etc/letsencrypt/live/buganalysis.example.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/buganalysis.example.org/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + client_max_body_size 768m; + client_body_timeout 120s; + location / { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Real-IP $remote_addr; + proxy_request_buffering off; + proxy_read_timeout 180s; + proxy_send_timeout 180s; + proxy_cache off; + proxy_no_cache 1; + } +} diff --git a/deploy/linux/worker.env.example b/deploy/linux/worker.env.example new file mode 100644 index 0000000..f779f60 --- /dev/null +++ b/deploy/linux/worker.env.example @@ -0,0 +1,3 @@ +# Read by systemd as root. chmod 0600, owner root. No shell export statements. +# GEMINI_API_KEY="your-cloud-key" +# VIANO_DB_PASSWORD="your-read-only-database-password" diff --git a/deploy/linux/worker.toml b/deploy/linux/worker.toml new file mode 100644 index 0000000..2f38c70 --- /dev/null +++ b/deploy/linux/worker.toml @@ -0,0 +1,7 @@ +[worker] +intake_config = "api.toml" +poll_seconds = 5 +error_retry_seconds = 60 +max_inbox_per_pass = 20 +min_free_bytes = 536870912 +telemetry_config = "telemetry.toml" diff --git a/deploy/viano-bug-analysis-api.service b/deploy/viano-bug-analysis-api.service new file mode 100644 index 0000000..c8ce556 --- /dev/null +++ b/deploy/viano-bug-analysis-api.service @@ -0,0 +1,36 @@ +[Unit] +Description=Viano BugAnalysis web and intake API +After=network-online.target +Wants=network-online.target +StartLimitIntervalSec=0 + +[Service] +Type=simple +User=buganalysis +Group=buganalysis +WorkingDirectory=/var/lib/viano-bug-analysis +EnvironmentFile=/etc/viano-bug-analysis/api.env +Environment=PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 +ExecStartPre=/opt/viano-bug-analysis/.venv/bin/python -m viano_bug_analysis.deployment check --role api --config /etc/viano-bug-analysis/api.toml +ExecStart=/opt/viano-bug-analysis/.venv/bin/python -m viano_bug_analysis.api serve --config /etc/viano-bug-analysis/api.toml +Restart=always +RestartSec=10 +KillSignal=SIGTERM +TimeoutStartSec=60 +TimeoutStopSec=180 +UMask=0077 +NoNewPrivileges=true +ProtectSystem=strict +ReadWritePaths=/var/lib/viano-bug-analysis +ProtectHome=true +PrivateTmp=true +PrivateDevices=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictSUIDSGID=true +CapabilityBoundingSet= +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 + +[Install] +WantedBy=multi-user.target diff --git a/deploy/viano-bug-analysis-worker.service b/deploy/viano-bug-analysis-worker.service index f94cef9..169bbf2 100644 --- a/deploy/viano-bug-analysis-worker.service +++ b/deploy/viano-bug-analysis-worker.service @@ -2,21 +2,36 @@ Description=Viano BugAnalysis analysis worker After=network-online.target Wants=network-online.target +StartLimitIntervalSec=0 [Service] Type=simple User=buganalysis Group=buganalysis -WorkingDirectory=/opt/viano-bug-analysis +WorkingDirectory=/var/lib/viano-bug-analysis EnvironmentFile=/etc/viano-bug-analysis/worker.env -ExecStart=/opt/viano-bug-analysis/.venv/bin/python -m viano_bug_analysis.worker run --config /opt/viano-bug-analysis/config/worker.local.toml -Restart=on-failure +Environment=PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 GIT_CONFIG_GLOBAL=/etc/viano-bug-analysis/gitconfig GIT_TERMINAL_PROMPT=0 +ExecStartPre=/opt/viano-bug-analysis/.venv/bin/python -m viano_bug_analysis.deployment check --role worker --config /etc/viano-bug-analysis/worker.toml +ExecStart=/opt/viano-bug-analysis/.venv/bin/python -m viano_bug_analysis.worker run --config /etc/viano-bug-analysis/worker.toml +Restart=always RestartSec=10 KillSignal=SIGTERM # Finish the current checkpointed dispatch; operator can still force-stop. TimeoutStopSec=infinity +TimeoutStartSec=300 UMask=0077 NoNewPrivileges=true +ProtectSystem=strict +ReadWritePaths=/var/lib/viano-bug-analysis +ProtectHome=true +PrivateTmp=true +PrivateDevices=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictSUIDSGID=true +CapabilityBoundingSet= +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 [Install] WantedBy=multi-user.target diff --git a/docs/linux-deployment.md b/docs/linux-deployment.md new file mode 100644 index 0000000..bf84c4a --- /dev/null +++ b/docs/linux-deployment.md @@ -0,0 +1,154 @@ +# Linux-Betrieb + +API und Analyseworker laufen als getrennte systemd-Dienste. Ein Auftragseingang +weckt keinen neuen Prozess: Der dauerhaft laufende Worker übernimmt Inbox-Aufträge. +Die Weboberfläche ist eine alternative Bedienung desselben Arbeitsablaufs. +Die Installation startet keine Dienste und führt keine Modellanfrage aus. + +## Voraussetzungen und Verzeichnisse + +Linux mit systemd, Python 3.11 oder neuer inklusive venv/pip, Git und Nginx. +Diese Programme über die Paketverwaltung des Servers installieren. Die aktuelle +Projektvorlage ist für WoW/Vcore eingerichtet; sie ist kein allgemeiner Adapter. + +| Pfad | Zweck und Rechte | +| --- | --- | +| `/opt/viano-bug-analysis` | Root-eigener Checkout und virtuelle Python-Umgebung; Dienst darf nur lesen | +| `/etc/viano-bug-analysis` | Root-eigene Konfiguration, Gruppe `buganalysis`, Verzeichnis 0750, Dateien 0640 | +| `/etc/viano-bug-analysis/*.env` | Zugangsdaten, root:buganalysis 0600; systemd liest sie vor dem Benutzerwechsel | +| `/etc/viano-bug-analysis/auth/users.sqlite3` | Root-verwaltete Nutzer, Gruppe `buganalysis` darf lesen | +| `/var/lib/viano-bug-analysis` | Dienst-eigene Fallakten, Laufzeitdaten und Websessions | +| `/srv/viano-bug-analysis/viano-core` | Root-eigener Source-Checkout, Dienst hat Leserechte | + +Die env-Dateien sind durch Betriebssystemrechte geschützte Klartextdateien, kein +verschlüsselter Tresor. Sie gehören nicht ins Repository oder in Diagnosepakete. +API und Worker erhalten unterschiedliche Dateien: Nur der Worker erhält Cloud- +und DB-Zugangsdaten. Die Diensthärtung und EnvironmentFile-Nutzung folgen der +[systemd-Dokumentation](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html). + +## Installation + +Den vertrauenswürdigen BugAnalysis-Checkout als root unter +`/opt/viano-bug-analysis` ablegen. Code und Unterverzeichnisse dürfen nicht für +andere Benutzer schreibbar sein. Dann: + +```sh +cd /opt/viano-bug-analysis +python3 deploy/install.py --dry-run +sudo python3 deploy/install.py +``` + +Der Installer legt den Dienstbenutzer `buganalysis`, Konfigurationen, venv und +Units an. Er installiert die Python-Abhängigkeiten und lädt systemd neu. +Bestehende Zielkonfigurationen und Units werden erhalten. Updates an bereits +installierten Units daher bewusst mit den versionierten Vorlagen abgleichen. +Bei Installationsfehlern bleiben vorhandene Dateien erhalten; erneutes Ausführen +setzt die Einrichtung fort. Der Installer verändert keine bestehende Datenbank. + +## Einmalige Konfiguration + +Mit `sudoedit` die Dateien unter `/etc/viano-bug-analysis` bearbeiten: + +1. `project.toml`: DB-Adresse/Port, Datenbank und nur lesenden Benutzer einstellen, + tatsächliche Snapshotkennung und vollständigen Source-Commit eintragen. + Die ausgelieferten Null-Commit- und `CONFIGURE_ME`-Werte sind Platzhalter. +2. Den Vcore-Checkout als root unter `/srv/viano-bug-analysis/viano-core` anlegen; + der konfigurierte Commit muss lokal vorhanden und für `buganalysis` lesbar sein. + Der Dienst lädt keine Commits nach. `gitconfig` vertraut ausschließlich diesem + konkreten Root-Checkout, nicht beliebigen Repositories. +3. `worker.env`: `GEMINI_API_KEY=...` und `VIANO_DB_PASSWORD=...` eintragen. + Keine `export`-Anweisungen verwenden. Für Sonderzeichen gelten die + EnvironmentFile-Quotingregeln von systemd. Werte nicht als Shellargumente übergeben. +4. `api.env`: `VBA_INTAKE_TOKEN=...` für den Erfassungsworker setzen + (32–256 Zeichen aus Buchstaben, Ziffern, `_` und `-`). Dies ist ein separater + Integrationsschlüssel, kein Gemini-Key. Wird keine externe API-Integration + benötigt, den zugehörigen `[[clients]]`-Block aus `api.toml` entfernen. +5. `llm.toml`: gewünschtes Modell, Anbieter und Cacheeinstellungen kontrollieren. + Investigation-, Budget- und Telemetrie-Dateien sind separat konfigurierbar. + +Die DB-Verbindung benötigt einen Benutzer mit ausschließlich passenden +SELECT-Rechten auf die freigegebenen World-Tabellen. Diese Rechte legt der +DB-Administrator an; BugAnalysis vergibt keine Grants. Der Readiness-Test belegt +Lesbarkeit, nicht die Abwesenheit zusätzlicher Schreibrechte. Für entfernte DBs +einen geschützten Netzwerkweg verwenden: Der aktuelle DB-Provider konfiguriert +selbst kein TLS. Zugang zu Produktionsdaten ist für die lokale Entwicklung +weiterhin nicht erforderlich. + +Webnutzer anlegen; das Passwort wird verdeckt abgefragt: + +```sh +sudo /opt/viano-bug-analysis/.venv/bin/python -m viano_bug_analysis.api create_user NAME --config /etc/viano-bug-analysis/api.toml --service-group buganalysis +``` + +Nutzerverwaltung bleibt root vorbehalten. Siehe [Nutzerverwaltung](user-accounts.md). +Keine Nutzer werden automatisch angelegt. Nach der Installation besitzen die +env-Dateien Modus 0600; diese Rechte nach manuellen Änderungen erhalten. + +## Kostenfreie Bereitschaftsprüfung + +Die Prüfung unter der tatsächlichen Dienstidentität samt env-Datei ausführen: + +```sh +sudo systemd-run --wait --pipe --collect --property=User=buganalysis --property=Group=buganalysis --property=EnvironmentFile=/etc/viano-bug-analysis/api.env /opt/viano-bug-analysis/.venv/bin/python -m viano_bug_analysis.deployment check --role api --config /etc/viano-bug-analysis/api.toml +sudo systemd-run --wait --pipe --collect --property=User=buganalysis --property=Group=buganalysis --property=EnvironmentFile=/etc/viano-bug-analysis/worker.env --setenv=GIT_CONFIG_GLOBAL=/etc/viano-bug-analysis/gitconfig /opt/viano-bug-analysis/.venv/bin/python -m viano_bug_analysis.deployment check --role worker --config /etc/viano-bug-analysis/worker.toml +``` + +Erwartet: `READY`, `model_requests: 0`. Geprüft werden Konfiguration, +Zugangsdaten-Präsenz, Speicherreserve, Schreibbarkeit der Laufzeitablage, +Webnutzer/Sessions beziehungsweise Git-Objekte und die freigegebenen DB-Schemata. +Cloud-Key-Gültigkeit und Modellverfügbarkeit sind damit noch nicht getestet. +`--offline` überspringt Git-/DB-Zugriffe und meldet nur `CONFIG_READY`. +Fehlerausgaben enthalten kontrollierte Fehlercodes statt Zugangsdaten. + +## HTTPS und Start + +Die API bleibt auf `127.0.0.1:8080`. `nginx.conf.example` enthält eine +Reverse-Proxy-Vorlage: Domain und Zertifikatpfade anpassen, gültiges Zertifikat +bereitstellen und als Nginx-Site installieren. Die Vorlage übernimmt die +Forwarded-Header kontrolliert; die API vertraut ihnen ausschließlich vom +Loopback-Proxy. So verwenden Anmeldung und Cookies das öffentliche HTTPS-Schema. +Die Proxyparameter sind in der +[Nginx-Dokumentation](https://nginx.org/en/docs/http/ngx_http_proxy_module.html) beschrieben. + +Vor dem ersten Start auf dem Linux-Zielsystem prüfen: + +```sh +sudo systemd-analyze verify /etc/systemd/system/viano-bug-analysis-api.service /etc/systemd/system/viano-bug-analysis-worker.service +sudo nginx -t +sudo systemctl reload nginx +sudo systemctl enable --now viano-bug-analysis-api.service viano-bug-analysis-worker.service +sudo systemctl status viano-bug-analysis-api.service viano-bug-analysis-worker.service +``` + +Sobald der Worker aktiviert ist, kann er vorhandene Aufträge verarbeiten und +kostenpflichtige Modellaufrufe senden. Erst mit leerem Eingang starten, dann einen +kontrollierten Feldtest über die Übergabe-API oder Weboberfläche übergeben. +Anmeldung, Auftragsannahme, Fortschritt, menschliche Testanfrage und Outbox dabei +gemeinsam prüfen. Die automatisierte Einrichtung ersetzt diesen Feldtest nicht. + +## Laufender Betrieb + +Die Dienste starten beim Booten und nach unerwartetem Prozessende erneut. +Fehlende Startvoraussetzungen werden nach zehn Sekunden erneut geprüft. +Externe DB und Sourcebereitstellung werden nicht von diesen Units installiert. +API und Worker können unabhängig voneinander laufen. + +```sh +sudo journalctl -u viano-bug-analysis-api -u viano-bug-analysis-worker -f +sudo systemctl stop viano-bug-analysis-worker +``` + +Der Worker beendet beim Stoppen den aktuellen Dispatch kontrolliert; seine Unit +setzt dafür kein hartes Stop-Zeitlimit. Ein Stop kann daher während einer +langlaufenden Anfrage dauern. Für Code-/Profiländerungen den Worker zuerst +anhalten. Profilbindungen laufender Fälle nicht beiläufig ändern: Unterschiede +werden als Aufmerksamkeit erfordernder Zustand behandelt, Budgets nicht erneuert. +Codeänderungen danach in die root-eigene venv installieren und Dienste neu starten. + +Backups umfassen die Fallablage, Konfiguration einschließlich geschützter Secrets +und Nutzer-SQLite. Für eine einfache konsistente Dateisicherung beide Dienste +anhalten. Sessions sind keine dauerhaft nötigen Fachdaten. Produktive Fallaufbewahrung +separat festlegen; das 20-GB-Entwicklungslimit ist kein Produktionslimit. + +Unter Windows bleibt die vorhandene PowerShell-Umgebungsvariable für Entwicklung +unterstützt. Dort installiert dieser Installer keine Dienste; `--dry-run` ist möglich. diff --git a/docs/test-results/linux-deployment-20261022.md b/docs/test-results/linux-deployment-20261022.md new file mode 100644 index 0000000..4bbe38a --- /dev/null +++ b/docs/test-results/linux-deployment-20261022.md @@ -0,0 +1,31 @@ +# Linux-Betriebspaket – lokale Prüfung 2026-10-22 + +Ziel: getrennte API-/Worker-Dienste, persistente geschützte Zugangsdaten, +Installation ohne automatische Aktivierung und Bereitschaftsprüfung ohne LLM. + +Änderungen: `deploy/install.py`, Linux-Konfigurationsvorlagen, beide systemd-Units, +`viano_bug_analysis.deployment` sowie explizite Loopback-Proxy-Vertrauensgrenze +beim API-Start. Anleitung: `docs/linux-deployment.md`. + +Validierung auf Windows mit Python aus `.venv`: + +```text +python -m pytest tests/test_deployment.py tests/test_web_auth.py tests/test_worker.py tests/profiles/vcore/test_worker_intake.py -q +31 passed, 1 dependency deprecation warning +git diff --check: keine Whitespacefehler +``` + +Prüfumfang: Installer-Dry-Run ohne Seiteneffekte, TOML-Vorlagen und Dateiverweise, +getrennte env-Dateien und Unit-Härtung, API-Start mit/ohne erforderliche +Zugangsdaten, Worker-Leseprüfung mit kontrollierten Git-/DB-Testdoubles, +Offline-Modus, keine Modellanfrage, sichere Fehlerausgabe, HTTPS-Login-Cookie +über Uvicorns Proxy-Middleware, bestehende Nutzer-/Worker-/Intake-Tests. + +Vor den Tests: Trial-Workspaces 16.939.129.428 Bytes, kuratiertes Fehlerkorpus +2.471.041 Bytes. Nur kleine synthetische Tempdateien; keine Paketkopien oder neuen +LLM-Trials. Das Entwicklungsbudget von 20 GB bleibt eingehalten. + +Keine Cloud-/DB-Zugangsdaten im Bericht. Keine realen LLM-Aufrufe oder neuen +Abrechnungskosten. Keine Installation, Dienstaktivierung oder Serveränderung. +Native systemd-/Nginx-Prüfung und produktiver End-to-End-Test stehen aus: +Auf dieser Windows-Umgebung ist kein nutzbares Linux/systemd vorhanden. diff --git a/src/viano_bug_analysis/api/__main__.py b/src/viano_bug_analysis/api/__main__.py index a69358b..2aa98ac 100644 --- a/src/viano_bug_analysis/api/__main__.py +++ b/src/viano_bug_analysis/api/__main__.py @@ -47,7 +47,8 @@ def main(): return 0 import uvicorn from .app import create_app - uvicorn.run(create_app(config), host=config.host, port=config.port, access_log=False) + uvicorn.run(create_app(config), host=config.host, port=config.port, access_log=False, + proxy_headers=True, forwarded_allow_ips='127.0.0.1') return 0 except APIError as error: print(json.dumps({'status': 'ERROR', 'code': error.code})) diff --git a/src/viano_bug_analysis/deployment.py b/src/viano_bug_analysis/deployment.py new file mode 100644 index 0000000..6e13fec --- /dev/null +++ b/src/viano_bug_analysis/deployment.py @@ -0,0 +1,68 @@ +"""Non-billable readiness checks for supervised Linux services.""" +import argparse +import json +import re +import shutil +import tempfile + +from .api.config import APIConfig,APIError,directory +from .api.sessions import Sessions +from .api.users import database +from .worker.config import WorkerConfig,load_project + + +def check(role,path,*,offline=False,loader=load_project): + checks=[] + try: + if role not in {'api','worker'}:raise APIError('DEPLOY_ROLE') + worker=WorkerConfig.load(path) if role=='worker' else None + config=worker.intake if worker else APIConfig.load(path) + if config.host not in {'127.0.0.1','::1','localhost'}: + raise APIError('DEPLOY_LOOPBACK_REQUIRED') + workspace=directory(config.workspace) + reserve=worker.min_free_bytes if worker else config.limits.min_free_bytes + if shutil.disk_usage(workspace).free