Dokumentation

Projekte bauen, die auf KynxGate perfekt laufen

Diese Anleitung zeigt, wie du dein Repository strukturierst, damit es zuverlässig deployt und online bleibt – und wie du dein Projekt per API Control automatisierst: Status, Live-Logs, Redeploys – und mit der Browser-Test-API echte End-to-End-Tests durch autonome Agenten (KynxAI, CI, eigene Bots).

Überblick

KynxGate deployt dein Git-Repository in einem isolierten Container und macht es unter einer eigenen Domain erreichbar. Du brauchst kein spezielles Framework – wichtig ist nur, dass dein Dienst auf dem von KynxGate gesetzten Port und auf 0.0.0.0 lauscht.

Der Ablauf eines Deployments:

  1. Code wird aus dem verbundenen Repository (Branch) geklont.
  2. Abhängigkeiten installieren → Build → Container starten.
  3. Health-Check: warten, bis dein Dienst antwortet.
  4. Routing & SSL für die Domain werden automatisch eingerichtet.
2 Minuten

Schnellstart

Ein minimaler Webdienst, der überall auf KynxGate funktioniert. Entscheidend sind die letzten beiden Zeilen – lausche auf process.env.PORT und 0.0.0.0:

server.js
// Funktioniert mit jeder Sprache – Beispiel Node.js / Express
const express = require("express")
const app = express()

// KynxGate setzt PORT, HOST und HOSTNAME automatisch.
const PORT = process.env.PORT || 3000

app.get("/health", (_req, res) => res.json({ ok: true }))
app.get("/", (_req, res) => res.send("Hallo von KynxGate!"))

// WICHTIG: auf 0.0.0.0 lauschen, NICHT auf 127.0.0.1/localhost
app.listen(PORT, "0.0.0.0", () => {
  console.log("läuft auf 0.0.0.0:" + PORT)
})
Das war's im Kern
Hat dein Projekt ein start-Script in der package.json, wird es automatisch erkannt. Für andere Sprachen (Go, Rust, Python …) gilt dasselbe Prinzip: Port aus der Umgebung lesen, auf 0.0.0.0 binden.
Wichtig

Port & Host

KynxGate setzt beim Start automatisch diese Umgebungsvariablen in deinen Container:

  • PORT – der Port, auf dem dein Dienst lauschen muss. Nutze immer process.env.PORT, hardcode niemals einen festen Port.
  • HOST und HOSTNAME sind auf 0.0.0.0 gesetzt.
Häufigster Fehler
Wenn dein Dienst nur auf 127.0.0.1 / localhost lauscht, ist er von außerhalb des Containers nicht erreichbar – der Health-Check schlägt fehl und das Projekt erscheint offline (503), obwohl die Logs „listening" zeigen. Binde immer an 0.0.0.0.

Frameworks wie Next.js, Express, Fastify und NestJS werden erkannt und passend mit PORT/HOST versorgt.

Build & Start

Install-, Build- und Start-Befehle kannst du in den Projekt-Einstellungen überschreiben. Ohne Angabe gilt eine sinnvolle Automatik:

  • Paketmanager: automatisch erkannt – bun.lock → Bun, yarn.lock → yarn, pnpm-lock.yaml → pnpm, sonst npm.
  • Install: bun install --frozen-lockfile, npm ci (bzw. yarn/pnpm --frozen-lockfile), Fallback npm install, wenn keine Lockfile vorhanden ist. devDependencies werden mitinstalliert (z. B. TypeScript, Vite).
  • Build: Standard npm run build bzw. bun run build bei Bun-Projekten.
  • Start: npm start (falls scripts.start in package.json), sonst Framework-Fallback (z. B. Vite → vite preview), sonst npm run dev, sonst node index.js / node server.js.
Pflicht für die meisten Node-Projekte
Du musst keine separate KynxGate-Datei anlegen — committe ein scripts.start in der package.json (oder setze den Start-Befehl in den Projekt-Einstellungen). Ohne start kann KynxGate auf npm run dev fallen; das ist für Production oft falsch und erscheint im Build-Log als [deploy-ready] code=START_USES_DEV_FALLBACK.
Empfehlung
Committe immer eine Lockfile (bun.lock, package-lock.json, yarn.lock oder pnpm-lock.yaml) – das macht Builds reproduzierbar und schneller.
Häufig

Vite & Static SPAs

Reine Vite-Frontends (React/Vue/Svelte ohne eigenes Backend) bauen nach dist/. Der Dev-Server (npm run dev) ist nicht für Deployments gedacht — er crasht im Container oft mit EROFS und lauscht nicht zuverlässig auf 0.0.0.0:$PORT.

Minimale package.json für KynxGate:

package.json (Vite SPA)
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "start": "vite preview --host 0.0.0.0 --port ${PORT:-4173}"
  }
}
Deploy-Logs lesen
Vor dem Build prüft KynxGate die Repo-Konfiguration. Probleme erscheinen als [deploy-ready] code=… severity=… in den Build-Logs (auch per API abrufbar) — z. B. fehlendes start-Script, 127.0.0.1 statt 0.0.0.0, oder fester Port.

Alternative zu vite preview: npx serve dist -l tcp://0.0.0.0:$PORT (Paket serve als dependency). kynxgate.json ist optional und steuert primär den Health-Check — nicht den Start-Befehl.

Dockerfile & Compose

Hat dein Repo ein Dockerfile, nutzt KynxGate es direkt – ideal für eigene Stacks (Go, Rust, Python …). EXPOSE und Healthcheck werden zur Laufzeit mit dem echten PORT überschrieben. Ohne Dockerfile wird automatisch ein passendes Image erzeugt.

Dockerfile
FROM node:20-alpine
WORKDIR /app

COPY package*.json ./
RUN npm ci --omit=dev

COPY . .
RUN npm run build

# PORT, HOST & HOSTNAME werden von KynxGate zur Laufzeit gesetzt.
EXPOSE 3000
CMD ["npm", "start"]

Mehrere Container? Eine docker-compose.yml wird ebenfalls unterstützt (z. B. App + Worker oder Game-Server). Der Haupt-Dienst muss weiterhin auf 0.0.0.0:$PORT erreichbar sein.

Health-Checks

Nach dem Start prüft KynxGate, ob dein Dienst antwortet, bevor er live geschaltet wird. Der Pfad wird in dieser Reihenfolge bestimmt:

  1. Projekt-Einstellungen (UI) → „Health-Check-Pfad"
  2. Repo-Konfiguration: kynxgate.json, package.json (Feld kynxgate), railway.json oder railway.toml
  3. Standard: /
kynxgate.json
{
  "healthcheckPath": "/health",
  "healthcheckTimeout": 120
}
Empfohlen
Lege einen schlanken /health-Endpoint an, der nur 200 OK zurückgibt, sobald dein Dienst wirklich bereit ist (DB verbunden etc.). Das macht Deployments stabiler und Zero-Downtime zuverlässig.

Env-Variablen & Secrets

Umgebungsvariablen und Secrets pflegst du im Dashboard unter Variablen. Sie stehen deinem Dienst zur Laufzeit als process.env.* zur Verfügung. Secrets werden verschlüsselt gespeichert.

Verwendung im Code
# Im Dashboard unter "Variablen" gesetzt, im Code so nutzen:
const dbUrl  = process.env.DATABASE_URL   // automatisch bei verbundener DB
const apiKey = process.env.STRIPE_SECRET  // eigene Secrets
const data   = process.env.VOLUME_PATH    // persistenter Speicher (Volume)
Niemals committen
Lege Secrets (API-Keys, Tokens, Passwörter) nicht ins Repository. Nutze die Variablen-Verwaltung oder die .gitignore für lokale .env-Dateien.

Datenbanken & Volumes

Datenbanken (PostgreSQL, MySQL, MongoDB, Redis …) erstellst du im Dashboard und verbindest sie mit dem Projekt. Die Verbindungsdaten werden automatisch als Umgebungsvariable injiziert (z. B. DATABASE_URL).

Volumes bieten persistenten Speicher, der Deployments und Neustarts überlebt. Der Mount-Pfad steht als VOLUME_PATH bereit (Standard /data). Schreibe dauerhafte Dateien (Uploads, SQLite, Caches) dorthin – das Container- Dateisystem selbst ist flüchtig.

Domains & SSL

Jedes Projekt bekommt automatisch eine Subdomain: <name>.up.kynxgate.de – inkl. HTTPS-Zertifikat (Let's Encrypt). Eine eigene Domain kannst du zusätzlich verbinden; das Zertifikat wird automatisch ausgestellt und erneuert.

Das Routing erfolgt anhand des Host-Headers. Dein Dienst muss dafür nichts tun – einfach auf 0.0.0.0:$PORT lauschen.

Zero-Downtime (Blue-Green)

Optional kannst du Zero-Downtime-Deployments aktivieren: Die neue Version startet parallel, und erst nach erfolgreichem Health-Check wird umgeschaltet. Voraussetzung ist ein sauberer /health-Endpoint und ein eigenes Dockerfile. Nicht geeignet für reine Compose-/Game-Server- Setups (z. B. CS2, TeamSpeak). Bei KynxAI nur mit aktivem Docker-Socket-Modus (allowDockerSocket) nicht verfügbar — normale KynxAI-Deploys unterstützen Blue-Green.

Logs

Alles, was dein Dienst nach stdout/stderr schreibt, landet in den Laufzeit-Logs. Zusätzlich werden Build- und Deploy-Logs pro Deployment gespeichert. Du siehst sie live im Dashboard – oder rufst sie per API ab (siehe unten).

Logge strukturiert und aussagekräftig (Startzeit, gebundener Port, DB-Status). Das hilft dir – und autonomen Agenten – beim schnellen Debuggen über die API.

Best-Practice-Checkliste

  • Dienst lauscht auf process.env.PORT und 0.0.0.0
  • Schlanker /health-Endpoint vorhanden
  • Lockfile committed (reproduzierbare Builds)
  • Secrets über Variablen, nicht im Repo
  • Persistente Daten ins Volume (VOLUME_PATH)
  • scripts.start in package.json (0.0.0.0 + $PORT) oder eigenes Dockerfile
  • Vite/React-SPA: vite preview oder serve dist — nicht npm run dev
  • Aussagekräftige Logs nach stdout/stderr
API Control

API Control – Überblick

Jedes Projekt hat eine eigene, projektgebundene REST-API unter /api/v1. Damit fragst du Status & Logs ab, löst Redeploys aus – und steuerst einen echten Chromium-Browser für automatische End-to-End-Tests (ideal für KynxAI und andere Agenten).

Du erstellst API-Keys direkt im Projekt: Projekt → Einstellungen → API Control. Jeder Key gilt nur für genau dieses Projekt und besitzt fein granulare Berechtigungen (Scopes).

Discovery & Agent-Doku (ohne Auth)
GET /api/v1 — globale Übersicht mit agentOnboarding-Schritten. GET /api/v1/docs — vollständige maschinenlesbare Doku (JSON). GET /api/v1/docs?format=markdown — dieselbe Doku als Markdown für LLM-Agenten.
API

Maschinenlesbare Doku für Agenten

Agenten sollen nicht die HTML-Doku scrapen — stattdessen gibt es dedizierte JSON/Markdown-Endpunkte. Beim ersten API-Test (Overview oder Verbindungstest im API-Control-Panel) bekommst du docsApi, docsMarkdown und agentOnboarding zurück.

Agent-Onboarding
# 1) Global (keine Auth)
curl -s https://kynxgate.de/api/v1 | jq .
curl -s "https://kynxgate.de/api/v1/docs?format=markdown"

# 2) Projektgebunden (mit API-Key) — gefiltert nach deinen Scopes, echte URLs
curl -s "https://kynxgate.de/api/v1/projects/<workspaceId>/<projectName>/docs?format=markdown" \
  -H "Authorization: Bearer $KYNX_API_KEY"

# 3) Einzelner Abschnitt (z. B. nur Browser)
curl -s "https://kynxgate.de/api/v1/docs?section=browser&format=markdown"
  • GET /api/v1/projects/…/ (Overview) — Handshake: Scopes, Endpunkte, nextSteps, Doku-URLs
  • GET /api/v1/projects/…/docs — scope-gefilterte Doku mit projektspezifischen URLs
  • GET /api/v1/projects/…/status — enthält apiHints mit Verweis auf Doku-API

Authentifizierung & Keys

Sende den Key bei jeder Anfrage in einem dieser Header:

Authorization: Bearer kgp_xxxx_xxxxxxxxxxxxxxxxxxxx
# oder
X-Api-Key: kgp_xxxx_xxxxxxxxxxxxxxxxxxxx

Verfügbare Scopes (Least-Privilege – vergib nur, was nötig ist):

ScopeErlaubt
status:readOnline-/Deploy-Status abfragen
logs:readContainer-, Build- & Deploy-Logs (inkl. Live-Stream)
deployments:readDeployment-Historie & Details
deploy:writeRedeploy auslösen
control:writeProjekt stoppen / Laufzeit steuern
browser:readBrowser-Sessions lesen, Screenshots/DOM/Console/Network/Diagnostics
browser:writeBrowser-Session starten, Aktionen & Assertions ausführen
Key-Sicherheit
Der Klartext-Key wird nur einmal beim Erstellen angezeigt und nirgends gespeichert (nur ein sha256-Hash). Verloren? Einfach widerrufen und einen neuen erstellen.

Endpunkte

Basis-URL (die exakte URL inkl. IDs steht im API-Control-Panel deines Projekts):

https://kynxgate.de/api/v1/projects/{workspaceId}/{projectName}
MethodePfadScopeZweck
GET/status:readÜbersicht + Scopes deines Keys
GET/docsstatus:readMaschinenlesbare Doku (scope-gefiltert)
GET/statusstatus:readOnline-/Deploy-Status
GET/deploymentsdeployments:readDeployment-Historie
GET/logs?source=runtime|build|deploylogs:readLogs abrufen
GET/logs/streamlogs:readLive-Stream (SSE)
POST/redeploydeploy:writeRedeploy auslösen
POST/stopcontrol:writeProjekt stoppen
POST/browser/sessionsbrowser:writeBrowser-Session starten
GET/browser/sessionsbrowser:readAktive Sessions auflisten
GET/browser/sessions/{id}browser:readSession-Status
DELETE/browser/sessions/{id}browser:writeSession schließen
POST/browser/sessions/{id}/actionsbrowser:writeKlick, Tippen, Assertions (closeAfter)
GET/browser/sessions/{id}/diagnosticsbrowser:readBrowser + Docker-Logs kombiniert
GET/browser/sessions/{id}/screenshotbrowser:readScreenshot (PNG base64)
GET/browser/sessions/{id}/dombrowser:readHTML-DOM
GET/browser/sessions/{id}/a11ybrowser:readAccessibility-Baum
GET/browser/sessions/{id}/consolebrowser:readBrowser-Console (tail)
GET/browser/sessions/{id}/networkbrowser:readNetwork-Events (failedOnly)
GET/browser/sessions/{id}/cookiesbrowser:readCookies
GET/browser/sessions/{id}/storagebrowser:readlocalStorage / sessionStorage

Beispiel-Antwort von /status:

200 OK · application/json
{
  "project": "kynxhotel",
  "workspaceId": "cml18wa4e0003...",
  "state": "online",
  "online": true,
  "url": "https://kynxhotel.up.kynxgate.de",
  "subdomainUrl": "https://kynxhotel.up.kynxgate.de",
  "customDomain": null,
  "port": 9101,
  "branch": "main",
  "deployment": {
    "id": "k7s9...",
    "status": "SUCCESS",
    "branch": "main",
    "commitHash": "a1b2c3d",
    "buildTimeMs": 48213,
    "completedAt": "2026-06-16T12:50:05.000Z"
  },
  "checkedAt": "2026-06-16T17:55:00.000Z"
}
Neu

Browser-Tests für Agenten

Die Browser-Test-API lässt externe Agenten eine echte Chromium-Instanz fernsteuern – wie ein Mensch mit Maus und Tastatur: Seiten öffnen, klicken, Formulare ausfüllen, einloggen, Dateien hochladen, Assertions prüfen, Screenshots machen.

Der Clou bei KynxGate: Du kontrollierst den gesamten Stack. Bei einem Fehler liefert /diagnostics Browser-Console, fehlgeschlagene Network-Requests und Docker-Runtime-Logs in einem Call – der Agent muss nicht raten, ob das Problem Frontend oder Backend ist.

Typischer Ablauf für einen autonomen Agenten:

  1. Deploy auslösen (POST /redeploy) und warten bis online: true
  2. Browser-Session starten (POST /browser/sessions)
  3. Aktionen ausführen + Assertions (POST .../actions mit closeAfter: true)
  4. Bei Fehler: /diagnostics → Code anpassen → erneut deployen → wiederholen
bash · kompletter Flow
# Voraussetzung: Projekt ist online (GET /status → online: true)
# API-Key mit browser:read + browser:write (Projekt → API Control)

export KEY="kgp_xxxx_xxxxxxxxxxxxxxxxxxxx"
export BASE="https://kynxgate.de/api/v1/projects/<workspaceId>/<projectName>"

# 1) Session starten
#    target: "public" = Live-URL (Apache → Proxy → Container)
#            "internal" = http://127.0.0.1:<PORT> (nur vom Server)
SESSION=$(curl -s -X POST "$BASE/browser/sessions" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"target":"public"}' | jq -r .sessionId)

# 2) Aktionen + automatisch schließen (closeAfter: true — empfohlen für Agenten!)
curl -s -X POST "$BASE/browser/sessions/$SESSION/actions" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{
    "closeAfter": true,
    "actions": [
      {"type":"goto","path":"/"},
      {"type":"click","text":"Login"},
      {"type":"fill","label":"Email","value":"test@example.com"},
      {"type":"fill","label":"Password","value":"secret"},
      {"type":"click","role":"button","name":"Sign in"},
      {"type":"assert","kind":"textVisible","text":"Dashboard"}
    ],
    "returnArtifacts": ["screenshot","console","network"]
  }' | jq .

# 3) Bei Fehler (ohne closeAfter): Diagnose-Bundle
curl -s "$BASE/browser/sessions/$SESSION/diagnostics?logTail=300" \
  -H "Authorization: Bearer $KEY" | jq .

# 4) Manuell schließen (falls closeAfter nicht gesetzt)
curl -s -X DELETE "$BASE/browser/sessions/$SESSION" \
  -H "Authorization: Bearer $KEY"

Browser-Sessions & Lifecycle

Eine Session ist ein isolierter BrowserContext (eigene Cookies, Storage, Cache). Mehrere Sessions können parallel laufen – z. B. für verschiedene Test-Szenarien oder mehrere Agenten gleichzeitig.

Session erstellenPOST /browser/sessions

Request-Body (alle Felder optional außer implizitem target)
{
  "target": "public",
  "viewport": { "width": 1280, "height": 720 },
  "timeoutMs": 30000,
  "recordVideo": false
}
  • target: "public" (Standard) — Live-URL des Projekts (Custom Domain oder *.up.kynxgate.de), identisch zum echten User-Traffic
  • target: "internal"http://127.0.0.1:<PORT> direkt am Container (ohne Apache/SSL, nur vom Server)
  • target: "https://deine-domain.de/pfad" — explizite URL, nur wenn der Host zum Projekt gehört (SSRF-Schutz)
Projekt muss online sein
POST /browser/sessions antwortet mit 409 project_offline, wenn kein Container läuft. Vorher GET /status prüfen.

Session zuverlässig schließen — drei Wege (wichtig, damit keine Fenster offen bleiben):

  1. Empfohlen: closeAfter: true im actions-Body — schließt die Session automatisch nach dem Test (auch bei Fehlern)
  2. Manuell: DELETE /browser/sessions/{id}
  3. Automatisch: Idle-TTL (~2 Min ohne Aktivität) oder Hard-TTL (max. 20 Min)

Zusätzlich synchronisiert ein Hintergrund-Reconciler die Datenbank alle 60s — „hängende“ ACTIVE-Einträge werden bereinigt, wenn der Browser-Service die Session nicht mehr kennt (z. B. nach Crash).

Aktionen & Assertions

Kern-Endpunkt: POST /browser/sessions/{id}/actions— führt ein Array von Aktionen sequentiell aus. Semantische Locators (agentenfreundlich) statt fragiler CSS-Selektoren:

actions-Body mit closeAfter
{
  "closeAfter": true,
  "stopOnError": true,
  "returnArtifacts": ["screenshot", "dom", "console", "network"],
  "actions": [
    { "type": "goto", "path": "/login" },
    { "type": "fill", "label": "E-Mail", "value": "test@example.com" },
    { "type": "fill", "label": "Passwort", "value": "geheim" },
    { "type": "click", "role": "button", "name": "Anmelden" },
    { "type": "waitFor", "text": "Dashboard", "timeoutMs": 10000 },
    { "type": "assert", "kind": "urlContains", "value": "/dashboard" },
    { "type": "assert", "kind": "textVisible", "text": "Dashboard" },
    { "type": "screenshot", "fullPage": true },
    { "type": "getConsole", "tail": 30 },
    { "type": "getNetwork", "tail": 20, "failedOnly": true }
  ]
}
typeWichtige FelderBeschreibung
gotourl oder pathSeite öffnen (path relativ zur Session-baseUrl)
clicktext, role+name, selector, labelElement anklicken
filllabel/value oder selector/valueFeld leeren und Text setzen
typetext (+ optional label/selector)Zeichen für Zeichen tippen
presskeyTastatur (Enter, Tab, Escape …)
hovertext, role, selectorMaus darüber
selectvalue, selector/labelDropdown-Option wählen
check / unchecklabel, selectorCheckbox
scrollto: top|bottom, by: N, selectorScrollen
waitForselector, text, url, state, msWarten bis Bedingung
uploadselector, files[]Datei-Upload (base64 oder path)
dragfrom, toDrag & Drop
screenshotfullPagePNG als base64
getDommaxCharsHTML der Seite
getA11yAccessibility-Baum (für LLM-Agenten)
getConsoletailBrowser-Console-Log
getNetworktail, failedOnlyNetwork-Events
getCookies / getStorageSession-State lesen
assertkind + …Erwartung prüfen (siehe unten)

Assertions (type: "assert") — Expectation → Action → Validation:

  • urlContains + value — URL enthält Text
  • textVisible + text — Text auf der Seite sichtbar
  • selectorExists + selector — Element im DOM
  • cookieExists + name — Cookie gesetzt
  • httpOkForLastNav — letzte Navigation war HTTP 2xx/3xx

Fehlgeschlagene Assertions liefern 422 assertion_failed mit Details. Mit returnArtifacts kommen optional Screenshot, DOM, Console und Network am Ende des Requests mit.

Diagnostics bei Fehlern
GET .../diagnostics?logTail=300 kombiniert Browser-Console-Errors, fehlgeschlagene Requests und die letzten 300 Zeilen Docker-Runtime-Logs — ein Call für die komplette Fehleranalyse.

Betrieb, Parallelität & Stabilität

Der Browser-Service läuft als eigener PM2-Prozess (kynxgate-browser) auf dem KynxGate-Server — getrennt vom Next.js-Prozess, damit Chromium-Last und Dev-Restarts sich nicht gegenseitig stören.

  • Auto-Start: npm run dev:restart startet Proxy (9003) und Browser-Service (9005) automatisch mit
  • PM2: npm run browser:restart — überlebt Terminal-Schließen und Server-Reboot (via pm2 save)
  • Health: curl http://127.0.0.1:9005/_internal/health (nur lokal)

Limits & Parallelität (Standardeinstellungen auf dem Server):

LimitWertEnv-Variable
Parallele Sessions (global)30BROWSER_MAX_SESSIONS
Parallele Sessions (pro Projekt)10BROWSER_MAX_SESSIONS_PER_PROJECT
API-Rate-Limit Browser60/min pro Key
API-Rate-Limit sonst120/min pro Key
Idle-Schließung~2 MinBROWSER_SESSION_IDLE_TTL_MS
Max. Session-Dauer20 MinBROWSER_SESSION_HARD_TTL_MS
Aktions-Timeout90s Hard-CapBROWSER_ACTION_HARD_CAP_MS
Crash-Schutz & Aufräumen
Wenn Chromium abstürzt oder sich aufhängt: alle Sessions werden verworfen, Artefakte gelöscht, der Browser startet beim nächsten Request neu. Tote Contexts werden alle 15s erkannt. Context-Close hat ein 8s-Timeout mit Force-Fallback. Der DB-Reconciler markiert verwaiste Sessions als geschlossen.
Best Practice für Agenten
Immer closeAfter: true setzen. Bei parallelen Tests pro Projekt bis zu 10 Sessions — jede mit eigenem isolierten Cookie-Jar. Für Load-Tests lieber mehrere kurze Sessions mit closeAfter als eine lange offene Session.

Beispiele

Status, Logs & Redeploy (curl):

bash
# 1) API-Key als Umgebungsvariable ablegen (im Projekt unter "API Control" erstellen)
export KYNX_API_KEY="kgp_xxxx_xxxxxxxxxxxxxxxxxxxx"
export BASE="https://kynxgate.de/api/v1/projects/<workspaceId>/<projectName>"

# 2) Status abfragen
curl -s "$BASE/status" -H "Authorization: Bearer $KYNX_API_KEY" | jq

# 3) Laufzeit-Logs (letzte 300 Zeilen)
curl -s "$BASE/logs?source=runtime&tail=300" -H "Authorization: Bearer $KYNX_API_KEY"

# 4) Redeploy auslösen
curl -s -X POST "$BASE/redeploy" -H "Authorization: Bearer $KYNX_API_KEY"

# 5) Live-Stream (Server-Sent Events): Status + neue Logzeilen
curl -N "$BASE/logs/stream" -H "Authorization: Bearer $KYNX_API_KEY"

Autonomer Agent: Deploy → Browser-E2E-Test mit closeAfter:

javascript · Browser-Agent-Loop
// Vollständiger Agent-Loop: Deploy → warten → Browser-Test → bei Fehler Diagnostics
const KEY = process.env.KYNX_API_KEY
const BASE = "https://kynxgate.de/api/v1/projects/<workspaceId>/<projectName>"
const auth = { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" }

async function waitOnline(maxMs = 300_000) {
  const t0 = Date.now()
  while (Date.now() - t0 < maxMs) {
    const s = await fetch(`${BASE}/status`, { headers: auth }).then(r => r.json())
    if (s.online && s.state === "online") return s
    await new Promise(r => setTimeout(r, 5000))
  }
  throw new Error("Projekt wurde nicht online")
}

// Nach Redeploy warten
await fetch(`${BASE}/redeploy`, { method: "POST", headers: auth })
await waitOnline()

// Browser-Session: Test + sofort schließen (closeAfter)
const session = await fetch(`${BASE}/browser/sessions`, {
  method: "POST", headers: auth, body: JSON.stringify({ target: "public" }),
}).then(r => r.json())

const actions = await fetch(`${BASE}/browser/sessions/${session.sessionId}/actions`, {
  method: "POST",
  headers: auth,
  body: JSON.stringify({
    closeAfter: true,
    actions: [
      { type: "goto", path: "/" },
      { type: "assert", kind: "httpOkForLastNav" },
      { type: "click", text: "Registrieren" },
      { type: "fill", label: "E-Mail", value: "agent@test.de" },
      { type: "assert", kind: "textVisible", text: "Willkommen" },
    ],
    returnArtifacts: ["screenshot", "console", "network"],
  }),
}).then(r => r.json())

const failed = actions.results?.some(r => !r.ok)
if (failed) {
  const diag = await fetch(
    `${BASE}/browser/sessions/${session.sessionId}/diagnostics?logTail=200`,
    { headers: auth }
  ).then(r => r.json())
  console.error("Browser-Fehler:", diag.browser)
  console.error("Container-Logs:", diag.logs?.runtime?.slice(-2000))
  // → Code fixen, redeploy, wiederholen
}

Klassischer Agent bei Build-Fehler:

javascript
const KEY = process.env.KYNX_API_KEY
const BASE = "https://kynxgate.de/api/v1/projects/<workspaceId>/<projectName>"
const auth = { Authorization: `Bearer ${KEY}` }

const status = await fetch(`${BASE}/status`, { headers: auth }).then(r => r.json())

if (status.state === "failed") {
  // Build-Logs holen und automatisch neu deployen
  const logs = await fetch(`${BASE}/logs?source=build`, { headers: auth }).then(r => r.json())
  console.error("Build fehlgeschlagen:\n", logs.log)
  await fetch(`${BASE}/redeploy`, { method: "POST", headers: auth })
}

Dasselbe in Python:

python
import os, requests

KEY  = os.environ["KYNX_API_KEY"]
BASE = "https://kynxgate.de/api/v1/projects/<workspaceId>/<projectName>"
auth = {"Authorization": f"Bearer {KEY}"}

status = requests.get(f"{BASE}/status", headers=auth).json()
print(status["state"], status["online"])

if status["state"] == "failed":
    logs = requests.get(f"{BASE}/logs", params={"source": "build"}, headers=auth).json()
    print(logs["log"])
    requests.post(f"{BASE}/redeploy", headers=auth)

Live-Logs streamen (SSE):

javascript
// Live-Stream in Node.js mitlesen (SSE über fetch)
const res = await fetch(`${BASE}/logs/stream`, {
  headers: { Authorization: `Bearer ${KEY}` },
})
const reader = res.body.getReader()
const dec = new TextDecoder()
for (;;) {
  const { value, done } = await reader.read()
  if (done) break
  process.stdout.write(dec.decode(value)) // event: log / deployment / ping …
}
SSE-Hinweis
Der Stream sendet Events vom Typ ready, deployment, log und ping. Da Browser-EventSource keine eigenen Header setzen kann, nutze zum Streamen fetch/curl -N mit dem Authorization-Header (wie oben).

Fehlercodes & Rate-Limits

StatusCodeBedeutung
401missing_api_key / invalid_api_keyKey fehlt oder ist ungültig
403insufficient_scopeKey hat die nötige Berechtigung nicht (z. B. browser:write)
403revoked / expiredKey widerrufen oder abgelaufen
403project_mismatchKey gehört zu einem anderen Projekt
404not_foundRessource nicht gefunden (Deployment, Browser-Session)
409project_offlineBrowser-Test: Projekt/Container läuft nicht
422assertion_failedBrowser-Assertion fehlgeschlagen (Details im Body)
429rate_limited / session_capZu viele Anfragen oder zu viele parallele Sessions
500action_failed / internal_errorAktion oder Serverfehler
503docker_disabledDocker nicht aktiv — Browser-Tests nicht verfügbar
Rate-Limits
Allgemeine API: 120 Anfragen/Minute pro Key. Browser-Endpunkte: separates Limit von 60 Anfragen/Minute. Bei Überschreitung: 429 mit Retry-After. Fehler immer als JSON { "error": "...", "message": "..." }.
Fragen oder etwas unklar? Erstelle einen API-Key unter Projekt → Einstellungen → API Control (für Browser-Tests: browser:read + browser:write) und leg los.