Ein eigenes Community-Check-Repository veröffentlichen
Ein eigenes Check-Repository betreiben: PowerShell-Check schreiben, Konfiguration und Ergebnisanzeige definieren, veröffentlichen und in octoja einbinden.
Ein Community-Check-Repository ist ein einfacher Ablageort im Web, der Monitoring-Checks veröffentlicht. Veröffentlichst du eines, kann jede octoja-Instanz es anbinden — die Checks erscheinen dann in der Check-Bibliothek und lassen sich Geräten genauso zuweisen wie eingebaute Checks. Diese Anleitung baut ein vollständiges Repository mit einem PowerShell-Check, der meldet, ob ein Windows-Prozess läuft.
Nutze ein Repository, wenn du denselben Check auf mehreren octoja-Instanzen brauchst — über deine eigenen Mandanten hinweg oder für Kunden, die eine eigene Instanz betreiben. Läuft der Check dagegen immer nur auf einer Instanz, ist ein Custom-Script-Check weniger Aufwand: du schreibst ihn im Browser, und octoja übernimmt die Verpackung. Siehe Einen Custom-Script-Check konfigurieren.
Bevor du beginnst
- Ein Ort, der statische Dateien über HTTPS ausliefert — ein Webserver, ein Object Storage, ein CDN oder ein Hoster für statische Seiten. octoja klont kein Git-Repository, sondern lädt Dateien über HTTP oder HTTPS.
- Die Berechtigung Check-Repository-Verwaltung auf der octoja-Instanz, an die du das fertige Repository anbindest. Berechtigungen werden über Gruppen unter Administration → Gruppen vergeben.
- Ein kurzes Herstellerpräfix für deine Check-IDs, damit sie nicht mit Checks aus anderen Repositories kollidieren. Diese Anleitung verwendet
acme. - Ein Windows-Gerät mit installiertem Agent, um den fertigen Check auszuprobieren.
Was du veröffentlichst
Ein Repository besteht aus drei Arten von Dateien: einem Index sowie je Check-Version einem UI-Manifest und einem Skript.
https://checks.example.com/
├── registry.json
└── checks/
└── acme-process-running/
└── 1.0.0/
├── ui.json
└── check-process.ps1
Die Repository-URL, die du später angibst, lautet https://checks.example.com/. octoja löst jeden weiteren Pfad gegen diese URL auf — sie muss also mit einem Schrägstrich enden, wenn das Repository in einem Unterverzeichnis liegt: https://example.com/octoja-checks/. Der Pfad checks/{id}/{version}/ui.json ist fest vorgegeben: octoja baut ihn aus id und version der registry.json, die Ordnernamen müssen also exakt diesen Werten entsprechen.
Schritt 1 — Das Check-Skript schreiben
octoja übergibt dem Check seine Konfiguration als base64-kodiertes JSON in einer einzigen Zeile auf der Standardeingabe und schließt den Datenstrom danach. Das Skript antwortet mit base64-kodiertem JSON auf der Standardausgabe, eingefasst in zwei Markerzeilen. Speichere dies als checks/acme-process-running/1.0.0/check-process.ps1:
$ErrorActionPreference = 'Stop'
function Write-CheckResult {
param([hashtable] $Result)
$json = $Result | ConvertTo-Json -Depth 10 -Compress
$bytes = [System.Text.Encoding]::UTF8.GetBytes($json)
$base64 = [Convert]::ToBase64String($bytes)
[Console]::Out.WriteLine('<<<OCTO_RESULT_BEGIN>>>')
[Console]::Out.WriteLine($base64)
[Console]::Out.WriteLine('<<<OCTO_RESULT_END>>>')
}
try {
$encodedInput = [Console]::In.ReadLine()
if ([string]::IsNullOrWhiteSpace($encodedInput)) {
throw 'The check received no input.'
}
$inputBytes = [Convert]::FromBase64String($encodedInput)
$checkInput = [System.Text.Encoding]::UTF8.GetString($inputBytes) | ConvertFrom-Json
$processName = [string] $checkInput.processName
if ([string]::IsNullOrWhiteSpace($processName)) {
throw 'No process name was configured.'
}
$processes = @(Get-Process -Name $processName -ErrorAction SilentlyContinue)
Write-CheckResult @{
checkResult = if ($processes.Count -gt 0) { 0 } else { 2 }
processName = $processName
processCount = $processes.Count
running = $processes.Count -gt 0
}
exit 0
}
catch {
[Console]::Error.WriteLine($_.Exception.ToString())
Write-CheckResult @{
checkResult = 3
errorMessage = $_.Exception.Message
}
exit 0
}
Drei Details entscheiden darüber, ob der Check überhaupt funktioniert:
- Die Marker. octoja liest ausschließlich die Bytes zwischen
<<<OCTO_RESULT_BEGIN>>>und<<<OCTO_RESULT_END>>>. Alles andere auf der Standardausgabe — eineWrite-Host-Zeile, ein Modul-Banner, ein versehentlichesWrite-Output— ist damit harmlos. Ohne die Marker liest octoja ersatzweise die gesamte Ausgabe als Ergebnis, und eine einzige zusätzliche Zeile zerstört den Check. - Beende immer mit Exit-Code 0. Bei einem Exit-Code ungleich null verwirft octoja das Ergebnis und meldet Failure, ganz gleich was das Skript geschrieben hat. Deshalb endet auch der Fehlerpfad oben mit
exit 0— er meldet das Problem stattdessen übercheckResult. - Diagnoseausgaben gehören auf die Standardfehlerausgabe. Nutze
[Console]::Error.WriteLine(…). Der Agent legt die Standardfehlerausgabe in seinem eigenen Log auf dem Gerät ab, sodass sie verfügbar ist, wenn ein Check sich seltsam verhält.
Hinweis: bei einem in octoja gebauten Custom-Script-Check ist es genau umgekehrt. Dort umhüllt octoja dein Skript und setzt die Marker selbst — schreibst du sie dort zusätzlich, zerstörst du das Ergebnis. In einem Repository wird dein Skript unverändert heruntergeladen und ausgeführt, das Setzen der Marker ist also deine Aufgabe.
Ergebnisstatus
Das Feld checkResult trägt den Status. Es akzeptiert die Zahl oder den Namen — 2 und "Critical" sind gleichwertig, und der Feldname selbst wird ohne Beachtung der Groß- und Kleinschreibung erkannt.
Halte das Ergebnis klein. Die dekodierte Ausgabe ist auf 1 MB begrenzt; darüber bleibt der Status erhalten, die Werte entfallen. Vollständige Logs und Inventarlisten gehören nicht in ein Check-Ergebnis.
Schritt 2 — Den Check in der registry.json beschreiben
Der Index liegt im Wurzelverzeichnis des Repositories und listet jeden Check auf, den du anbietest. Speichere ihn als registry.json:
{
"version": "1.0",
"generated": "2026-08-11T12:00:00Z",
"baseUrl": "https://checks.example.com/",
"checks": [
{
"id": "acme-process-running",
"name": {
"en": "Process running",
"de": "Prozess läuft"
},
"description": {
"en": "Checks whether a Windows process is running.",
"de": "Prüft, ob ein bestimmter Windows-Prozess läuft."
},
"author": "ACME",
"icon": { "type": "component", "library": "lucide", "name": "activity" },
"category": "services",
"tags": ["windows", "process", "service"],
"platforms": ["win-x64"],
"version": "1.0.0",
"uiVersion": "ui-1",
"interval": 5,
"timeoutSeconds": 60,
"executionLocation": "Device",
"executable": {
"win-x64": {
"downloadUri": "https://checks.example.com/checks/acme-process-running/1.0.0/check-process.ps1",
"executableName": "check-process.ps1",
"fileType": "Ps1",
"executionType": "PowerShell"
}
}
}
]
}
Die Felder, bei denen am häufigsten etwas schiefgeht:
interval ist das Standard-Ausführungsintervall in Minuten, und executionLocation steht auf Device für einen Check, der auf dem Agent läuft — das ist der Standard und kann weggelassen werden. Lokalisierte Texte nehmen die Schlüssel en, de, fr und nl; gib immer en an, denn das ist der Rückfallwert, wenn die Sprache eines Lesers fehlt. Die vollständige Feldliste steht in der Check-Repository-Referenz.
Schritt 3 — Konfiguration und Ergebnisanzeige in der ui.json definieren
Das Manifest legt fest, was beim Zuweisen des Checks konfiguriert wird und wie das Ergebnis dargestellt wird. Speichere es als checks/acme-process-running/1.0.0/ui.json:
{
"id": "acme/acme-process-running",
"input": {
"processName": {
"type": "string",
"title": { "en": "Process name", "de": "Prozessname" },
"description": {
"en": "Process name without .exe, for example spoolsv.",
"de": "Prozessname ohne .exe, zum Beispiel spoolsv."
},
"default": "spoolsv",
"isRequired": true
}
},
"output": {
"detailRenderer": {
"type": "react-inline",
"code": "export default function ProcessRunning({ data, lang }) {\n const de = (lang || '').startsWith('de');\n const running = data?.running === true;\n const failed = Number(data?.checkResult) === 3;\n const color = failed || !running ? 'text-destructive' : 'text-green-600 dark:text-green-400';\n const label = failed ? (de ? 'Check fehlgeschlagen' : 'Check failed') : running ? (de ? 'Prozess läuft' : 'Process is running') : (de ? 'Prozess läuft nicht' : 'Process is not running');\n return <div className='rounded-lg border border-border p-3'><div className={'text-sm font-medium ' + color}>{label}</div>{data?.processName && <div className='mt-1 text-xs text-muted-foreground'>{data.processName}{running ? ' · ' + (data.processCount ?? 0) : ''}</div>}</div>;\n}"
}
}
}
Jeder Schlüssel unter input wird zu einem Feld im Zuweisungsdialog und kommt im Eingabe-JSON des Skripts unter genau diesem Namen an. Das Beispiel deklariert processName, das Skript liest also $checkInput.processName — benennst du eines um, musst du auch das andere umbenennen.
Gängige Feldtypen sind string, text, integer, number, boolean, select, radio und array. Nutze password für alles Geheime: der Wert wird in der Oberfläche maskiert und bei der Zuweisung gespeichert, statt in deinen veröffentlichten Dateien zu liegen.
Fragt der Check ein entferntes Gerät ab statt der Maschine, auf der er läuft, deklariere das Ziel als string-Feld mit dem Schlüssel host, ip, ipaddress, target, url, endpoint oder address — damit lässt sich der Check Netzwerkgeräten zuweisen, wo octoja das Feld ausblendet und bei jedem Lauf die Adresse des Geräts einsetzt. Wähle string, nicht text — text ist trotz des Namens kein Eingabefeld, sondern eine statische Beschriftung, und als Host-Schlüssel zählt nur string. Die vollständigen Regeln, einschließlich der Array-Variante und der SNMP-Zugangsdaten-Schlüssel, stehen in der Check-Repository-Referenz.
Der detailRenderer ist optional — lässt du ihn weg, werden die Ergebnisfelder unverändert aufgelistet. Ist er vorhanden, ist sein code eine kleine React-Komponente, die data (dein Ergebnis-JSON), input (die konfigurierten Werte) und lang erhält. JSX ist erlaubt und wird für dich kompiliert. Renderer-Code aus einem Community-Repository läuft in einem isolierten Bereich ohne Zugriff auf die octoja-Sitzung, Cookies oder die umgebende Seite — er kann also das Ergebnis anzeigen und sonst nichts.
Schritt 4 — Das Skript lokal testen
Probiere das Protokoll vor dem Veröffentlichen auf deinem eigenen Rechner aus. Führe dies in dem Ordner aus, in dem das Skript liegt:
$inputJson = '{"processName":"spoolsv"}'
$inputBytes = [System.Text.Encoding]::UTF8.GetBytes($inputJson)
$inputBase64 = [Convert]::ToBase64String($inputBytes)
$raw = ($inputBase64 | powershell.exe -ExecutionPolicy Bypass -File .\check-process.ps1) -join "`n"
if ($raw -match '(?s)<<<OCTO_RESULT_BEGIN>>>(.*?)<<<OCTO_RESULT_END>>>') {
[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Matches[1].Trim()))
} else {
Write-Warning 'No result block found — check what the script writes to standard output.'
$raw
}
Diese Extraktion macht octoja mit der Ausgabe genauso. Die letzte Zeile sollte dein Ergebnis-JSON ausgeben, zum Beispiel:
{"checkResult":0,"running":true,"processName":"spoolsv","processCount":1}
Führe es noch einmal mit einem nicht laufenden und mit einem leeren Prozessnamen aus, um zu bestätigen, dass du 2 und 3 erhältst — und jedes Mal einen Exit-Code von 0. Die Reihenfolge der Felder im JSON ist nicht festgelegt, nur die Feldnamen zählen.
Beachte, dass in diesem Befehl kein -NoProfile steht, denn der Agent verwendet es ebenfalls nicht — er ruft powershell -ExecutionPolicy Bypass -File mit Windows PowerShell auf. Ohne -NoProfile zu testen bedeutet: ein systemweites PowerShell-Profil, das beim Start etwas ausgibt, fällt hier auf statt erst in der Produktion. Teste auf einem Gerät, das deinen Zielsystemen ähnelt.
Schritt 5 — Die Dateien veröffentlichen
Kopiere die drei Dateien auf den Webserver und behalte dabei den Aufbau aus Was du veröffentlichst bei:
/registry.json/checks/acme-process-running/1.0.0/ui.json/checks/acme-process-running/1.0.0/check-process.ps1
Prüfe anschließend, dass alle drei erreichbar sind:
curl https://checks.example.com/registry.json
curl https://checks.example.com/checks/acme-process-running/1.0.0/ui.json
curl https://checks.example.com/checks/acme-process-running/1.0.0/check-process.ps1
Ein privates Repository darf über alle drei Pfade hinter einer HTTP-Basic-Authentifizierung liegen. octoja sendet dieselben Zugangsdaten beim Lesen der Registry, beim Abruf eines Manifests und beim Herunterladen eines Skripts — und kombiniert Zugangsdaten nur mit einer https://-URL, außer bei Loopback-Adressen wie localhost.
Schritt 6 — Das Repository anbinden
Gehe auf der octoja-Instanz zu Einstellungen → Repositories, klicke auf Community-Repository hinzufügen und trage einen Anzeigenamen sowie https://checks.example.com/ ein. Bestätige mit Verbindung prüfen, dass octoja die Registry lesen kann — es meldet, wie viele Checks es gefunden hat — und speichere dann. Die vollständige Anleitung inklusive Basic-Auth-Zugangsdaten und der Bedeutung jeder Fehlermeldung findest du unter Ein Community-Check-Repository anbinden.
Dein Check erscheint danach mit dem Abzeichen Community in der Check-Bibliothek und lässt sich über ein Konfigurationspaket zuweisen.
Ein Update veröffentlichen
Ändert sich das Skript, veröffentliche es unter einer neuen Version:
- Wähle eine neue Versionsnummer, zum Beispiel
1.0.1. - Lege
/checks/acme-process-running/1.0.1/an und veröffentliche Skript undui.jsondort. - Aktualisiere
versionund diedownloadUrides Executables in derregistry.json. - Setze
generatedauf die aktuelle UTC-Zeit.
Die Version ist Teil des Pfads, unter dem der Agent das Skript ablegt, und ein Agent, der diese Version bereits hat, lädt sie nie erneut. Ein geändertes Skript unter unveränderter Version erreicht deshalb keine Geräte, die die alte Fassung ausgeführt haben — sie behalten ihre Kopie. Erhöhe die Version immer.
Ändert sich nur die ui.json, ersetze sie an Ort und Stelle, ändere uiVersion und setze generated. Die Geräte behalten das Skript, das sie schon haben, und die neuen Konfigurationsfelder und die neue Ergebnisanzeige kommen mit der nächsten Synchronisation.
octoja synchronisiert angebundene Repositories automatisch einmal pro Stunde. Jetzt synchronisieren auf der Seite Repositories erzwingt eine sofortige Aktualisierung.
Sicherheit
Ein Repository gibt ausführbaren Code an jedes Gerät weiter, das seine Checks ausführt — behandle das Veröffentlichen deshalb in beide Richtungen als Vertrauensverhältnis.
Dasselbe gilt, wenn du ein fremdes Repository anbindest: nimm nur eines aus einer Quelle auf, der du vertraust.
Fehlerbehebung
Der Verbindungstest meldet, dass die Registry nicht gelesen werden konnte. Die URL hat geantwortet, aber octoja fand direkt darunter keine lesbare registry.json. Prüfe, ob die Basis-URL mit einem Schrägstrich endet, und rufe registry.json mit curl ab, um zu bestätigen, dass der Server die Datei zurückgibt und nicht ein Verzeichnislisting oder eine Fehlerseite.
Der Check ist gelistet, hat aber keine Konfigurationsfelder. Das Manifest konnte bei der ersten Synchronisation nicht geladen werden. Prüfe, ob checks/{id}/{version}/ui.json für genau die id und version aus der Registry erreichbar ist — ein abweichender Ordnername ist die übliche Ursache — und nutze dann Jetzt synchronisieren.
Der Check meldet auf dem Gerät Failure. Entweder hat das Skript mit einem Exit-Code ungleich null geendet, oder octoja konnte aus seiner Ausgabe kein Ergebnis lesen. Prüfe, ob das Skript auf jedem Pfad mit exit 0 endet und ob die Marker exakt wie gezeigt geschrieben werden. Der Agent bewahrt die Standardfehlerausgabe des Skripts in seinem Check-Log auf dem Gerät auf.
Ein geändertes Skript zeigt keine Wirkung. Die Version in der registry.json hat sich nicht geändert, die Agenten haben also ihre vorhandene Kopie behalten. Veröffentliche unter einer neuen Versionsnummer.
Die Check-Bibliothek zeigt den neuen Check nicht. Erhöhe generated in der registry.json — die stündliche Synchronisation überspringt ein Repository, das nichts Neueres meldet — oder nutze Jetzt synchronisieren. Zum Sehen der Check-Bibliothek brauchst du außerdem die Berechtigung Monitoring-Check-Verwaltung.
Aktuelle Störungen und bekannte Einschränkungen findest du unter Produktstatus & bekannte Einschränkungen.
Verwandte Artikel
- Check-Repository-Referenz — jedes Feld von Registry und Manifest und wie die Synchronisation entscheidet, was geladen wird.
- Ein Community-Check-Repository anbinden — ein Repository hinzufügen, bearbeiten und synchronisieren.
- Einen Custom-Script-Check konfigurieren — einen Check in octoja bauen, statt eines zu hosten.