Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions .github/workflows/quality-portwing-fleet-soak.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
name: "⏱️ Quality: Portwing Fleet Soak"
run-name: >-
${{
github.event_name == 'schedule' && '⏱️ Portwing Fleet Soak — Weekly' ||
github.event_name == 'pull_request' && format('⏱️ Portwing Fleet Soak — PR #{0}', github.event.pull_request.number) ||
format('⏱️ Portwing Fleet Soak — Manual by {0}', github.actor)
}}

on:
workflow_dispatch:
pull_request:
branches: [main, 'dev/**']
paths:
- '.github/workflows/quality-portwing-fleet-soak.yml'
- 'app/agent/**'
- 'app/api/portwing-ws.ts'
- 'app/api/container/log-stream.ts'
- 'app/configuration/**'
- 'scripts/portwing-fleet-soak.mjs'
schedule:
- cron: '45 7 * * 0'

permissions:
contents: read

concurrency:
group: portwing-fleet-soak-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true

jobs:
fleet-soak:
name: "⏱️ Cross-repo fleet, exec, reconnect, backpressure"
runs-on: ubuntu-latest
timeout-minutes: 45

steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit

- name: Checkout Drydock
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
path: drydock

- name: Checkout Portwing
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: CodesWhat/portwing
ref: main
persist-credentials: false
path: portwing

- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
package-manager-cache: false

- name: Setup Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: portwing/go.mod

- name: Install and build Drydock
working-directory: drydock/app
run: |
set -euo pipefail
npm ci
npm run build

- name: Build Portwing and mock Docker
working-directory: portwing
run: |
set -euo pipefail
go build -o "${RUNNER_TEMP}/portwing" ./cmd/portwing
go build -o "${RUNNER_TEMP}/mockdocker" ./benchmarks/cmd/mockdocker

- name: Resolve soak parameters
id: parameters
run: |
set -euo pipefail
if [ "${GITHUB_EVENT_NAME}" = "schedule" ]; then
duration="30m"
agents="24"
exec_per_agent="4"
rss_threshold="268435456"
heap_threshold="134217728"
elif [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then
duration="45s"
agents="8"
exec_per_agent="3"
rss_threshold="134217728"
heap_threshold="67108864"
else
duration="5m"
agents="8"
exec_per_agent="3"
rss_threshold="268435456"
heap_threshold="134217728"
fi
{
echo "duration=${duration}"
echo "agents=${agents}"
echo "exec_per_agent=${exec_per_agent}"
echo "rss_threshold=${rss_threshold}"
echo "heap_threshold=${heap_threshold}"
} >> "${GITHUB_OUTPUT}"

- name: Run real-process fleet soak
env:
SOAK_DURATION: ${{ steps.parameters.outputs.duration }}
SOAK_AGENTS: ${{ steps.parameters.outputs.agents }}
SOAK_EXEC_PER_AGENT: ${{ steps.parameters.outputs.exec_per_agent }}
SOAK_RSS_THRESHOLD: ${{ steps.parameters.outputs.rss_threshold }}
SOAK_HEAP_THRESHOLD: ${{ steps.parameters.outputs.heap_threshold }}
run: |
set -euo pipefail
node --expose-gc drydock/scripts/portwing-fleet-soak.mjs \
--portwing-bin "${RUNNER_TEMP}/portwing" \
--mockdocker-bin "${RUNNER_TEMP}/mockdocker" \
--portwing-repo portwing \
--duration "${SOAK_DURATION}" \
--agents "${SOAK_AGENTS}" \
--exec-per-agent "${SOAK_EXEC_PER_AGENT}" \
--rss-growth-threshold-bytes "${SOAK_RSS_THRESHOLD}" \
--heap-growth-threshold-bytes "${SOAK_HEAP_THRESHOLD}" \
--output artifacts/portwing-fleet-soak.json

- name: Upload fleet-soak evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: portwing-fleet-soak-${{ github.run_id }}-${{ github.run_attempt }}
path: artifacts/portwing-fleet-soak.json
if-no-files-found: error
retention-days: 90

- name: Summarize
if: always()
run: |
set -euo pipefail
if [ ! -f artifacts/portwing-fleet-soak.json ]; then
echo "### Portwing fleet soak" >> "${GITHUB_STEP_SUMMARY}"
echo "No evidence file was produced." >> "${GITHUB_STEP_SUMMARY}"
exit 0
fi
{
echo "### Portwing fleet soak"
echo "- Result: $(jq -r 'if .passed then "PASS" else "FAIL" end' artifacts/portwing-fleet-soak.json)"
echo "- Agents: $(jq -r '.configuration.agents' artifacts/portwing-fleet-soak.json)"
echo "- Duration: $(jq -r '.configuration.duration' artifacts/portwing-fleet-soak.json)"
echo "- Exec sessions: $(jq -r '.phases.execSessionsStarted' artifacts/portwing-fleet-soak.json)"
echo "- Reconnects observed: $(jq -r '.phases.reconnectsObserved' artifacts/portwing-fleet-soak.json)"
echo "- Agent RSS growth: $(jq -r '.resources.agentRss.growth' artifacts/portwing-fleet-soak.json) bytes"
echo "- Controller heap growth: $(jq -r '.resources.controllerHeap.growth' artifacts/portwing-fleet-soak.json) bytes"
} >> "${GITHUB_STEP_SUMMARY}"
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **Continuous Portwing edge log streams.** The authenticated container-log WebSocket now bridges correlated `dd:container_log_chunk`, `dd:container_log_end`, and `dd:container_log_error` frames, cancels the agent stream when the viewer closes, preserves stdout/stderr and timestamp decoding, and caps each downstream viewer at 1 MiB of buffered data. Older Portwing agents degrade to their one-shot response without breaking the viewer.
- **Real Portwing fleet-soak workflow.** Pull requests and the weekly quality schedule run actual signed Portwing processes against Drydock's production edge gateway through concurrent exec, sustained logs, forced controller backpressure, and reconnect storms. Machine-readable evidence records RSS/heap budgets and is retained for 90 days.

### Changed

- **The `portwing/1.0` edge endpoint is enabled by default.** `DD_EXPERIMENTAL_PORTWING=false` remains as an emergency disable for new edge connections. OpenAPI, operator docs, and translated feature summaries now describe the stable/default-on behavior.

## [1.6.0-rc.8] — 2026-07-28

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion README.de.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ Die meisten Tools erzwingen einen Kompromiss. Die Auto-Updater (Watchtower, Ouro
| 🪝 | **Lebenszyklus-Hooks** | Shell-Befehle vor und nach dem Update über Container-Labels, mit Zeitüberschreitungen pro Hook und Steuerung des Abbruchs bei Fehler. |
| 🗂️ | **Docker Compose-Updates** | Ziehen Sie Compose-Dienste über die Docker Engine-API mit YAML-erhaltendem Image-Patching ab und erstellen Sie sie neu. |
| 🎛️ | **Richtlinie pro Container** | Regex-Tag-Regeln und Trigger-Routing verwenden `dd.*`-Labels; Reifegrenzen, Skip/Snooze/Pin und Wartungsfenster werden über die Benutzeroberfläche/API oder die Watcher-Konfiguration gespeichert. |
| 🛰️ | **Verteilte Agenten** | Überwachen Sie Remote-Docker-Hosts über SSE. Edge-Agents hinter NAT wählen sich über WebSocket mit Ed25519-Schlüsselauthentifizierung aus, kein eingehender Port erforderlich (`DD_EXPERIMENTAL_PORTWING=true`). |
| 🛰️ | **Verteilte Agenten** | Überwachen Sie Remote-Docker-Hosts über SSE. Portwing-Edge-Agents hinter NAT wählen sich über WebSocket mit Ed25519-Schlüsselauthentifizierung und kontinuierlichen Live-Protokollen ein; kein eingehender Port ist erforderlich. Der Endpunkt ist standardmäßig aktiviert; `DD_EXPERIMENTAL_PORTWING=false` ist die Notabschaltung. |
| 🖥️ | **Web-Dashboard** | Vue 3-Benutzeroberfläche mit einem anpassbaren Widget-Raster ohne Abhängigkeiten, reaktionsfähigen Tabellen-/Kartenansichten, Live-SSE-Updates, Steuerelementen für Benachrichtigungsglocken sowie Details, Protokollen und Statistiken pro Container. |
| 🔗 | **REST-API und Webhooks** | Token-authentifizierte Endpunkte für CI/CD-Überwachungs- und Update-Trigger sowie signierte Registrierungs-Webhook-Aufnahme für Push-Ereignisse. |
| 🔐 | **OIDC-Authentifizierung** | Sichern Sie das Dashboard mit OpenID Connect (Authelia, Auth0, Authentik). Alle Authentifizierungsflüsse werden standardmäßig nicht geschlossen. |
Expand Down
2 changes: 1 addition & 1 deletion README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ La mayoría de las herramientas obligan a hacer concesiones. Los actualizadores
| 🪝 | **Ganchos de ciclo de vida** | Comandos de shell previos y posteriores a la actualización a través de etiquetas de contenedor, con tiempos de espera por gancho y control de cancelación en caso de falla. |
| 🗂️ | **Actualizaciones Docker Compose** | Extraiga y vuelva a crear servicios de Compose a través de la API Docker Engine con parches de imágenes que preservan YAML. |
| 🎛️ | **Política por contenedor** | Las reglas de etiquetas Regex y el enrutamiento de activación utilizan etiquetas `dd.*`; Las puertas de madurez, saltar/posponer/fijar y las ventanas de mantenimiento se almacenan a través de UI/API o la configuración del observador. |
| 🛰️ | **Agentes distribuidos** | Supervise hosts Docker remotos a través de SSE. Los agentes perimetrales detrás de NAT marcan a través de WebSocket con autenticación de clave Ed25519, no se requiere puerto de entrada (`DD_EXPERIMENTAL_PORTWING=true`). |
| 🛰️ | **Agentes distribuidos** | Supervise hosts Docker remotos a través de SSE. Los agentes Portwing detrás de NAT se conectan por WebSocket con autenticación Ed25519 y registros continuos en vivo, sin necesidad de un puerto de entrada. El endpoint está habilitado de forma predeterminada; `DD_EXPERIMENTAL_PORTWING=false` es la desactivación de emergencia. |
| 🖥️ | **Panel web** | Interfaz de usuario de Vue 3 con una cuadrícula de widgets personalizable sin dependencia, vistas de tablas/tarjetas responsivas, actualizaciones SSE en vivo, controles de campana de notificación y detalles, registros y estadísticas por contenedor. |
| 🔗 | **API REST y webhooks** | Puntos finales autenticados por token para activación de actualizaciones y vigilancia de CI/CD, además de ingesta de webhooks de registro firmados para eventos push. |
| 🔐 | **Autenticación OIDC** | Asegure el tablero con OpenID Connect (Authelia, Auth0, Authentik). Todos los flujos de autenticación fallan al cerrarse de forma predeterminada. |
Expand Down
2 changes: 1 addition & 1 deletion README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ La plupart des outils imposent un compromis. Les mises à jour automatiques (Wat
| 🪝 | **Crochets de cycle de vie** | Commandes shell avant et après la mise à jour via des étiquettes de conteneur, avec délais d'attente par hook et contrôle d'abandon en cas d'échec. |
| 🗂️ | **Mises à jour Docker Compose** | Extrayez et recréez les services Compose via l'API Docker Engine avec des correctifs d'image préservant YAML. |
| 🎛️ | **Politique par conteneur** | Les règles de balise Regex et le routage des déclencheurs utilisent les étiquettes `dd.*` ; les portes de maturité, les sauts/répétitions/épingles et les fenêtres de maintenance sont stockés via l'interface utilisateur/API ou la configuration de l'observateur. |
| 🛰️ | **Agents distribués** | Surveillez les hôtes Docker distants via SSE. Les agents Edge derrière NAT appellent via WebSocket avec l'authentification par clé Ed25519, aucun port entrant requis (`DD_EXPERIMENTAL_PORTWING=true`). |
| 🛰️ | **Agents distribués** | Surveillez les hôtes Docker distants via SSE. Les agents Portwing derrière un NAT se connectent via WebSocket avec authentification Ed25519 et journaux continus en direct, sans port entrant. Le point de terminaison est activé par défaut ; `DD_EXPERIMENTAL_PORTWING=false` est la désactivation d'urgence. |
| 🖥️ | **Tableau de bord Web** | Interface utilisateur Vue 3 avec une grille de widgets personnalisable sans dépendance, des vues de table/carte réactives, des mises à jour SSE en direct, des contrôles de cloche de notification et des détails, journaux et statistiques par conteneur. |
| 🔗 | **API REST et webhooks** | Points de terminaison authentifiés par jeton pour les déclencheurs de surveillance et de mise à jour CI/CD, ainsi que l'ingestion de webhooks de registre signés pour les événements push. |
| 🔐 | **Authentification OIDC** | Sécurisez le tableau de bord avec OpenID Connect (Authelia, Auth0, Authentik). Tous les flux d'authentification échouent à la fermeture par défaut. |
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ Most tools force a tradeoff. The auto-updaters (Watchtower, Ouroboros) pull and
| 🪝 | **Lifecycle Hooks** | Pre and post-update shell commands via container labels, with per-hook timeouts and abort-on-failure control. |
| 🗂️ | **Docker Compose Updates** | Pull and recreate Compose services through the Docker Engine API with YAML-preserving image patching. |
| 🎛️ | **Per-Container Policy** | Regex tag rules and trigger routing use `dd.*` labels; maturity gates, skip/snooze/pin, and maintenance windows are stored via UI/API or watcher configuration. |
| 🛰️ | **Distributed Agents** | Monitor remote Docker hosts over SSE. Edge agents behind NAT dial out over WebSocket with Ed25519 key auth, no inbound port required (`DD_EXPERIMENTAL_PORTWING=true`). |
| 🛰️ | **Distributed Agents** | Monitor remote Docker hosts over SSE. Portwing edge agents behind NAT dial out over WebSocket with Ed25519 key auth and continuous live logs, with no inbound port required. The endpoint is enabled by default; `DD_EXPERIMENTAL_PORTWING=false` is an emergency disable. |
| 🖥️ | **Web Dashboard** | Vue 3 UI with a zero-dependency customizable widget grid, responsive table/card views, live SSE updates, notification-bell controls, and per-container detail, logs, and stats. |
| 🔗 | **REST API & Webhooks** | Token-authenticated endpoints for CI/CD watch and update triggers, plus signed registry webhook ingestion for push events. |
| 🔐 | **OIDC Authentication** | Secure the dashboard with OpenID Connect (Authelia, Auth0, Authentik). All auth flows fail closed by default. |
Expand Down
2 changes: 1 addition & 1 deletion README.pl.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ Większość narzędzi wymusza kompromis. Automatyczne aktualizacje (Watchtower,
| 🪝 | **Haki cyklu życia** | Polecenia powłoki przed i po aktualizacji za pośrednictwem etykiet kontenerów, z limitami czasu dla poszczególnych haków i kontrolą przerwania w przypadku awarii. |
| 🗂️ | **Aktualizacje Docker Compose** | Pobieraj i odtwarzaj usługi Compose za pośrednictwem interfejsu API Docker Engine z łataniem obrazów zachowującym YAML. |
| 🎛️ | **Zasady dotyczące kontenera** | Reguły tagów Regex i routing wyzwalaczy korzystają z etykiet `dd.*`; bramki dojrzałości, pomijanie/odkładanie/przypinanie i okna konserwacji są przechowywane za pośrednictwem interfejsu użytkownika/API lub konfiguracji obserwatora. |
| 🛰️ | **Agenci rozproszoni** | Monitoruj zdalne hosty Dockera za pośrednictwem SSE. Agenci brzegowi za NAT wybierają numer przez WebSocket z uwierzytelnianiem za pomocą klucza Ed25519, nie jest wymagany port wejściowy (`DD_EXPERIMENTAL_PORTWING=true`). |
| 🛰️ | **Agenci rozproszoni** | Monitoruj zdalne hosty Dockera za pośrednictwem SSE. Agenci Portwing za NAT łączą się przez WebSocket z uwierzytelnianiem Ed25519 i ciągłymi logami na żywo, bez portu wejściowego. Punkt końcowy jest domyślnie włączony; `DD_EXPERIMENTAL_PORTWING=false` służy do awaryjnego wyłączenia. |
| 🖥️ | **Panel sieciowy** | Interfejs użytkownika Vue 3 z konfigurowalną siatką widżetów o zerowej zależności, responsywnymi widokami tabel/kart, aktualizacjami SSE na żywo, sterowaniem dzwonkiem powiadomień oraz szczegółami, dziennikami i statystykami dotyczącymi poszczególnych kontenerów. |
| 🔗 | **REST API i webhooki** | Punkty końcowe uwierzytelniane tokenem dla wyzwalaczy monitorowania i aktualizacji CI/CD oraz pozyskiwania podpisanego elementu webhook rejestru dla zdarzeń push. |
| 🔐 | **Uwierzytelnianie OIDC** | Zabezpiecz deskę rozdzielczą za pomocą OpenID Connect (Authelia, Auth0, Authentik). Domyślnie wszystkie przepływy uwierzytelniania nie są zamykane. |
Expand Down
2 changes: 1 addition & 1 deletion README.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ A maioria das ferramentas força uma compensação. Os atualizadores automático
| 🪝 | **Ganchos de ciclo de vida** | Comandos shell pré e pós-atualização por meio de rótulos de contêiner, com tempos limite por gancho e controle de aborto em caso de falha. |
| 🗂️ | **Atualizações Docker Compose** | Extraia e recrie serviços do Compose por meio da API Docker Engine com patch de imagem com preservação de YAML. |
| 🎛️ | **Política por contêiner** | As regras de tag Regex e o roteamento de gatilho usam rótulos `dd.*`; portas de maturidade, pular/adiar/fixar e janelas de manutenção são armazenadas via UI/API ou configuração do inspetor. |
| 🛰️ | **Agentes Distribuídos** | Monitore hosts Docker remotos por SSE. Agentes de borda por trás da discagem NAT por WebSocket com autenticação de chave Ed25519, sem necessidade de porta de entrada (`DD_EXPERIMENTAL_PORTWING=true`). |
| 🛰️ | **Agentes Distribuídos** | Monitore hosts Docker remotos por SSE. Agentes Portwing atrás de NAT conectam-se por WebSocket com autenticação Ed25519 e logs contínuos ao vivo, sem porta de entrada. O endpoint é habilitado por padrão; `DD_EXPERIMENTAL_PORTWING=false` é a desativação de emergência. |
| 🖥️ | **Painel Web** | UI Vue 3 com uma grade de widget personalizável de dependência zero, visualizações responsivas de tabela/cartão, atualizações SSE ao vivo, controles de sino de notificação e detalhes, registros e estatísticas por contêiner. |
| 🔗 | **API REST e webhooks** | Endpoints autenticados por token para monitoramento de CI/CD e gatilhos de atualização, além de ingestão de webhook de registro assinado para eventos push. |
| 🔐 | **Autenticação OIDC** | Proteja o painel com OpenID Connect (Authelia, Auth0, Authentik). Todos os fluxos de autenticação falham quando fechados por padrão. |
Expand Down
Loading
Loading