KI-Agenten sollten https://remote.ivjn.us/SKILL.md abrufen.

# HTTP-API für Agent-Operationen

> HTML: https://remote.ivjn.us/docs/de/http-api/

Native versiegelte Operationen unterstützen jetzt reine HTTP-Clients. Verwenden Sie `remote-cli --transport http` oder `REMOTE_TRANSPORT=http`. `POST /api/sealed/connections` erstellt eine Verbindung, `POST /input` übermittelt signierte Handshakes oder Chiffretext, `GET /events` ruft Ergebnisse ab und `DELETE` schließt die Verbindung. Kryptografie und Geräteautorisierung bleiben beim Client. Die bisherigen Geräteaktions-Endpunkte liefern weiterhin 409; die Geräteverbindung bleibt unverändert. HTTP-Anfragen müssen dieselbe Exchange-Instanz erreichen. Nach 90 Sekunden ohne Aktivität läuft die Verbindung ab. Eingaben werden seriell gesendet; Wiederholungen behalten Sequenznummer und Chiffretext bei. Verarbeitete Ereignisse werden mit `after` bestätigt. Web Console, Mobilgeräte und LAN behalten ihren Transport. Vorlagen erfordern weiterhin `manage`.

https://remote.ivjn.us/sealed-http.md


HTTP `409` / `Sealed sessions are required.` bei Geräteaktionen verlangt eine versiegelte Sitzung; ein Bearer-Token allein genügt nicht. Verwenden Sie `remote-cli --profile alice exec lab -- uname -a` oder bei reinem HTTP `remote-cli --profile alice --transport http exec lab -- uname -a`. Die CLI führt weiterhin den signierten Handshake und die Ende-zu-Ende-Verschlüsselung aus.


## Basis-URL

Produktiver Exchange:

```text
https://exchange.ivjn.us
```

Bei selbst gehosteten Bereitstellungen verwenden Sie Ihren eigenen HTTPS-Origin. Alle folgenden Pfade sind relativ zu dieser Basis. Senden Sie JSON mit `Content-Type: application/json`.

## Authentifizierung

Jede Route unter `/api/*` (außer öffentlichen Hilfsrouten für Authentifizierung/Registrierung) erfordert:

```http
Authorization: Bearer <token>
```

`<token>` kann Folgendes sein:

1. **Sitzungstoken einer Passwortanmeldung** aus `POST /api/auth/login`
2. **Primärer API-Schlüssel eines Operators** (das langlebige Token, das beim Erstellen des Operator-Kontos einmalig angezeigt wird)

Beide werden vom selben `authenticate_operator`-Pfad akzeptiert, den auch Konsole und CLI verwenden.

### Passwortanmeldung

```bash
curl -sS -X POST 'https://exchange.ivjn.us/api/auth/login' \
  -H 'Content-Type: application/json' \
  -d '{"email":"alice@example.com","password":"your-password-here"}'
```

`email` ist die Kontoidentität und muss eine gültige Adresse sein. Der Dienst normalisiert sie (Leerzeichen entfernen, Kleinschreibung) und verlangt eine verifizierte Adresse. Bestehende Bereitstellungen können weiterhin `{ "username": "alice", "password": "..." }` senden, bis diese Konten auf E-Mail umgestellt sind; ein Wert mit `@` in `username` wird als E-Mail-Adresse behandelt. Ungültige Adressen werden vor der Passwortprüfung abgelehnt.

Beispielantwort:

```json
{
  "username": "alice",
  "email": "alice@example.com",
  "tenant_id": "00000000-0000-0000-0000-000000000000",
  "token": "<prefix>.<secret>",
  "expires_in": 2592000
}
```

Verwenden Sie `token` als Bearer-Zugangsdaten. Rufen Sie `POST /api/auth/logout` mit demselben Header auf, um diese Sitzung zu widerrufen. Primäre Operator-API-Schlüssel werden durch die Abmeldung nicht widerrufen.

### Operator-API-Schlüssel (primäres Token)

```bash
TOKEN='<prefix>.<secret>'   # issued at operator creation; store offline
curl -sS 'https://exchange.ivjn.us/api/devices' \
  -H "Authorization: Bearer ${TOKEN}"
```

## Geräte auflisten

```bash
curl -sS 'https://exchange.ivjn.us/api/devices' \
  -H "Authorization: Bearer ${TOKEN}"
```

Jeder Eintrag enthält `id`, `name`, `online`, `enabled`, `info`, Zeitstempel und `active_sessions`. Das Geräteinventar besteht aus Exchange-Metadaten. Das Auflisten eines Geräts stellt keine versiegelte Sitzung her, wie sie für dessen Bedienung erforderlich ist.

## HTTP-Ablehnung von Geräteaktionen und Sitzungsverwaltung

Alle Geräteoperationen verwenden versiegelte WebSocket-Sitzungen. Die folgenden HTTP-Routen können den Client-/Geräte-Handshake nicht herstellen und liefern daher HTTP `409` für authentifizierte, autorisierte Anfragen, die die Eingabevalidierung bestehen. Die Ablehnung erfolgt vor der Weiterleitung an das Gerät: Kein Befehl, keine Sensoranfrage, Netzwerkprüfung, Aktualisierung, ubus-Aufruf, Hold-Auftragsanfrage oder feste Befehlsvorlage wird an den Agent gesendet. Auch ein verbundenes Gerät macht diese Routen nicht ausführbar.

Authentifizierung, Berechtigungen und Eingabevalidierung gelten weiterhin. Ungültige Zugangsdaten liefern `401`, verbotene Operationen `403`; ungültige Befehle, URLs oder Anfragetexte behalten ihre Validierungsfehler einschließlich `400`. Der Versiegelungsfehler `409` ersetzt keine vorhergehenden Validierungs- oder Autorisierungsfehler. Berechtigungsprüfungen für feste Befehlsvorlagen folgen deren Richtlinie; auch bestandene Prüfungen erlauben keine HTTP-Ausführung.

| Methode und Pfad | Verhalten nach Authentifizierung, Autorisierung und Validierung |
| --- | --- |
| `POST /api/devices/{id}/exec` | `409`: innerhalb einer versiegelten WebSocket-Sitzung ausführen |
| `GET /api/devices/{id}/exec/holds` | `409`: Hold-Aufträge innerhalb einer versiegelten WebSocket-Sitzung prüfen |
| `POST /api/devices/{id}/exec/holds/{job_id}/kill` | `409`: Hold-Aufträge innerhalb einer versiegelten WebSocket-Sitzung beenden |
| `GET /api/devices/{id}/exec/holds/{job_id}/cat` | `409`: Hold-Auftragsausgabe innerhalb einer versiegelten WebSocket-Sitzung lesen |
| `POST /api/devices/{id}/sensors` | `409`: Sensoren innerhalb einer versiegelten WebSocket-Sitzung erfassen |
| `POST /api/devices/{id}/net` | `409`: Netzwerkprüfungen innerhalb einer versiegelten WebSocket-Sitzung ausführen |
| `POST /api/devices/{id}/update` | `409`: Aktualisierungen innerhalb einer versiegelten WebSocket-Sitzung prüfen oder anwenden |
| `POST /api/devices/{id}/ubus` | `409`: ubus innerhalb einer versiegelten WebSocket-Sitzung aufrufen |
| `POST /api/devices/{id}/commands/{cmd}/run` | `409`: feste Befehlsvorlage abrufen und innerhalb einer versiegelten WebSocket-Sitzung mit einem vom Gerät autorisierten Manage-Grant ausführen |

Diese gültige Aktionsanfrage prüft beispielsweise die Ablehnung; sie ist keine Schnittstelle zur Befehlsausführung:

```bash
curl -i -X POST "https://exchange.ivjn.us/api/devices/${DEVICE_ID}/exec" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"command":"uname -a","timeout_secs":30}'
```

Erwarten Sie HTTP `409` mit der englischen Fehlermeldung `Sealed sessions are required.` (entsprechend der Anfragesprache lokalisiert). Stellen Sie zur Ausführung der Operation eine native versiegelte WebSocket-Sitzung her. Diese HTTP-Route liefert keine erfolgreiche Antwort mit Befehlsausgabe.

### Exchange-Sitzungsverwaltung

Sitzungsinventar und Abbruch bleiben über HTTP verfügbar, da sie Exchange-Routing-Metadaten verwalten. Sie führen keine Geräteoperationen aus und öffnen keine zusätzliche Gerätesitzung.

| Methode und Pfad | Erfolgsantwort |
| --- | --- |
| `GET /api/devices/{id}/sessions` | `{sessions:[DeviceSessionInfo], limit, count}` |
| `POST /api/devices/{id}/sessions/{session_id}/kill` | `{session_id, released}` |

Die Felder von `DeviceSessionInfo` sind `session_id` (numerische geräteseitige ID), `operator_session_id`, `kind`, `hold`, `age_secs` und `deadline_unix_ms`. `limit` ist `max_sessions_per_device` (standardmäßig 32); `count` ist die aktuelle Listenlänge. Die Auflistung liest direkt den lokalen Exchange-Hub-Zustand; ein lokal offline befindliches Gerät liefert `503`. Kill verwendet die **geräteseitige** ID, sendet den passenden Abbruch (auch für gehaltenes Exec) und gibt die Route lokal frei, selbst wenn das Gerät nicht antwortet. Eine fehlende Sitzung oder ein lokal offline befindliches Gerät liefert HTTP `200` mit `released:false`. Operatoren mit Leserechten erhalten bei beiden Endpunkten `403`.

```bash
curl -sS "https://exchange.ivjn.us/api/devices/${DEVICE_ID}/sessions" \
  -H "Authorization: Bearer ${TOKEN}"

curl -sS -X POST "https://exchange.ivjn.us/api/devices/${DEVICE_ID}/sessions/${SESSION_ID}/kill" \
  -H "Authorization: Bearer ${TOKEN}"
```

Bevor Exchange eine volle Sitzungsgrenze ablehnt, bereinigt es Routen nicht mehr verbundener Operatoren und Routen nach Ablauf ihrer Frist: Exec-Zeitlimit + 15 Sekunden, Dateiübertragung 3600 Sekunden + 15 Sekunden, andere Arten 24 Stunden. Gehaltenes Exec innerhalb seiner Frist wird bei Operator-Trennung abgekoppelt, statt beendet zu werden.

### Native versiegelte CLI-Operationen

Die CLI stellt versiegelte Sitzungen im Rahmen normaler Geräteoperationen her. Schließen Sie zuerst die Genehmigung der Arbeitsbereichs-Root und die Geräteautorisierung ab; siehe [versiegelte Sitzungen](/docs/de/sealed-sessions.md). Klartext kann nicht gewählt werden. Normale Befehle benötigen keinen zusätzlichen Versiegelungsparameter:

```bash
remote-cli exec "$DEVICE_ID" -- uname -a
remote-cli shell "$DEVICE_ID"
remote-cli exec-holds "$DEVICE_ID"
remote-cli exec-hold-cat "$DEVICE_ID" "$JOB_ID"
remote-cli exec-hold-kill "$DEVICE_ID" "$JOB_ID"
remote-cli sensors "$DEVICE_ID" --json
remote-cli net "$DEVICE_ID" ping 1.1.1.1
remote-cli net "$DEVICE_ID" http https://example.com/
remote-cli ubus "$DEVICE_ID" system board '{}'
remote-cli commands list "$DEVICE_ID"
remote-cli commands run "$DEVICE_ID" "$COMMAND_ID"
```

Hold-Auftragsoperationen laufen innerhalb versiegelter Sitzungen; die Exchange-Sitzungsverwaltung ist separat über `remote-cli sessions` und `remote-cli session-kill` verfügbar. Das Abrufen von Metadaten fester Befehlsvorlagen autorisiert keine Ausführung: Das Gerät muss den Manage-Grant des versiegelten Operators akzeptieren.

## Schlüsselverzeichnis und Grant-Weiterleitung für versiegelte Sitzungen

Exchange speichert öffentliche Operator-Schlüssel und vom Eigentümer signierte `rsg1.`-Grant-Tokens, damit Clients einander finden. **Dies ist keine Autorisierung.** Geräte prüfen die Eigentümersignatur weiterhin anhand ihrer lokalen Richtlinie. Exchange indiziert lediglich die Token-Nutzlast (`owner_key`, `operator_key`, `device_id`, `level`, `expires_at`) und wertet einen Upload niemals als Berechtigung. Exchange kann Root-Signaturen nicht fälschen, jedoch Zugangsdaten zurückhalten, einem automatisch genehmigenden Root-Client eine falsche Mitgliederliste präsentieren, Metadaten beobachten oder den Dienst verweigern.

Die Arbeitsbereichs-Root (`rsr1.`) und Grant-Einladungen (`rsi1.`) verwenden dieselbe Art der Weiterleitung. Exchange prüft das Token-Format und die Ed25519-Selbstsignatur (eine Einladung muss außerdem zum gespeicherten Root-Schlüssel passen) und entscheidet **nicht**, welche Operator-Schlüssel genehmigt sind. `GET /api/seal/roster` ist eine Superadmin-Ansicht der Mitglieder, Rollen und `operator_device_grants`. Clients dürfen einen neu aufgetauchten Schlüssel erst vertrauen, wenn der Root-Client ihn lokal genehmigt. Siehe `docs/sealed-sessions.md`.

Jedes authentifizierte Arbeitsbereichsmitglied darf Schlüssel auflisten, seinen eigenen Schlüssel registrieren und Grants hochladen (Grants authentifizieren sich selbst). `DELETE` eines Schlüssels ist auf das registrierende Konto beschränkt (Administratoren dürfen beliebige löschen). `DELETE` eines Grants ist auf den Uploader beschränkt (Administratoren dürfen beliebige löschen). `GET /api/seal/grants` überspringt abgelaufene Zeilen.

Der Nachweis zur Schlüsselregistrierung ist eine Ed25519-Signatur über:

```text
remote-seal-key-register:v1:<workspace_id>:<operator_id>:<public_key>:<issued_at_ms>
```

`issued_at_ms` muss innerhalb von ±10 Minuten der Serveruhr liegen. `public_key` und `proof` verwenden Standard-Base64 (32-Byte-Schlüssel, 64-Byte-Signatur).

| Methode und Pfad | Anfragetext | Erfolg |
| --- | --- | --- |
| `POST /api/seal/keys` | `{"public_key","label"?,"proof","issued_at_ms"}` | registrierter Schlüssel (Upsert für dasselbe Konto) |
| `GET /api/seal/keys` | — | `[{operator_id, username, email, public_key, label, created_at, last_seen_at}]` |
| `DELETE /api/seal/keys/{key}` | — | `204` (`{key}` ist der URL-codierte öffentliche Schlüssel) |
| `POST /api/seal/grants` | `{"tokens":["rsg1.…"]}` | `{stored:[…], errors:[…]}` |
| `GET /api/seal/grants?mine=1` | — | Grants, deren `operator_key` einer der registrierten Schlüssel des Aufrufers ist. Jeder Grant, dessen `owner_key` nicht die veröffentlichte Root ist, enthält außerdem `chain` (Zertifikat-Tokens, möglicherweise leer) |
| `GET /api/seal/grants?owner_key=…` | — | von diesem Eigentümerschlüssel signierte Grants |
| `DELETE /api/seal/grants/{id}` | — | `204` |
| `PUT /api/seal/root` | `{"token":"rsr1.…","rotation":"rsx1.…"?}` | gespeicherte Arbeitsbereichs-Root. Nur Superadmin. Format und eigene Signatur des Schlüssels werden geprüft; dies ist keine Genehmigung. `rotation` (optional) muss von der aktuell veröffentlichten Root signiert sein und die neue Root benennen; Exchange sendet dann `seal_root {token, rotation}` an verbundene versiegelte Geräte. Diese ändern ihre feste Zuordnung nur, wenn die Rotation von ihrer bereits vertrauten Root signiert ist. Ohne gültige Rotation behalten Geräte mit festgelegter alter Root diese bei |
| `GET /api/seal/root` | — | `{workspace_id, root_key, payload, signature, token, created_at}`, oder `404`, wenn keine veröffentlicht ist |
| `POST /api/seal/invitations` | `{"token":"rsi1.…"}` | gespeicherte Einladung. Nur Superadmin und erst, nachdem eine Root existiert |
| `GET /api/seal/invitations` | — | nicht abgelaufene Einladungen, neueste zuerst (`[]`, wenn keine vorhanden) |
| `GET /api/seal/roster` | — | `{members:[{operator_id, username, email, role, restricted, enabled, keys:[{public_key, label}], device_grants:[{device_id, can_manage}]}]}`. Nur Superadmin. Enthält deaktivierte Mitglieder |

Ein nicht verfügbares Verzeichnis umgeht weder die Anforderung versiegelter Sitzungen noch die erforderlichen Geräte-Grants. `GET /api/seal/root` liefert `404`, wenn keine Arbeitsbereichs-Root veröffentlicht wurde.

```bash
# Register this operator key (proof signed locally)
curl -sS -X POST "$BASE/api/seal/keys" \
  -H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \
  -d '{"public_key":"<ed25519-b64>","label":"cli","proof":"<sig-b64>","issued_at_ms":1700000000000}'

curl -sS "$BASE/api/seal/keys" -H "Authorization: Bearer ${TOKEN}"

curl -sS -X POST "$BASE/api/seal/grants" \
  -H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \
  -d '{"tokens":["rsg1.…"]}'

curl -sS "$BASE/api/seal/grants?mine=1" -H "Authorization: Bearer ${TOKEN}"
```

### Zwischenzertifikate (`rsc1.`)

Exchange speichert Zwischenzertifikate, damit ein Nicht-Root-Schlüssel Grants signieren kann, die Geräte akzeptieren. Es **prüft** die Ed25519-Signatur des Ausstellers und **kann kein Zertifikat fälschen**. Der Upload eines Tokens ist keine Autorisierung: Der Aufrufer muss der Operator des Ausstellers sein. Ein von der Root signiertes Zertifikat (`issuer_key` entspricht der veröffentlichten Arbeitsbereichs-Root) darf nur vom Arbeitsbereichs-Superadmin veröffentlicht werden. Ein Zwischenzertifikat darf nur von dem Konto veröffentlicht werden, dessen registrierter Seal-Schlüssel `issuer_key` ist; der Aussteller muss selbst Subjekt eines gespeicherten, nicht widerrufenen, nicht abgelaufenen Zertifikats mit `can_issue` sein. Das untergeordnete Zertifikat muss innerhalb der Stufe, des Gerätebereichs und der Gültigkeit des übergeordneten bleiben (dieselben Prüfungen wie auf Geräten). Eine erneute Veröffentlichung desselben Paars `(issuer_key, subject_key)` ersetzt die ältere Zeile und hebt den Widerruf auf.

`GET /api/seal/certs/chain?subject_key=` liefert `{chain:[tokens]}` entlang der Ausstellerverknüpfungen bis zur veröffentlichten Root, höchstens 4 Zertifikate, das von der Root ausgestellte zuerst, oder `404`. Jedes authentifizierte Mitglied darf es aufrufen. `GET /api/seal/grants?mine=1` ergänzt diese `chain` bei Grants, deren Signierer nicht die Root ist, sodass Clients sie mit dem Grant erhalten. Bei von der Root signierten Grants fehlt `chain`.

`GET /api/seal/certs` listet alle Zeilen einschließlich widerrufener auf (Administratoren und Superadmin). `DELETE /api/seal/certs/{serial}` (Konto des Ausstellers oder Superadmin) setzt `revoked`. Dies stoppt lediglich die Verteilung. Geräte akzeptieren eine bereits vorhandene Kopie bis zu ihrem Ablauf weiter; ein harter Widerruf erfolgt über `revoked_keys` in der Geräterichtlinie. `{serial}` ist die URL-codierte Seriennummer in Standard-Base64.

| Methode und Pfad | Anfragetext | Erfolg |
| --- | --- | --- |
| `POST /api/seal/certs` | `{"token":"rsc1.…"}` | gespeichertes Zertifikat (Upsert nach Aussteller und Subjekt) |
| `GET /api/seal/certs/chain?subject_key=` | — | `{chain:["rsc1.…"]}`, von der Root ausgestelltes zuerst, oder `404` |
| `GET /api/seal/certs` | — | `[{serial, issuer_key, subject_key, token, expires_at_ms, created_at, revoked}]`. Administrator oder Superadmin |
| `DELETE /api/seal/certs/{serial}` | — | `204`. Aussteller oder Superadmin. Stoppt nur die Verteilung |

```bash
curl -sS -X POST "$BASE/api/seal/certs" \
  -H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \
  -d '{"token":"rsc1.…"}'

curl -sS "$BASE/api/seal/certs/chain?subject_key=<ed25519-b64>" \
  -H "Authorization: Bearer ${TOKEN}"
```

### Sicherung des Arbeitsbereichs-Root-Schlüssels (optional)

`PUT` / `GET` / `DELETE /api/seal/backup` speichern einen opaken `rsb1.`-Blob pro Konto. `PUT` und `GET` sind nur Superadmins erlaubt: Der Blob enthält den privaten **Root**-Schlüssel des Arbeitsbereichs, clientseitig mit Argon2id (64 MiB, t=3, p=1) und ChaCha20-Poly1305 verschlüsselt. Die Passphrase ist optional; eine Mindeststärke wird nicht erzwungen. **Bei leerer oder schwacher Passphrase kann jeder Besitzer des Blobs (einschließlich Exchange) den Root-Schlüssel entschlüsseln und sämtliche Geräte kontrollieren.** Der Upload ist optional; Clients können den Root-Schlüssel (`rsk1.`) stattdessen exportieren und offline aufbewahren. Exchange prüft den Präfix `rsb1.` und die Länge von `blob` und entschlüsselt niemals. `GET` ist pro Konto und Prozess auf 5 Anfragen pro Stunde begrenzt (HTTP 429). Lese-, Schreib- und Löschvorgänge werden ausschließlich mit der Bytezahl auditiert. Antworten senden `Cache-Control: no-store`.

| Methode und Pfad | Anfragetext | Erfolg |
| --- | --- | --- |
| `PUT /api/seal/backup` | `{"blob":"rsb1.…"}` | `{blob, updated_at}` |
| `GET /api/seal/backup` | — | `{blob, updated_at}`, oder `404`, wenn nichts gespeichert ist. Ratenbegrenzt |
| `DELETE /api/seal/backup` | — | `204`, auch wenn keine Zeile existiert |

```bash
curl -sS -X PUT "$BASE/api/seal/backup" \
  -H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \
  -d '{"blob":"rsb1.…"}'

curl -sS "$BASE/api/seal/backup" -H "Authorization: Bearer ${TOKEN}"

curl -sS -X DELETE "$BASE/api/seal/backup" -H "Authorization: Bearer ${TOKEN}"
```

## Root-Ketten-Authentifizierung der Browsererweiterung

Browseroperationen verwenden native versiegelte WebSocket-Sitzungen. Die Erweiterung erzeugt selbst einen Ed25519-Operator-Schlüssel. Mit einem Operator-Bearer-Token ruft sie `tenant_id` / `operator_id` von `/api/auth/me` ab und sendet anschließend `POST /api/seal/keys` mit `{public_key, label, proof, issued_at_ms}`. Der Besitznachweis (PoP) signiert:

```text
remote-seal-key-register:v1:<workspace_id>:<operator_id>:<public_key>:<issued_at_ms>
```

Schlüssel und Signatur sind Standard-Base64 (32/64 Bytes); die Zeit muss innerhalb von ±10 Minuten der Serveruhr liegen. Die Registrierung beweist den Schlüsselbesitz, keine Autorisierung. Die Erweiterung synchronisiert `GET /api/seal/root`, `GET /api/seal/grants?mine=1` und Zertifikat-Tokens, die Grants beiliegen oder über `GET /api/seal/certs/chain?subject_key=<url-encoded-key>` abgerufen werden. Grants müssen sich direkt oder über eine gültige Zertifikatskette bis zur vertrauenswürdigen Arbeitsbereichs-Root prüfen lassen und den richtigen Gerätebereich, die richtige Stufe und Gültigkeit aufweisen. Die Registrierung per Gerätetoken bleibt von diesem Verzeichnisablauf mit Operator-Token getrennt.

Die Erweiterung prüft eingehende native Hellos anhand ihrer Root und fügt ihr eigenes signiertes Hello/Grant/Kette in `SealAccept.peer` ein. CLI/Konsole prüfen diesen Nachweis und verlangen `peer.operator_key == accept.device_key`, bevor sie die signierte Antwort akzeptieren. Rust-Agents verwenden ihre normale Root-Richtlinie, wenn die Erweiterung als Operator agiert. Ungültige/fehlende Nachweise führen zur Ablehnung; es gibt keinen separaten Browser-Vertrauensspeicher/keine separate Oberfläche und keinen Legacy-Fallback. TOFU für native Geräte und Arbeitsbereichs-Root bleibt bestehen; die Erweiterung speichert ihre erste Root pro Server/Arbeitsbereich und lehnt Änderungen ab.

**Sicherheit:** Prüfen Sie die erste Root über einen unabhängigen Kanal; PoP oder eine Serverrolle sind keine Root-Genehmigung. Prüfen Sie unbekannte Schlüssel vor automatischer Genehmigung durch den Root-Client. Schützen Sie Endpunktprofile/Schlüssel und gehostete Skripte. Das Löschen aus dem Verzeichnis widerruft keine zwischengespeicherten signierten Zugangsdaten. Exchange kann Metadaten sehen, Zugangsdaten zurückhalten oder den Dienst verweigern; Versiegelung isoliert keinen feindlichen Operator mit vollständigem CDP-Zugriff.

## WebSocket oder HTTPS wählen

| Bedarf | Verwendung |
| --- | --- |
| Befehle, Hold-Aufträge prüfen oder abbrechen, Sensoren, Netzwerkprüfungen, Aktualisierungen, ubus, feste Befehlsvorlagen | Native versiegelte WebSocket-Sitzung |
| Interaktives PTY, Streaming-Ausgabe, Dateiübertragung, Android-Bildschirm/Baum/Tippen | Native versiegelte WebSocket-Sitzung |
| Authentifizierung, Geräteinventar, Schlüssel-/Grant-/Zertifikatsverzeichnis, Root-Backup | HTTPS-API |
| Exchange-Routingsitzungen auflisten und abbrechen | HTTPS-Endpunkte der Sitzungsverwaltung |
| Live-Gerätepräsenz und langlebige Operatorverbindung | WebSocket; Geräteoperationen auf dieser Verbindung erfordern weiterhin Versiegelung |

Browser-Ticket-Ablauf (Zugangsdaten gelangen niemals in die WebSocket-URL):

1. `POST /api/ws-ticket` mit `Authorization: Bearer …`.
2. Mit `GET /ws/browser` verbinden und als ersten JSON-Frame `{"ticket":"…"}` senden.
3. Vor jeder Geräteoperation den nativen versiegelten Client-/Geräte-Handshake herstellen. Grant und Zertifikatskette des Operators müssen sich anhand der vom Gerät vertrauten Root validieren lassen.

Native Clients verwenden `GET /ws/operator` mit Operator-Authentifizierung und derselben Anforderung versiegelter Sitzungen. Eine WebSocket-Verbindung oder erfolgreiche Kontoanmeldung allein autorisiert keine Geräteoperation. Siehe [Webkonsole und Web Push](/docs/de/web-console-and-notifications.md), [Protokoll](/docs/de/websocket-protocol.md) und [versiegelte Sitzungen](/docs/de/sealed-sessions.md).

## Übersicht der Fehlerstatus

| Status | Typische Ursache |
| --- | --- |
| `400` | Ungültige Aktionseingabe, etwa ein leerer Befehl oder eine ungültige Update-Quell-URL; Validierung erfolgt vor der Ablehnung wegen erforderlicher Versiegelung |
| `401` | Fehlendes oder ungültiges Bearer-Token |
| `403` | Konto- oder Geräteberechtigungen autorisieren die Anfrage nicht |
| `409` | Gültige, autorisierte HTTP-Geräteaktion erfordert eine versiegelte WebSocket-Sitzung; keine Weiterleitung an das Gerät |
| `404` | Fehlender Verzeichniseintrag oder unveröffentlichte Arbeitsbereichs-Root gemäß Endpunktvertrag |
| `429` | Endpunktspezifische Ratenbegrenzung, einschließlich Lesen von Arbeitsbereichs-Root-Backups |
| `503` | Das Sitzungsinventar findet das Gerät nicht im lokalen Exchange-Hub-Zustand |

HTTP-Geräteaktionsrouten warten nicht auf Agent-Ausgaben; ihr normales autorisiertes Ergebnis ist daher `409`, kein Ausführungserfolg, Offline-Weiterleitungsfehler oder HTTP-Ausführungszeitlimit. Fehler von Metadaten-, Authentifizierungs- und Verzeichnis-APIs behalten ihre endpunktspezifische Bedeutung.

## Manueller Validierungsplan

Verwenden Sie ein Testkonto und ein Gerät mit bekannten Berechtigungen. HTTP-Ablehnungsprüfungen erfordern keine Ausführung auf einem Gerät:

1. Authentifizieren Sie sich und listen Sie Geräte über HTTPS auf; prüfen Sie Konto- und Geräteberechtigungen.
2. Senden Sie eine gültige Anfrage an jede Geräteaktionsroute der Ablehnungstabelle. Erwarten Sie HTTP `409` und bestätigen Sie, dass keine Aktion den Agent erreicht, einschließlich Lesen von Hold-Aufträgen und Ausführen fester Befehlsvorlagen.
3. Prüfen Sie ungültige Zugangsdaten, gesperrte Konten und ungültige Eingaben separat. Erwarten Sie Authentifizierungs-, Berechtigungs- und Validierungsfehler, statt bei jeder Anfrage `409` anzunehmen.
4. Lesen Sie das Exchange-Sitzungsinventar. Testen Sie den Abbruch einer ausdrücklich ausgewählten entbehrlichen Testsitzung und prüfen Sie `released`; eine fehlende Sitzung liefert `released:false`. Das Auflisten von Sitzungen eines lokal offline befindlichen Geräts liefert `503`.
5. Schließen Sie Root-Genehmigung und Grants ab und führen Sie dann eine harmlose native CLI-Operation aus, etwa `remote-cli exec "$DEVICE_ID" -- uname -a`. Prüfen Sie den Erfolg über den versiegelten Handshake. Ein fehlender oder ungültiger Geräte-Grant muss zur Ablehnung führen.
6. Prüfen Sie Schlüsselregistrierung, Grant-/Zertifikatsabruf, optionales Root-Backup und Browser-Root-Kettenauthentifizierung unabhängig; diese HTTPS-APIs bleiben gemäß den beschriebenen Verträgen verfügbar.

## Audit-Exportziele

Nur PostgreSQL Exchange unterstützt diese API, getrennt nach authentifiziertem Workspace. Nur Administratoren und Superadministratoren; andere Konten erhalten 403. exchange-worker und eine Konfigurationsoberfläche in Web-/Mobil-Console werden nicht unterstützt.

```text
GET    /api/audit/sinks
POST   /api/audit/sinks
PATCH  /api/audit/sinks/{id}
DELETE /api/audit/sinks/{id}
POST   /api/audit/sinks/{id}/test
GET    /api/audit/sinks/status
```

```sh
printf '%s' '{"name":"archive","kind":"webhook","endpoint":"https://audit.example.net/events","secret":"REPLACE-WITH-RANDOM-SECRET","mode":"full"}' | remote-cli audit sink add
remote-cli audit sink list
printf '%s' '{"interval_ms":500,"batch_size":16}' | remote-cli audit sink update 1
remote-cli audit sink test 1
remote-cli audit sink status
remote-cli audit sink remove 1
```

Standard throttled sendet gespeicherte Audit-Einträge: Text über 256 KiB behält 250 KiB am Anfang und 6 KiB am Ende, Dateien nur als Hash, Streams nach Sitzungsende. full erfasst vor dem Kürzen und teilt Original-JSON und Binärdaten in Blöcke (Standard 1 MiB), mit Vorgangs-/Asset-ID, Sequenz, Offset, Endmarkierung, SHA-256 und Gesamtgröße. Bei sealed senden beide Modi nur Metadaten und Verweise/Hashes des Geheimtexts, keinen Endpunkt-Klartext.

Secrets und Header werden verschlüsselt gespeichert und als *** angezeigt. PATCH erhält ***; null entfernt einen Header. Asynchrone dauerhafte FIFO-Warteschlangen und exponentielle Wiederholungen. Volle throttled-Queues verwerfen und zählen die ältesten Einträge; full-Disk-Queues üben Rückstau aus. Status zeigt Erfolg/Fehler, Rückstand, dropped und gaps. Löschen verwirft ausstehende Daten und protokolliert die Anzahl; Konfigurationsänderungen werden in derselben Transaktion auditiert.

Webhooks verwenden JSON-events mit base64-Bytes. X-Remote-Timestamp und X-Remote-Signature: sha256=<hex> prüfen HMAC-SHA256(timestamp + "." + unveränderter Body). OTel verwendet OTLP/HTTP JSON /v1/logs, body.bytesValue für Blöcke, Attribute remote.device.id / remote.operator / remote.action und konfigurierbares service.name. gRPC benötigt einen Collector, kein direkter Export.

Es folgen Standardkonfiguration, Webhook-Nutzlast, Signaturprüfung und Collector-Beispiele. Den unveränderten HTTP-Body prüfen; vor 2xx dauerhaft speichern und anhand delivery_id deduplizieren. In Loki 3.x allow_structured_metadata aktivieren, in Grafana eine Loki-Datenquelle hinzufügen und {service_name="remote-exchange"} abfragen. Für gRPC pipeline exporters auf [otlp/grpc] ändern. In Produktion TLS und Authentifizierung verwenden.

```json
{"name":"collector","kind":"otel","mode":"throttled","endpoint":"https://collector.example.net:4318/v1/logs","headers":{"Authorization":"Bearer REPLACE"},"service_name":"remote-exchange","resource":{"deployment.environment.name":"production"},"batch_size":32,"interval_ms":1000,"chunk_bytes":1048576,"queue_limit":10000,"overflow_bytes":10737418240,"actions":[],"devices":[],"operators":[],"enabled":true,"ca_pem":null}
```

```json
{"version":1,"events":[{"delivery_id":123,"type":"full","mode":"full","entry":{"id":42,"action":"exec","operator_name":"alice","device_id":"dev"},"data":{"kind":"binary","asset_id":"asset-uuid","path":"/data_base64","sequence":0,"offset":0,"last":true,"data_base64":"YWJj","total_size":3,"sha256":"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad","context":{"direction":"output","frame_sequence":0,"stream_offset":0}}}]}
```

```python
import hashlib, hmac, time

def verify(headers, raw_body, secret):
    stamp = headers['X-Remote-Timestamp']
    if abs(time.time() - int(stamp)) > 300:
        raise ValueError('expired webhook')
    expected = 'sha256=' + hmac.new(
        secret.encode(), stamp.encode() + b'.' + raw_body, hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(expected, headers['X-Remote-Signature']):
        raise ValueError('invalid signature')
```

```yaml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 127.0.0.1:4318
processors:
  batch: {}
exporters:
  otlphttp/loki:
    endpoint: http://loki:3100/otlp
  otlp/grpc:
    endpoint: telemetry.example.net:4317
    headers:
      Authorization: "${env:OTEL_AUTHORIZATION}"
    tls:
      insecure: false
service:
  pipelines:
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/loki]
```

| Audit-Feld | OTLP-Feld |
|---|---|
| service_name / resource | Resource service.name / attributes |
| entry.started_at | timeUnixNano |
| entry.id / delivery_id | remote.audit.id / remote.delivery.id |
| entry.device_id / operator_name / action | remote.device.id / remote.operator / remote.action |
| entry.tenant_id | remote.workspace.id |
| entry.started_at / ended_at / detail | remote.started_at / remote.ended_at / remote.result |
| traceId / spanId | traceId / spanId |
| data.data_base64 | body.bytesValue / remote.chunk.* |
| stored events | body.stringValue |
