Files
insight-updater/AGENTS.md
T
B0rbor4d cc4c3fcecb Hub-and-Spoke Umbau: Multi-Tenant Zentrale + Satellite-Agent
- Backend: Customer/Satellite Models, customer_id auf Server/Job/Audit
- Satellite-API: heartbeat, poll (atomares Claiming), logs, result,
  scan-result, health-report - Auth via X-Api-Key (SHA-256 gehasht)
- Job-Queue: pending/claimed/running/success/failed + Stale-Janitor
- Batch-Trigger: ein Job pro Server, Satellite arbeitet sequenziell ab
- Credentials bleiben lokal: nur symbolische credential_ref zentral
- Neues Paket satellite/: Pull-Loop, WinRM/SSH/CAU/Scanner, PyInstaller-tauglich
- Frontend: Kunden-Switcher, Satelliten-View, Polling statt WebSocket
- Entfernt: WebSocket/Socket.io, Redis, zentrale Credentials, JobRunner
- Docs: README/AGENTS/PROMPT auf neue Architektur aktualisiert
2026-08-07 03:42:06 +00:00

5.4 KiB

Insight Updater - Agent Orientation

Project Purpose

Zentrale, mandantenfaehige Update-Orchestrierung (Hub-and-Spoke). Zentrale (Docker) + Satelliten beim Kunden (Binary, kein Docker). Satelliten pollen Jobs, fuehren sie lokal im Kundennetz aus (WinRM/SSH/CAU/Scan) und melden Ergebnisse zurueck.

Quick Start

# Zentrale lokal
cd ~/projects/insight-updater
docker compose up -d --build

# Backend only
cd backend && pip install -e . && uvicorn app.main:app --reload

# Frontend only
cd frontend && npm install && npm run dev

# Satellite (Dev, gegen lokale Zentrale)
cd satellite && pip install -e .
cp config.example.yaml config.yaml  # api_key eintragen
insight-satellite

Architecture Overview

Kundennetz                          Zentrale
┌────────────┐    outbound HTTPS   ┌──────────────────┐
│ Satellite  │ ──────────────────▶ │ FastAPI Backend  │
│ (pollt     │   X-Api-Key Auth    │  /api/satellite  │
│  alle 30s) │ ◀────────────────── │  /api (Dashboard │
└────────────┘   Jobs (claimed)    │   JWT) + DB      │
     │ WinRM/SSH/CAU lokal         └──────────────────┘
     ▼                                      ▲
 Server im Kundennetz              Vue 3 Frontend (JWT, Kunden-Switcher)

Key Directories

Pfad Zweck
backend/app/api/routes/satellite_api.py Agent-API: heartbeat, poll, logs, result, scan-result, health-report
backend/app/api/routes/ Dashboard-REST: auth, customers, satellites, servers, updates, audit
backend/app/models/ Customer, Satellite, Server, UpdateJob, UpdateLog, AuditLog, User
backend/app/services/janitor.py Markiert stale Jobs (Satellite meldet nicht mehr) als failed
satellite/satellite/runner.py Main-Loop: heartbeat, poll, execute, report
satellite/satellite/winrm_exec.py Windows Update via pywinrm
satellite/satellite/ssh_exec.py Linux Update via asyncssh
satellite/satellite/cau_exec.py Invoke-CauRun via WinRM
satellite/satellite/scanner.py Ping-Sweep + Port-Probe (5985/22)
frontend/src/stores/ Pinia: auth, customers, satellites, servers, updates

Core Models

Model Beschreibung
Customer Tenant: name, slug
Satellite Agent beim Kunden: api_key_hash, last_seen, version, hostname
Server Inventar pro Kunde: hostname, type, credential_ref (symbolisch!)
UpdateJob customer_id, server_id (null bei Scan), satellite_id, status, params (JSON)
UpdateLog Log-Zeilen pro Job (Batch-Upload vom Satellite)
AuditLog Wer, wann, was - mit customer_id
User Dashboard-User (lokal oder LDAP-Stub)

Job Lifecycle (Pull-Modell)

pending -> claimed (beim Poll) -> running (erster Log-Push) -> success | failed
                                                                 | cancelled (nur aus pending)
  • Claiming passiert atomar im Poll (with_for_update), zwei Satelliten eines Kunden bekommen nie denselben Job
  • Janitor (services/janitor.py, alle 60s): claimed/running ohne Report seit JOB_STALE_TIMEOUT (default 3600s) -> failed
  • Cancel nur moeglich solange pending

API Endpoints (Dashboard, JWT)

Method Path Beschreibung
POST /api/auth/login Login
GET/POST/PATCH/DELETE /api/customers[/{id}] Kunden CRUD
GET/POST/DELETE /api/satellites[/{id}] Satelliten CRUD
POST /api/satellites/{id}/rotate-key Neuer API-Key
GET/POST/PATCH/DELETE /api/servers[/{id}] Inventar (customer_id scoped)
POST /api/updates/trigger Einzelner Job
POST /api/updates/trigger-batch Ein Job pro Server
GET /api/updates/{id}/logs Job-Logs
GET /api/audit Audit (filterbar per customer_id)

API Endpoints (Satellite, X-Api-Key)

Method Path Beschreibung
POST /api/satellite/heartbeat Lebenszeichen + Version
GET /api/satellite/poll Pending Jobs claimen + abholen
POST /api/satellite/logs Log-Batch + Progress
POST /api/satellite/result Abschluss success/failed
POST /api/satellite/scan-result Gefundene Hosts (legt Server an)
POST /api/satellite/health-report Health-Ergebnis pro Server

Satellite-Deployment beim Kunden

  • Binary via PyInstaller: pyinstaller --onefile satellite/runner.py
  • config.yaml: central_url, api_key, poll_interval
  • credentials.yaml: WinRM/SSH Zugangsdaten (bleiben lokal!)
  • Start: Scheduled Task (Windows) oder systemd (Linux), siehe satellite/README.md
  • 1-2 Stueck pro Kunde reichen - steuern das ganze Netz

Conventions

  • Sprache: Deutsch fuer User-facing Text, Englisch fuer Code/Kommentare
  • Keine Umlaute in .ps1/Python-Dateien (ae, oe, ue, ss)
  • Keine Credentials zentral - nur credential_ref Strings
  • Async: Backend vollstaendig async; Satellite async mit to_thread fuer pywinrm
  • Types: Strict mypy, Pydantic v2, SQLAlchemy 2.0

Deployment

Target Zentrale: monitoring (10.0.2.105) Reverse Proxy: Traefik (Docker labels) Git Remote: ssh://git@gitea.insight-it.de:2222/b0rbor4d/insight-updater.git Wichtig: Zentrale muss von Kundenstandorten aus per HTTPS (443) erreichbar sein.