Compare commits

...
1 Commits
Author SHA1 Message Date
patrickandClaude Sonnet 5.5 f708d0ea87 ai-app: Deployment-Anbindung (Port 8900, Migrationswerkzeug, systemd-Template)
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Waiting to run
DB-Integrationstests / db-tests (pull_request) Waiting to run
Supply-Chain-Scan (Go) / govulncheck (pull_request) Waiting to run
Supply-Chain-Scan (npm) / npm-audit (pull_request) Waiting to run
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Waiting to run
AI-Pflichttest-Gate / pflichttest-gate (pull_request) Waiting to run
CI / Core-Modul (Build/Vet/Test) (pull_request) Waiting to run
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Waiting to run
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (pull_request) Waiting to run
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Waiting to run
CI / Web (dms-browser) (pull_request) Waiting to run
CI / Web (pdf-viewer) (pull_request) Waiting to run
CI / Web (shl) (pull_request) Waiting to run
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Waiting to run
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Successful in 3s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Waiting to run
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Failing after 15s
CI / Web (pdf-viewer) (push) Failing after 34s
Supply-Chain-Scan (Go) / govulncheck (push) Failing after 17s
CI / Core-Modul (Build/Vet/Test) (push) Successful in 42s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (push) Successful in 2s
CI / Web (dms-browser) (push) Failing after 53s
CI / Web (shl) (push) Failing after 7s
Standard-Port in den fuer ai reservierten Bereich verschoben (9600 belegt
workflow-app). Fortschrittstabelle ai_schema_migrations statt
connect_schema_migrations.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 23:07:51 +01:00
7 changed files with 172 additions and 2 deletions

No files matched your search

+1 -1
View File
@@ -49,7 +49,7 @@ import (
const workerPollInterval = 2 * time.Second
const defaultListenAddr = "127.0.0.1:9600"
const defaultListenAddr = "127.0.0.1:8900"
const defaultModel = "default"
type config struct {
+40
View File
@@ -0,0 +1,40 @@
// Command migrate wendet die offenen Postgres-Migrationen des AI-Moduls an.
// Einmal gegen eine frische Datenbank ausfuehren, bevor ai-app startet.
package main
import (
"context"
"log"
"os"
"github.com/jackc/pgx/v5/pgxpool"
"gitea.perlbach24.de/scripte/nexarch/ai/internal/migrate"
)
func main() {
dsn := os.Getenv("NEXARCH_AI_APP_TENANT_DSN")
if dsn == "" {
log.Fatal("migrate: NEXARCH_AI_APP_TENANT_DSN ist erforderlich")
}
dir := "migrations"
if len(os.Args) > 1 {
dir = os.Args[1]
}
ctx := context.Background()
pool, err := pgxpool.New(ctx, dsn)
if err != nil {
log.Fatalf("migrate: datenbankverbindung: %v", err)
}
defer pool.Close()
migrations, err := migrate.Load(dir)
if err != nil {
log.Fatalf("migrate: migrationen laden: %v", err)
}
applied, err := migrate.Up(ctx, pool, migrations)
if err != nil {
log.Fatalf("migrate: migrationen anwenden: %v", err)
}
log.Printf("migrate: %d migration(en) angewendet", len(applied))
}
+26
View File
@@ -0,0 +1,26 @@
# Betrieb: ai-app
Port-Bereich 8900 bis 8999 (`deploy/ports.tsv`), Routenpräfix `/api/ai/v1` (`deploy/routes.tsv`). Secrets gehören ausschließlich in die Umgebung (`/etc/nexarch/ai-app.env`, Rechte 600), nie in Code, Logs oder Dokumentation.
## Einrichten
1. Datenbank und Rolle anlegen (nicht Teil der Units).
2. `/etc/nexarch/ai-app.env` mit den Variablen unten anlegen.
3. Migrationen anwenden: `NEXARCH_AI_APP_TENANT_DSN=... ./migrate ai/migrations` (Fortschrittstabelle `ai_schema_migrations`).
4. Unit aus `deploy/systemd/nexarch-ai-app.service.tmpl` installieren (`__INSTALL_DIR__` ersetzen), `systemctl enable --now nexarch-ai-app`.
5. Health: `GET /healthz`.
Eine Datenbank, die vor ENG-10 mit den AI-Migrationen angelegt wurde, führte den Fortschritt in `connect_schema_migrations`. Ist sie ausschließlich AI-Datenbank, vor dem ersten Lauf `ALTER TABLE connect_schema_migrations RENAME TO ai_schema_migrations;`. Bei gemeinsamer Datenbank mit Connect nicht umbenennen, sondern die AI-Einträge von Hand in die neue Tabelle übertragen.
## Variablen
| Variable | Standard | Wirkung |
|---|---|---|
| `NEXARCH_AI_APP_TENANT_DSN` | Pflicht | Datenbank des AI-Moduls |
| `NEXARCH_AI_APP_SERVICE_TOKEN` | Pflicht | Service-Token, das alle Aufrufer im Header `X-Service-Token` senden |
| `NEXARCH_AI_APP_LISTEN_ADDR` | `127.0.0.1:8900` | Lauschadresse |
| `NEXARCH_AI_APP_MODEL` | `default` | Modellname für Inferenzaufrufe |
| `NEXARCH_AI_APP_POLICY_URL` / `_POLICY_SERVICE_TOKEN` | Pflicht, oder Übergangsschalter | Rechteprüfung der semantischen Suche; Token Pflicht, sobald URL gesetzt |
| `NEXARCH_AI_APP_POLICY_DISABLED` | aus | `1` startet ohne Rechteprüfung (Übergang, Warnlog) |
| `NEXARCH_AI_APP_MODULE_BASE_URLS` | leer | `modul=url,modul2=url2`, Basis-URLs für den Rückschreib-Push |
| `NEXARCH_AI_APP_CORE_BASE_URL` / `_CORE_SERVICE_TOKEN` | leer | Core-Anbindung; ohne sie ist die Objekttyp-Registrierung (GOV-01) deaktiviert |
+40
View File
@@ -0,0 +1,40 @@
# ENG-10 – ai-app: Deployment-Anbindung – Prüfprotokoll
Datum: 2026-11-06. Host: Entwicklungshost 192.168.1.204 (Wegwerf-Rolle/DB `eng10_test`, nach Abschluss entfernt).
## Befund (Ausgangslage)
- `ai-app` lauschte standardmäßig auf `127.0.0.1:9600`, außerhalb des für `ai` reservierten Bereichs 8900 bis 8999 (`deploy/ports.tsv`). 9600 belegt außerdem `workflow-app` (`docs/BETRIEB-PORTAL.md`). `scripts/check-port-defaults.sh` erkennt nur `":NNNN"`-Literale und meldete den Verstoß nicht.
- Kein systemd-Template für `ai-app` (Connect und DMS haben eines).
- Kein Migrationswerkzeug für das AI-Modul; `ai/internal/migrate` führte den Fortschritt in der Tabelle `connect_schema_migrations` (Kopierfehler aus dem Connect-Modul).
## Umsetzung
- Standard-Lauschadresse `127.0.0.1:8900`.
- `ai/cmd/migrate` (Muster `connect/cmd/migrate`, DSN aus `NEXARCH_AI_APP_TENANT_DSN`, Verzeichnis als Argument).
- Fortschrittstabelle `ai_schema_migrations`.
- `deploy/systemd/nexarch-ai-app.service.tmpl` nach dem Muster von `nexarch-connect-app` (gleiche Härtung).
- `ai/docs/BETRIEB-UMGEBUNGSVARIABLEN.md` mit Einrichtungsschritten und Variablen.
## Akzeptanzkriterien
| # | Kriterium | Ergebnis |
|---|---|---|
| 1 | Standard-Port liegt im Bereich von `ai` | **Erfüllt.** 8900. `scripts/check-port-defaults.sh` meldet für `ai/cmd/ai-app` nichts (dort erkennt es allerdings das Format `127.0.0.1:NNNN` ohnehin nicht, siehe Einschränkungen). |
| 2 | systemd-Template vorhanden und gehärtet | **Erfüllt.** `scripts/check-systemd-hardening.sh`: ok, 18 Dienste geprüft. Start der Unit selbst nicht getestet. |
| 3 | Migrationen anwendbar, idempotent | **Erfüllt.** Frische DB: 16 Migrationen angewendet, zweiter Lauf 0; ohne DSN klare Fehlermeldung, Abbruch. |
| 4 | Eigene Fortschrittstabelle | **Erfüllt.** `ai_schema_migrations` existiert, `connect_schema_migrations` nicht (`TestUp_UsesOwnTrackingTable`, echte DB). |
## Prüfungen vor der Abnahme
| # | Prüfung | Ergebnis |
|---|---|---|
| 1 | Migrationswerkzeug gegen frische Wegwerf-DB | **Bestanden** (siehe AK3). |
| 2 | Bestehende Modultests | **Bestanden.** `./internal/migrate/`, `./cmd/ai-app/` grün, `go build ./...` und `go vet` sauber. |
| 3 | Unit startet `ai-app` real | **Offen.** Template nicht auf einem Host installiert, `systemctl` nicht ausgeführt. |
## Einschränkungen
- Umbenennung der Fortschrittstabelle betrifft bestehende AI-Datenbanken; Hinweis in `BETRIEB-UMGEBUNGSVARIABLEN.md`. Eine automatische Übernahme gibt es nicht.
- Nicht Teil der Kachel: Aufnahme in `deploy/rollout-131-*.sh` (Host-131-Rollout), Provisionierung von DB, Rolle und Secrets.
- Weitere Port-Verstöße außerhalb von `ai`, die beim Prüfen auffielen und nicht behoben wurden: `workflow-app` und `tasks-a11y-fixture` auf 9600 (Bereich 8800 bis 8899), Connect-Fixtures doppelt auf 9500, mehrere Fixtures und `verapdf-sidecar` außerhalb ihrer Bereiche. `check-port-defaults.sh` erkennt `127.0.0.1:NNNN`-Literale nicht; eine Erweiterung würde diese Verstöße sichtbar machen und braucht eine eigene Entscheidung.
+1 -1
View File
@@ -12,7 +12,7 @@ import (
type Migration = cmigrate.Migration
const TrackingTable = "connect_schema_migrations"
const TrackingTable = "ai_schema_migrations"
var migrator = cmigrate.MustNew(TrackingTable)
+41
View File
@@ -0,0 +1,41 @@
package migrate
import (
"context"
"os"
"path/filepath"
"testing"
"gitea.perlbach24.de/scripte/nexarch/common/testdb"
)
func TestUp_UsesOwnTrackingTable(t *testing.T) {
dsn := os.Getenv("TEST_TENANT_DSN")
if dsn == "" {
t.Skip("TEST_TENANT_DSN nicht gesetzt, Integrationstest uebersprungen")
}
pool := testdb.Pool(t, dsn)
dir, err := filepath.Abs("../../migrations")
if err != nil {
t.Fatal(err)
}
migrations, err := Load(dir)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
if _, err := Up(ctx, pool, migrations); err != nil {
t.Fatal(err)
}
again, err := Up(ctx, pool, migrations)
if err != nil || len(again) != 0 {
t.Fatalf("zweiter lauf soll nichts anwenden: %v %v", again, err)
}
var own, foreign bool
if err := pool.QueryRow(ctx, `SELECT to_regclass('ai_schema_migrations') IS NOT NULL, to_regclass('connect_schema_migrations') IS NOT NULL`).Scan(&own, &foreign); err != nil {
t.Fatal(err)
}
if !own || foreign {
t.Fatalf("fortschrittstabelle: ai=%v connect=%v", own, foreign)
}
}
@@ -0,0 +1,23 @@
[Unit]
Description=NEXARCH AI - ai-app (ENG-10)
After=network.target postgresql.service
Wants=postgresql.service
[Service]
Type=simple
User=nexarch
Group=nexarch
EnvironmentFile=/etc/nexarch/ai-app.env
ExecStart=__INSTALL_DIR__/bin/ai-app
Restart=on-failure
RestartSec=3
WorkingDirectory=__INSTALL_DIR__
# QA-15 F-13/OPS-08: reiner Netzwerkdienst (HTTP + Postgres-DSN aus
# EnvironmentFile), keine eigene Schreibarbeit im Dateisystem.
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target