TCPP 1.3.0
tcpp.digital · Schema 1.3.0 Benutzerhandbuch

TCPP Node

Wie man einen TCPP-Node betreibt und bedient: schemagesteuerte Anfragen zusammenstellen, signierte Frames mit anderen Nodes austauschen, auf Eingegangenes antworten und Identitäten, Peers und Records verwalten.

Klicken Sie eine Abbildung an, um sie zu vergrößern.

Schema 1.3.0
27 Paare aus Anfrage und Antwort, ein gemeinsamer Umschlag.
ES384
Jeder Frame ist ein kompakter JWS über ECDSA P-384.
Eine Binärdatei
Anwendung, Schemas und Beispiele einkompiliert. Nichts zu installieren.
Zur Sprache

Die Oberfläche der Anwendung ist englisch. Bedienelemente stehen deshalb auch hier unter ihrem englischen Namen, genau so, wie sie auf dem Bildschirm und in den Abbildungen heißen.

Was ein Node ist

Ein TCPP-Node ist eine einzelne ausführbare Datei. Sie starten sie, und sie tut zweierlei zugleich: Sie liefert eine schemagesteuerte Web-Anwendung an Ihren Browser aus, und sie nimmt TCPP-Frames anderer Nodes über TLS entgegen. Alles, was sie dafür braucht, ist in die Binärdatei einkompiliert: die JSON-Schemas der Version 1.3.0, die mitgelieferten Beispiel-Nutzlasten und die Browser-Anwendung selbst. Es muss nichts weiter installiert werden, und sie greift auf nichts im Internet zu.

Die Arbeitseinheit ist ein Frame: eine TCPP-Nachricht, vom sendenden Node als kompaktes JWS (ein JWT) signiert und an einen Peer geschickt. Der Node hält jeden gesendeten und jeden empfangenen Frame in einem Log neben der Binärdatei fest, an das nur angehängt wird, und zwar mit Nutzlast, rohem Token, Absender und dem Ergebnis der Signaturprüfung.

Was Sie damit tun können

  • Jeden der 27 TCPP-Anfragetypen in einem Formular ausfüllen, das aus dem Schema erzeugt wird.
  • Ihn signieren, an einen anderen Node senden und dort live ankommen sehen.
  • Eine eingegangene Anfrage über ein Antwortformular beantworten, das aus ihr vorbefüllt ist.
  • Zurechtgelegte Nutzlasten als benannte Records aufbewahren und später erneut senden.
  • Die öffentlichen Schlüssel der Peers hinterlegen und jeden gespeicherten Frame dagegen prüfen lassen.
  • Die vollständige Historie nachlesen: Nutzlast, Token, JOSE-Header, Transportergebnis.

Eine einzige Binärdatei deckt zudem jede Konstellation ab, die bei der Arbeit mit TCPP anfällt. Der Node kann:

  • Frames mit sich selbst austauschen, sodass eine einzelne Instanz für einen vollständigen Umlauf genügt. Ohne --peer ist die eigene Adresse das voreingestellte Ziel.
  • Frames mit weiteren Nodes auf demselben Rechner austauschen, von denen jeder auf einem eigenen Port läuft.
  • Frames mit Nodes auf anderen Hosts über das Netz austauschen. Kommt nichts an, ist die Firewall das Erste, was zu prüfen ist.
  • Die mitgelieferten Beispiel-Nutzlasten laden sowie eigene Testdaten sichern und wieder laden.
  • Ein eigenes Schlüsselpaar erzeugen und die öffentlichen Schlüssel der Peers direkt bei diesen abholen, was in einer etablierten PKI seinen vollen Nutzen entfaltet.
  • Als Gegenstelle dienen, während Sie eine eigene TCPP-Implementierung entwickeln und testen.

Erste Schritte

Was Sie bekommen haben

Eine ausführbare Datei, sonst nichts. Es gibt kein Installationsprogramm, keine Laufzeitumgebung, die vorher eingerichtet werden müsste, und keine Konfigurationsdatei zu schreiben: Legen Sie die Datei in ein eigenes Verzeichnis und starten Sie sie. Der Download ist für ein Betriebssystem und einen Prozessor gebaut; nehmen Sie also die Datei, die zu Ihrem Rechner passt:

DateiLäuft auf
tcpp-node-windows-amd64.exeWindows, Intel- oder AMD-Prozessor (der Normalfall)
tcpp-node-windows-arm64.exeWindows auf ARM
tcpp-node-linux-amd64Linux, Intel oder AMD
tcpp-node-linux-arm64Linux auf ARM, ein Raspberry Pi oder ein ARM-Server
tcpp-node-darwin-amd64macOS, Intel
tcpp-node-darwin-arm64macOS, Apple Silicon (M1 und neuer)

Es ist dasselbe Programm; nur der Maschinencode unterscheidet sich. Jede Datei steht für sich, ohne Bibliotheken danebenzulegen und ohne etwas auszupacken, und keine von ihnen greift ins Internet. Benennen Sie die heruntergeladene Datei in tcpp-node um (unter Windows tcpp-node.exe); so wird sie in diesem Handbuch durchgehend geschrieben.

Geben Sie ihr ein Verzeichnis, das Sie wiederfinden. Alles, was der Node anlegt (seinen Schlüssel, sein Zertifikat, sein Frame-Log, seine Records), schreibt er neben die ausführbare Datei; das Verzeichnis ist also der Node.

Starten

Ohne Argumente gestartet, lauscht er auf Port 8443.

Windows

In der PowerShell oder in cmd, aus dem Verzeichnis des Nodes heraus. Ein Doppelklick funktioniert auch, nur schließt sich das Fenster dann zusammen mit dem Node, und Sie sehen nie, woran er gescheitert ist.

.\tcpp-node.exe

macOS und Linux

Eine heruntergeladene Datei ist noch nicht ausführbar, also einmal markieren:

chmod +x tcpp-node
./tcpp-node
Der Download ist nicht signiert

Er trägt kein Code-Signing-Zertifikat, deshalb protestiert jedes System einmal, und nur beim ersten Start:

  • Windows. SmartScreen meldet einen unbekannten Herausgeber: Weitere Informationen → Trotzdem ausführen. Weigert es sich ganz, ist die Datei als aus dem Internet stammend markiert: Rechtsklick → Eigenschaften → Zulassen, dann OK.
  • macOS. Gatekeeper blockiert sie: Ordner öffnen, Rechtsklick auf die Datei → Öffnen und bestätigen, oder die Markierung mit xattr -d com.apple.quarantine tcpp-node entfernen.
  • Linux. Nichts protestiert; chmod +x genügt.

Von da an ist es überall dasselbe Programm. Der erste Start erzeugt eine Identität und gibt aus, was er gleich ausliefern wird:

2026/09/03 18:03:48 created C:\Users\User\tcpp-lab\a\tcpp-key.pem
2026/09/03 18:03:48 created C:\Users\User\tcpp-lab\a\tcpp-cert.pem
2026/09/03 18:03:48 TCPP node CSU / P:NODE-9BED6853
2026/09/03 18:03:48   listening   https://localhost:8443/
2026/09/03 18:03:48   advertising localhost:8443
2026/09/03 18:03:48   signing     ES384, kid 9bed6853
2026/09/03 18:03:48   identity    C:\Users\User\tcpp-lab\a
2026/09/03 18:03:48   frames      0 replayed from tcpp-frames.jsonl
2026/09/03 18:03:48   schemas     27 endpoints
2026/09/03 18:03:48   records     63 in C:\Users\User\tcpp-lab\a\records
2026/09/03 18:03:48   target      localhost:8444
2026/09/03 18:03:48 The certificate is self-signed, so the browser will warn once.

Öffnen Sie https://localhost:8443/. Das Zertifikat, das der Node sich selbst ausgestellt hat, ist selbstsigniert, deshalb unterbricht der Browser einmal mit einer Sicherheitswarnung; gehen Sie daran vorbei (in Chrome: Erweitert → Weiter zu…). Für diesen Host kommt die Warnung nicht wieder, bis der Node ein neues Zertifikat bekommt.

Der Node hat keine Anmeldung

Nichts an der HTTP-Schnittstelle ist authentifiziert. Wer den Port erreicht, kann das Frame-Log lesen, Records speichern, Peers ändern und Frames als dieser Node senden. Betreiben Sie ihn in einem Netz, dem Sie trauen (einem Laptop, einem Laborsegment, einem privaten VPN), nicht unter einer öffentlichen Adresse.

Was auf der Platte entsteht

Der gesamte Zustand liegt neben der Binärdatei (oder in --data-dir). Ein Node ist ein Verzeichnis, das man kopieren kann.

DateiInhaltRechte
tcpp-key.pemprivater ECDSA-P-384-Schlüssel, PKCS#8; das Geheimnis0600
tcpp-cert.pemselbstsigniertes Zertifikat zum selben Schlüssel, 10 Jahre gültig0644
tcpp-node.jsonTCPP-Typ und -Id dieses Nodes0644
tcpp-frames.jsonljeder gesendete und empfangene Frame, ein JSON-Objekt pro Zeile0644
tcpp-peers.jsonbekannte Peers und ihre öffentlichen Schlüssel0644
records/die Record-Bibliothek, <schema>/<name>.json0755

Der Kurztest mit einem Node

Für den Rundlauf brauchen Sie keinen zweiten Node. Ohne --peer ist das Standardziel localhost:<Port>, dieser Node selbst; ein Frame, den er signiert, landet also in seinem eigenen Posteingang. Öffnen Sie Compose, wählen Sie ping, drücken Sie Load…example und dann Sign & Send. Er erscheint sofort unter Inbox.

Das Zwei-Node-Labor dieses Handbuchs

Jede Abbildung in diesem Handbuch stammt aus zwei Nodes auf einem Rechner: A, eine Charge Switch Unit auf Port 8443, und B, ein Trusted Management Host auf Port 8444. Jeder hat sein eigenes Verzeichnis und damit seinen eigenen Schlüssel, sein eigenes Log und seine eigenen Records.

# macOS und Linux: zwei Verzeichnisse, in jedem eine Kopie der Binärdatei
mkdir a b
cp tcpp-node a/ && cp tcpp-node b/

# Terminal 1
a/tcpp-node --port 8443 --node-type CSU --peer localhost:8444
# Terminal 2
b/tcpp-node --port 8444 --node-type TMH --peer localhost:8443
# PowerShell
mkdir a, b
copy tcpp-node.exe a\; copy tcpp-node.exe b\

# Fenster 1
a\tcpp-node.exe --port 8443 --node-type CSU --peer localhost:8444
# Fenster 2
b\tcpp-node.exe --port 8444 --node-type TMH --peer localhost:8443

Öffnen Sie https://localhost:8443/ und https://localhost:8444/ in zwei Browser-Tabs, dann sehen Sie beide Seiten eines Austauschs gleichzeitig.

Das Fenster

Das Fenster des TCPP-Node: Kopfzeile, Navigationsleiste, Schema-Liste, erzeugtes Formular sowie die Bereiche für die ausgehende Anfrage und das JWT.
Die ganze Anwendung. Die Kopfzeile benennt den Node, die Leiste links wechselt die Ansicht, und die drei Spalten gehören zu Compose.

Kopfzeile

Der Chip ganz oben benennt immer den Node, den Sie gerade vor sich haben, was wichtig ist, sobald zwei Tabs offen sind:

  • CSU / P:NODE-9BED6853: TCPP-Modultyp und -Id des Nodes.
  • :8443: der Port, von dem diese Seite ausgeliefert wurde.
  • ES384 · 9bed6853: der Signaturalgorithmus und die kurze Schlüssel-Id, die in jedem signierten Token auftaucht.

Rechts bedeutet LIVE, dass der Browser am Ereignisstrom des Nodes hängt und neue Frames von selbst erscheinen. Bricht die Verbindung ab, wird daraus OFFLINE.

Navigationsleiste

AnsichtWozu sie dient
ComposeEine Anfrage aus einem Schema zusammenstellen und senden.
InboxHier eingegangene Frames. Das rote Abzeichen zählt die ungelesenen.
SentVon diesem Node gesendete Frames, mit ihrem Transportergebnis.
PeersBekannte Nodes, ihre Adressen und öffentlichen Schlüssel.
IdentityDer eigene Schlüssel, der Fingerabdruck und die Dateipfade dieses Nodes.
LogAlles, gesendet wie empfangen, in dichten Zeilen.

Am Fuß der Leiste steht, wie viele Frames der Node zurzeit hält. Kurze Bestätigungen (sent to localhost:8444, peer saved, loaded ping_request/example) erscheinen für einen Moment als blaue Einblendung unten rechts.

Hell und dunkel

Die Anwendung folgt dem Farbschema Ihres Betriebssystems. Sonst ändert sich nichts.

Derselbe Compose-Bildschirm im dunklen Farbschema.
Derselbe Bildschirm, mit dem System auf dunkel gestellt.

Compose

Compose besteht aus drei Spalten: links die Anfrage-Schemas, in der Mitte ein aus dem gewählten Schema erzeugtes Formular und rechts das, was tatsächlich hinausgeht.

Ein Schema wählen

Die linke Spalte listet alle 27 Anfragetypen. Tippen Sie in filter…, um die Liste einzugrenzen; gefiltert wird nach dem Schemanamen, charge lässt also die drei Charge-Endpunkte übrig.

Die Liste der Anfrage-Schemas, gefiltert auf die drei Charge-Endpunkte, mit einem Entwurfspunkt neben einem Eintrag.
Die gefilterte Schema-Liste. Ein kleiner bernsteinfarbener Punkt markiert ein Schema, zu dem ein ungesendeter Entwurf existiert; das gewählte Schema ist hervorgehoben.

Das erzeugte Formular

Die mittlere Spalte wird aus dem JSON-Schema gebaut, nicht von Hand geschrieben, und passt deshalb immer zu dem, was der Node annimmt. Jedes Feld zeigt seinen Namen in Festbreitenschrift, ein rotes *, wenn es Pflicht ist, die Beschreibung aus dem Schema und eine knappe Zusammenfassung seiner Einschränkungen (pattern, max 120, min 3, >= 1).

Im Schema stehtSie bekommen
enumein Auswahlfeld, mit einem Eintrag (none), wenn das Feld entfallen oder null sein darf
format: dateeine Datumsauswahl
format: date-timeeine Datums- und Zeitauswahl, ausgelesen als ISO-Zeitstempel
String, großes maxLengthein mehrzeiliges Textfeld
Stringein Textfeld; fahren Sie darüber, um das geforderte Muster zu sehen
Zahl, Ganzzahlein Zahlenfeld, das minimum und multipleOf mitführt
Booleanein Ankreuzfeld
Objekteine umrandete Gruppe; eine nullbare bekommt einen Schalter set to null, der ihren Inhalt ausblendet
Arraynummerierte Element-Karten mit Remove und darunter eine Schaltfläche + Add …
Objekt ohne Eigenschaftenein rohes JSON-Textfeld, beim Tippen geprüft
Das trigger_set-Formular, gescrollt bis zum Abschnitt record, mit Ankreuzfeldern, einer Array-Element-Karte mit Remove-Schaltfläche und einer Schaltfläche Add trigger_formula.
trigger_set, heruntergescrollt bis record: Ankreuzfelder, ein nummeriertes Array-Element mit Remove und + Add trigger_formula, um ein weiteres anzuhängen.

Was hinausgeht

Die rechte Spalte spiegelt das Formular, während Sie tippen. Outgoing request ist genau das JSON, das signiert wird, und das Abzeichen daneben sagt valid oder wie viele Beanstandungen das Schema gefunden hat. Der Bereich JWT bleibt bis zum Senden leer: Signiert wird im Node, der private Schlüssel gelangt also nie in den Browser. Nach dem Senden steht dort das kompakte Token, und Copy JSON / Copy JWT legen das eine oder das andere in die Zwischenablage.

Send to nimmt ein host:port. Anfangs steht dort das Standardziel des Nodes (--peer), und die bekannten Peers werden als Vorschläge angeboten; an einen reinen Hostnamen oder eine IP wird der eigene Port angehängt. Drücken Sie Sign & Send, und die Statuszeile daneben meldet das Ergebnis.

Der Bereich der ausgehenden Anfrage, das gefüllte JWT-Feld und die Sendezeile, die 202 accepted mit einer Frame-Id meldet.
Nach einem erfolgreichen Senden: das signierte Token und ✓ 202 accepted · frame f-… mit dem Namen des Frames, den Sie nun unter Sent finden.
Entwürfe

Jedes Schema behält seinen eigenen Entwurf, solange der Tab offen ist; Sie können also zu einem anderen Schema springen und zu dem zurückkommen, was Sie gerade tippten. Reset verwirft den Entwurf und baut das Formular leer neu auf. Entwürfe überleben kein Neuladen der Seite; was bleiben soll, speichern Sie als Record.

Die Record-Bibliothek

Ein Record ist eine im Node gespeicherte Nutzlast, unter records/<schema>/<name>.json. Beim ersten Start schreibt der Node jedes in der Binärdatei mitgelieferte Beispiel in dieses Verzeichnis; von da an gehört die Bibliothek Ihnen. Load… und Save… liegen in der Werkzeugleiste des Compose- wie des Antwortformulars.

Das Load-Aufklappfenster mit dem mitgelieferten Beispiel-Record und einem gespeicherten Record namens lab ping.
Load… listet die für dieses Schema gespeicherten Records. Ein shipped-Record kam mit der Binärdatei, ein saved-Record ist Ihrer. × löscht einen, nach Rückfrage.
Das Save-Aufklappfenster mit einem Namensfeld, in dem 'lab ping' steht, und einer Save-Schaltfläche.
Save… fragt nach einem Namen. Es schlägt den zuletzt geladenen vor, warnt vor dem Überschreiben und lehnt Namen außerhalb von Buchstaben, Ziffern, Leerzeichen, Punkt, Unterstrich, Bindestrich ab.

Das Laden eines Records ersetzt den Formularinhalt; das Speichern legt ab, was das Formular gerade enthält. Die Bytes bleiben exakt erhalten, Schlüsselreihenfolge eingeschlossen, denn genau diese Bytes werden signiert, wenn der Record das nächste Mal geladen und gesendet wird.

Die mitgelieferten Beispiele zurückholen

Angelegt wird je Schema-Verzeichnis: ein bereits vorhandenes bleibt unangetastet, ein gelöschter Record bleibt also gelöscht. Löschen Sie records/<schema>/ oder records/ ganz, und die mitgelieferten Beispiele sind beim nächsten Start wieder da. Die eingebetteten Beispiele sind unveränderlich und stets unter /api/example/… verfügbar; das Bearbeiten eines Records kann ihnen nichts anhaben.

Validierung

Das Formular wird beim Tippen gegen das Schema geprüft. Ein Feld, das durchfällt, bekommt einen roten Rahmen und darunter die Begründung, jede Gruppe mit einem fehlerhaften Feld wird umrandet, und das Abzeichen über dem JSON-Bereich zählt, was noch offen ist. Ein Klick darauf springt zum ersten beanstandeten Feld.

Ein Compose-Formular mit einem rot markierten Feld, der Begründung darunter und einem roten Abzeichen '1 issue' über dem JSON-Bereich.
Ein Feld verfehlt sein Muster. Die Meldung unter dem Feld ist die tatsächliche Regel aus dem Schema, und das Abzeichen liest sich 1 issue.

Das Feld, in dem Sie gerade tippen, wird nie markiert; die Markierung erfolgt, wenn Sie es verlassen, damit das Formular während der Arbeit nicht rot aufblitzt.

Beanstandungen verhindern nie das Senden

Sign & Send bleibt aktiv, wie viele Beanstandungen es auch gibt. Einen absichtlich fehlerhaften Record zu senden ist ein legitimer Weg zu prüfen, was ein Peer damit macht. Über das, was angenommen wird, entscheidet der Peer, nicht der Absender.

Geprüft werden type, required, properties, items, minItems, minLength/maxLength, pattern, minimum/maximum, multipleOf, enum, format: date, format: date-time und lokale $ref. Alles andere im Schema wird ignoriert, statt es zu erraten.

Posteingang und Antworten

Alles, was ein anderer Node hierher sendet, landet in Inbox, auch Frames, die sich nicht auswerten ließen; sie werden festgehalten statt verworfen. Neuankömmlinge erscheinen ohne Neuladen und lösen eine Einblendung aus.

Der Posteingang: ein Peer-Filter mit 'any' und CSU-node über einer Liste dreier empfangener Frames mit Ungelesen-Punkten und Signatur-Badges.
Die linke Spalte des Posteingangs. Der obere Bereich filtert nach Absender, der untere listet die Frames, neueste zuerst, mit Mark all read rechts.

Eine Frame-Zeile lesen

Ein einzelner Eintrag der Frame-Liste mit Record-Typ, Richtung, Zeit, Peer, Badges und einer Zuordnungszeile.
Ein Eintrag. Oben: der Record-Typ, seine Richtung und die Uhrzeit. Mitte: der Peer und die Zustands-Badges. Unten: wie er zu einem anderen Frame gehört.

Die dritte Zeile erscheint nur, wenn der Frame Teil eines Paares ist:

  • ↳ answered: eine Ihrer Anfragen, die inzwischen beantwortet wurde.
  • ↳ awaiting response: eine Anfrage, die noch keine Antwort hat.
  • ↳ answers an earlier request: dieser Frame ist die Antwort auf eine.

Frame-Detail

Ein ausgewählter Frame öffnet sich rechts und gilt damit als gelesen.

Eine empfangene Ping-Anfrage, geöffnet: Titel, Herkunft, Peer-Adresse, Zeitstempel, Signatur-Badge, Re-verify-Schaltfläche und der Payload-Reiter mit formatiertem JSON.
Eine empfangene Anfrage. Die Zeile unter dem Titel nennt den Absender, über welche Adresse sie ankam, wann, und wie ihre Signatur ausfiel.

Vier Reiter enthalten alles, was der Node aufbewahrt hat:

ReiterZeigt
Payloaddie dekodierte TCPP-Nachricht, formatiert.
JWTeine einzeilige Zusammenfassung und das rohe kompakte Token, wie es ankam.
Headerden dekodierten JOSE-Header: alg, kid, typ.
TransportFrame-Id, Richtung, Peer, Prüfzustand, Zustellergebnis, Zuordnungen.
Der JWT-Reiter mit 'alg ES384 · typ JWT · kid … · 96-byte signature' über dem rohen kompakten Token.
Der JWT-Reiter. Das Token ist exakt das, was geprüft wurde; auf dem Weg herein wurde nichts neu kodiert.

Re-verify prüft die gespeicherte Signatur erneut gegen den Schlüssel, den der Peer jetzt hat, und meldet das Ergebnis als Einblendung. Es ist die Schaltfläche, die man drückt, nachdem man den Schlüssel eines Peers hinzugefügt oder geändert hat.

Antworten

Ist der geöffnete Frame eine Anfrage und hat der Schemasatz eine passende Antwort, wird unter den Reitern ein Antwortformular angehängt, bereits aus der Anfrage ausgefüllt:

  • Herkunft und Ziel vertauscht, denn Sie antworten dem Absender;
  • record_udt auf jetzt gesetzt und record_pagination um eins erhöht;
  • error_code auf 000000 zurückgesetzt, statt den der Anfrage zu wiederholen;
  • parsed mit einem Eintrag je Record-Schlüssel vorbelegt;
  • references[0] mit dem head der Anfrage gefüllt, was beiden Nodes erlaubt, die zwei Frames einander zuzuordnen.
Der Antwortblock: eine Überschrift Reply: ping_response, ein Hinweis auf das Vorbefüllte und das erzeugte Antwortformular mit vertauschter Herkunft und Ziel.
Der Antwortblock. Beachten Sie die vertauschten origin_type/target_type und die erhöhte record_pagination. Load… und Save… beziehen sich hier auf das Response-Schema.

Die Antwortadresse ist vorbelegt und änderbar. Hat der Absender eine Rückadresse mitgeteilt, wird Ihnen das gesagt; musste der Node sie aus der Verbindung erschließen, werden Sie gebeten, sie zu prüfen. Sign & Send response signiert und stellt zu, Reset reply befüllt das Formular erneut aus der Anfrage.

Eine begonnene Antwort bleibt je Frame erhalten, solange der Tab offen ist; zu einem anderen Frame und zurück zu wechseln verliert sie also nicht. Ist der geöffnete Frame selbst eine Antwort, gibt es kein Formular, nur einen Hinweis auf die Anfrage, die er beantwortet.

Sent

Sent ist dieselbe Anordnung für ausgehende Frames. Ungelesen- Markierungen gibt es hier nicht; worauf es ankommt, ist, ob die Zustellung geklappt hat.

Die Sent-Ansicht: vier ausgehende Frames, einer als 'answered' markiert, einer mit 'send failed', und die Nutzlast der gewählten Ping-Anfrage.
Ausgehende Frames. signed by us bei allen, 202, wo der Peer sie angenommen hat, und send failed, wo niemand zuhörte.

Ein Frame wird festgehalten, ob er zugestellt wurde oder nicht; ein gescheitertes Senden hinterlässt also einen vollständigen Eintrag, Token eingeschlossen, und der Grund steht im Reiter Transport. Sobald die Antwort eintrifft, bekommt die Anfrage ihre Zeile ↳ answered.

Peers und Verifikation

Ein Peer ist ein Node, mit dem Sie Frames ausgetauscht haben, oder einer, den Sie selbst eingetragen haben: ein Name, ein host:port und wahlweise sein öffentlicher Schlüssel. Peers erscheinen von selbst, sobald zum ersten Mal ein Frame in die eine oder andere Richtung läuft, aber nie mit Schlüssel. Den Schlüssel zu hinterlegen ist immer eine bewusste Handlung.

Die Peers-Ansicht: eine Tabelle mit TMH-node samt Schlüssel und lab-csu ohne, sowie die Editorkarte des gewählten Peers.
Zwei Peers: einer, dessen Schlüssel geholt wurde, und einer, der nie kontaktiert wurde. Eine ausgewählte Zeile öffnet sich in der Karte darunter.

Einem Peer seinen Schlüssel geben

  1. Wählen Sie den Peer in der Tabelle, oder drücken Sie + Add peer und tragen Sie Name und Adresse ein.
  2. Fügen Sie entweder sein SPKI-PEM in das Schlüsselfeld ein, oder drücken Sie Fetch from peer, um es von dessen /api/identity zu holen. Der Peer muss gespeichert sein, bevor von ihm geholt werden kann.
  3. Drücken Sie Save. Jeder gespeicherte Frame dieses Peers wird sofort erneut geprüft, und das Abzeichen unter dem Feld wird grün.

Das einzufügende PEM finden Sie auf dem Bildschirm Identity des anderen Nodes hinter Copy PEM. Forget entfernt den Peer; die von ihm gesendeten Frames bleiben im Log und fallen auf no key zurück.

Was die Badges bedeuten

BadgeBedeutung
signature ✓Das Token verifiziert gegen den für diesen Peer gespeicherten Schlüssel.
signature ✗Tut es nicht. Der Frame wurde trotzdem festgehalten; sehen Sie ihn sich an.
no keyFür diesen Peer ist kein Schlüssel hinterlegt, es wurde also nichts geprüft.
unverifiedNicht geprüft, etwa ein Frame, dessen Token sich nicht auswerten ließ.
signed by usDieser Node hat ihn signiert. Steht an allem unter Sent.
Verifizieren ist freiwillig, Signieren nicht

Ein Frame von einem Peer ohne Schlüssel wird festgehalten und angezeigt, nicht abgewiesen; Sie können ein ganzes Labor betreiben, ohne einen einzigen Schlüssel zu hinterlegen. Signiert wird dagegen immer: Jeder Frame, den dieser Node sendet, ist ein ES384-JWS.

Identität

Die Identity-Ansicht: Node-Typ und -Id, angekündigte Adresse, Algorithmus, Schlüssel-Id, Fingerabdruck, Zertifikatsgültigkeit, Startzeit, das öffentliche Schlüssel-PEM und die Dateipfade.
Alles, was dieser Node ist, und wo er es aufbewahrt.
Node
Der TCPP-Modultyp und die Id, als die dieser Node signiert und spricht.
Advertised
Die Adresse, die Peers als X-TCPP-From mitgeteilt wird, damit sie wissen, wohin sie antworten sollen. Mit --advertise zu ändern, wenn Peers Sie unter einem anderen Namen erreichen.
Algorithm
Immer ES384: ECDSA auf P-384 mit SHA-384.
Key id
Aus dem öffentlichen Schlüssel abgeleitet. Ihre ersten acht Zeichen stehen im JOSE-Header jedes Tokens; daran erkennt man auf einen Blick, welcher Schlüssel einen Frame signiert hat.
Fingerprint
SHA-256 über das Zertifikat, zum Vergleichen auf anderem Weg.
Certificate
Selbstsigniert, zehn Jahre, beim ersten Start erzeugt.

Die mittlere Karte enthält den öffentlichen Schlüssel für jeden, der diesen Node verifizieren möchte; Copy PEM legt ihn in die Zwischenablage. Die untere Karte listet die sechs Pfade aus Erste Schritte, aufgelöst.

Den eigenen Schlüssel mitbringen

Dasselbe Paar signiert JWTs und terminiert TLS; die eigene Identität zu verwenden heißt deshalb schlicht, tcpp-key.pem und tcpp-cert.pem neben die Binärdatei zu legen und neu zu starten. Ein Schlüssel ohne Zertifikat bekommt ein frisches, aus ihm abgeleitetes Zertifikat. Ein Zertifikat, das nicht zum Schlüssel passt, ist ein Startfehler statt einer stillen Unstimmigkeit, und ein Zertifikat ohne seinen Schlüssel wird rundheraus abgelehnt, denn ein Zertifikat kann nichts signieren.

Log

Die Log-Ansicht: Filter für Richtung und Peer, ein Suchfeld und sechs dichte Zeilen mit Zeitstempel, Richtung, Peer, Record-Typ und Zustand.
Beide Richtungen an einem Ort. Die Zeile unten nennt die Datei, aus der diese Zeilen gelesen werden.

Jede Zeile besteht aus einem Zeitstempel, SENT oder RECV, der Peer-Adresse, Record-Typ und Richtung sowie einer Zustandsspalte: dem HTTP-Status oder ERR bei ausgehenden Frames, dem Prüfergebnis bei eingehenden. Filtern Sie nach Richtung oder Peer, suchen Sie über Record-Typ, Herkunft, Ziel und Frame-Id, und drücken Sie Refresh, um die Liste neu zu holen.

Die Datei dahinter

tcpp-frames.jsonl wird nur angehängt: ein JSON-Objekt pro Zeile, nie umgeschrieben, mit dem Prüfzustand wie er in jenem Moment war. Beim Start spielt der Node die letzten --log-tail Zeilen wieder ein (standardmäßig 500), damit Zuordnung und Historie einen Neustart überleben, und prüft die Signaturen erneut gegen die Schlüssel, die Sie jetzt haben. Nach einem Neustart ist nichts ungelesen. Zeilen, die sich nicht auswerten lassen, werden mit einer Warnung übersprungen, statt den Node anzuhalten.

Arbeitsabläufe

A. Die erste Anfrage senden

  1. Öffnen Sie Compose und wählen Sie ping in der Schema-Liste.
  2. Drücken Sie Load… und wählen Sie example. Das Formular füllt sich, und das Abzeichen wird valid.
  3. Ändern Sie, was Sie mögen; origin_id und target_id sind die üblichen ersten Stationen.
  4. Prüfen Sie Send to. Dort steht das Standardziel; die Auswahlliste bietet bekannte Peers an.
  5. Drücken Sie Sign & Send. ✓ 202 accepted heißt, der Peer hat ihn genommen.
  6. Öffnen Sie Sent, um den Frame, sein Token und sein Transportergebnis zu sehen.

B. Eine eingegangene Anfrage beantworten

  1. Öffnen Sie auf dem empfangenden Node Inbox. Ungelesene Frames tragen einen gefüllten Punkt, und das Abzeichen in der Leiste zählt sie.
  2. Wählen Sie den Frame. Er gilt damit als gelesen und öffnet sich rechts.
  3. Lesen Sie ihn unter Payload; wer ihn signiert hat, sagt Ihnen das Badge in der Kopfzeile.
  4. Scrollen Sie zu Reply hinunter. Die Antwort ist aus der Anfrage vorbefüllt; füllen Sie die Teile aus, die Ihre Antwort tragen.
  5. Bestätigen Sie die Rückadresse und drücken Sie Sign & Send response.
  6. Auf dem ersten Node kommt die Antwort im Posteingang an, und seine Anfrage liest sich nun ↳ answered.

C. Aus no key ein signature ✓ machen

  1. Öffnen Sie Peers und wählen Sie den Peer, oder legen Sie ihn mit seinem host:port an.
  2. Geben Sie ihm einen Namen, den Sie in den Frame-Listen wiedererkennen, und drücken Sie Save.
  3. Drücken Sie Fetch from peer (oder fügen Sie das PEM vom Bildschirm Identity jenes Nodes ein) und drücken Sie erneut Save.
  4. Zurück in Inbox: Jeder Frame, den dieser Peer je gesendet hat, liest sich nun signature ✓. Die Prüfung wird über das ganze gespeicherte Log neu gerechnet, nicht nur über Neuankömmlinge.

D. Eine Nutzlast aufheben, die Sie wieder brauchen

  1. Stellen Sie die Anfrage zusammen, bis sie sagt, was sie sagen soll.
  2. Drücken Sie Save…, geben Sie einen Namen (lab ping, ac-start-berlin) und drücken Sie Save.
  3. Sie erscheint unter Load… als saved markiert, und auf der Platte unter records/<schema>/<name>.json.
  4. Records sind gewöhnliche Dateien. Kopieren Sie das Verzeichnis auf einen anderen Node, und die Bibliothek zieht mit.

E. Prüfen, was ein Peer mit einer fehlerhaften Nachricht macht

  1. Laden Sie einen Record und machen Sie ein Feld absichtlich kaputt: eine Id, die ihr Muster verfehlt, ein geleertes Pflichtfeld.
  2. Das Feld wird rot, und das Abzeichen zählt die Beanstandung. Senden Sie trotzdem.
  3. Auf dem empfangenden Node wird der Frame wie jeder andere festgehalten; was dessen Betreiber sieht, ist genau das, was auch Ihre Peer-Implementierung sähe.
  4. Ein nicht auswertbares Token wird ebenfalls festgehalten, als unverified markiert und mit 400 statt 202 beantwortet.

F. Einen Node umziehen oder ihm eine neue Identität geben

  1. Zum Umziehen halten Sie den Node an und kopieren sein ganzes Verzeichnis. Schlüssel, Zertifikat, Name, Log, Peers und Records reisen zusammen, er kommt also als derselbe Node zurück.
  2. Für eine neue Identität legen Sie Ihre tcpp-key.pem und tcpp-cert.pem hinein und starten neu.
  3. Damit eine frische erzeugt wird, löschen Sie beide Dateien und starten neu; die Node-Id wird aus dem neuen Schlüssel abgeleitet.
  4. Was daraus geworden ist, zeigt ./tcpp-node --print-identity; geben Sie das ausgegebene PEM den Peers, die Sie verifizieren.
Der Schlüssel ist der Node

tcpp-key.pem ist ein Geheimnis und nicht wiederherstellbar. Ein neuer Schlüssel bedeutet eine neue Schlüssel-Id und eine neue Node-Id, und jeder Peer, der Sie verifiziert, braucht den neuen öffentlichen Schlüssel.

Kommandozeile und Dateien

Flags

Es gibt keine Unterbefehle und keine Umgebungsvariablen; die Flags und die Dateien auf der Platte sind die gesamte Konfiguration. Sowohl -flag als auch --flag funktioniert.

FlagStandardBedeutung
--port8443TLS-Port, auf dem gelauscht wird.
--data-dirneben der BinärdateiWo Schlüssel, Zertifikat, Log, Peers und Records liegen.
--advertiselocalhost:<Port>Adresse, an die Peers antworten sollen, gesendet als X-TCPP-From.
--peerlocalhost:<Port>Standardziel, im Browser vorbelegt. Der Standard ist dieser Node selbst.
--node-typeCSU, oder der gespeicherte WertDrei Großbuchstaben. Nach dem ersten Start gespeichert.
--node-idaus dem Schlüssel abgeleitet3–120 Zeichen. Nach dem ersten Start gespeichert.
--hostskeineWeitere Hostnamen oder IPs für das Zertifikat, kommagetrennt.
--log-tail500Beim Start aus dem Log wiedereingespielte Frames.
--insecure-peerstrueSelbstsignierte Zertifikate der Peers annehmen. Mit --insecure-peers=false abschalten.
--print-identityfalseIdentität anlegen oder laden, ausgeben und beenden, ohne zu lauschen.

Das Zertifikat, das ein Node erzeugt, deckt immer localhost, 127.0.0.1, ::1 und den Hostnamen des Rechners ab; alles Weitere, worüber Peers ihn erreichen sollen, gehört in --hosts.

Ein Node je Verzeichnis

Zwei Kopien der Binärdatei, im selben Verzeichnis gestartet, teilten sich einen Schlüssel, ein Frame-Log und eine Record-Bibliothek, und die zweite bekäme den Port nicht. Geben Sie jedem Node ein eigenes Verzeichnis oder ein eigenes --data-dir.

--print-identity

$ ./tcpp-node --print-identity
node        CSU / P:NODE-74734B2A
alg         ES384
kid         74734b2a3dc9c8f9
fingerprint 48:81:2b:67:07:83:91:d7:ce:59:20:9a:83:0c:07:fa:ac:0f:da:f0:6a:da:02:6c:54:09:ca:b6:1d:1d:5e:54
key         C:\Users\User\tcpp-lab\a\tcpp-key.pem
cert        C:\Users\User\tcpp-lab\a\tcpp-cert.pem (valid to 2036-08-31T16:33:05Z)

-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEnfz9/7TBzZv7NCAdmkGv+KzYUVnfVeum
amuA/gGEdCStd2Tm7X1JJALLWPYN7dJ9LzNIx4qNPUIMiRyOKZkif28Zvl6iuRTr
+voCuitlwh+WBuGNBFiCSvFQcFbfjDpy
-----END PUBLIC KEY-----

Fehlt eine Identität, wird sie dabei angelegt: ein bequemer Weg, einen Node vor seinem ersten Start einzurichten.

Laufen und Anhalten

Der Node läuft im Vordergrund und schreibt seine Meldungen nach Standardfehler. Strg+C (oder SIGTERM) fährt ihn binnen fünf Sekunden geordnet herunter. Er endet mit 0 nach sauberem Herunterfahren und mit 1, nachdem er tcppnode: <Grund> ausgegeben hat, sei es ein bereits belegter Port, ein Datenverzeichnis, in das er nicht schreiben kann, oder ein Zertifikat, das nicht zu seinem Schlüssel passt.

Was zu sichern ist

tcpp-key.pem und tcpp-cert.pem sind die Identität und lassen sich nicht wiederherstellen. tcpp-node.json, tcpp-peers.json und records/ sind erhaltenswerte Konfiguration. tcpp-frames.jsonl ist Historie: sie wächst endlos und darf rotiert werden, wenn sie lästig wird; der Node liest immer nur ihr Ende.

HTTP-API

Die Browser-Anwendung ist ein gewöhnlicher Client der HTTP-Schnittstelle des Nodes; alles, was sie tut, lässt sich also skripten. Alles davon wird über TLS auf dem einen Port des Nodes ausgeliefert.

Keine Authentifizierung

Keine dieser Routen verlangt Zugangsdaten, und es gibt keine Origin-Prüfung. Wer den Node erreicht, hat ihn vollständig in der Hand.

Node zu Node

RouteBedeutung
POST /tcppDer Rumpf ist ein kompaktes JWS, Content-Type: application/jwt, bis 1 MB. Senden Sie X-TCPP-From, damit der Empfänger antworten kann, und X-TCPP-Node, um sich zu benennen. Antwortet 202 mit {"received":"<Frame-Id>","node":"<Bezeichnung>"}, oder 400, wenn sich das Token nicht lesen lässt; der Frame wird dann trotzdem festgehalten.

Browser

RouteLiefert
GET /Die Web-Anwendung, eingebettet in die Binärdatei.
GET /schema/1.3.0/{name}Ein eingebettetes Schema, als application/schema+json. Eine Endung .json wird akzeptiert.
GET /api/identityNode-Typ und -Id, Bezeichnung, angekündigte Adresse, Standard-Peer, öffentliches Schlüssel-PEM, kid, Fingerabdruck, Zertifikatsablauf, Dateipfade, Startzeit, Frame- und Ungelesen-Zähler.
GET /api/catalogJeden Endpunkt mit den Namen seiner Request- und Response-Schemas und seinen Beispielvarianten.
GET /api/example/{base}[/{variant}]Eine mitgelieferte Beispiel-Nutzlast. Ohne Variante bedeutet example.
POST /api/sendSigniert und stellt zu. Rumpf {"peer":"host:port","message":{…},"in_reply_to":"<Frame-Id>"}. Immer 200, mit ok, dem festgehaltenen frame, dem jwt und einem transport-Objekt; error steht dabei, wenn die Zustellung scheiterte.
GET /api/framesFrame-Liste samt Zählern je Peer und Gesamtzahlen. Filter: ?direction=sent|received, ?peer=host:port, ?limit=n.
GET /api/frames/{id}Einen Frame vollständig: Nutzlast, Token, dekodierter JOSE-Header und, wo es eines gibt, das Antwortschema.
POST /api/frames/{id}/read
POST /api/frames/read
Einen Frame oder alle als gelesen markieren.
POST /api/frames/{id}/verifyEine Signatur erneut prüfen. Ein optionaler Rumpf {"public_key_pem":"…"} prüft gegen einen von Ihnen gelieferten Schlüssel statt gegen den gespeicherten.
GET /api/peersBekannte Peers mit Name, Adresse, Schlüsselzustand, kid, Node-Id, zuletzt gesehen.
PUT /api/peersAnlegen oder ändern: {"name","address","public_key_pem","node_id","rename"}. Beim Speichern wird jeder gespeicherte Frame dieses Peers erneut geprüft.
DELETE /api/peers/{name}Einen Peer vergessen.
POST /api/peers/{name}/fetch-keyDie /api/identity des Peers lesen und seinen öffentlichen Schlüssel speichern.
GET /api/records/{schema}Die für ein Schema gespeicherten Records, jeder mit shipped, modified, size.
GET /api/records/{schema}/{name}Einen Record, Byte für Byte wie gespeichert.
PUT /api/records/{schema}/{name}Einen Record speichern. Der Rumpf ist die rohe JSON-Nutzlast, bis 1 MB.
DELETE /api/records/{schema}/{name}Einen Record entfernen.
GET /api/eventsServer-Sent Events: frame.received, frame.sent, frame.updated.

Einen Node aus der Shell steuern

Das Zertifikat des Nodes ist selbstsigniert, einem Client muss man also sagen, dass er es annehmen soll.

# die eigene Identität des Nodes
curl -k https://localhost:8443/api/identity

# einen gespeicherten Record an einen Peer senden
curl -k -X POST https://localhost:8443/api/send \
  -H 'content-type: application/json' \
  -d "{\"peer\":\"localhost:8444\",\"message\":$(curl -sk https://localhost:8443/api/records/ping_request/example)}"

# was angekommen ist
curl -k 'https://localhost:8443/api/frames?direction=received&limit=10'
# PowerShell
$node = 'https://localhost:8443'
$msg  = Invoke-RestMethod "$node/api/records/ping_request/example" -SkipCertificateCheck
Invoke-RestMethod "$node/api/send" -Method Post -SkipCertificateCheck `
  -ContentType 'application/json' `
  -Body (@{ peer = 'localhost:8444'; message = $msg } | ConvertTo-Json -Depth 20)
Nutzlast-Bytes werden nie neu kodiert

/api/send signiert die message genau so, wie Sie sie übergeben, Schlüsselreihenfolge eingeschlossen, und ein Empfänger liest sie wortwörtlich zurück. Das ist Absicht: Eine Signatur deckt Bytes ab, nicht ein abstraktes Objekt; nichts darf eine Nachricht zwischen Signieren und Prüfen neu serialisieren.

Die TCPP-Nachricht

TCPP ist das Protokoll, das diese Schemas definieren: ein JSON-Umschlag, der zwischen Modulen der Ladeinfrastruktur ausgetauscht wird. Die Version dieses Handbuchs ist 1.3.0, und jede Nachricht nennt sie in ihrem ersten Feld.

{
  "tcpp": "https://tcpp.digital/schema/1.3.0/",
  "head": {
    "record_type":       "ping",
    "record_direction":  "request",          request | response | internal
    "record_udt":        "2018-07-27T13:30:07.000Z",
    "record_pagination": 45330,              zählt aufwärts, von 1 bis endlos
    "record_version":    "1.3.0",
    "origin_type":       "CSU",              drei Buchstaben, das sendende Modul
    "origin_id":         "E:DE*PID*E*IWS*00100017",
    "target_type":       "TMH",
    "target_id":         "P:ECS-HOST",
    "context_type":      "INF",              der Vorgang, nicht das Modul
    "context_id":        "G:90676293-fa28-49bd-81ba-1b58a0f62abd",
    "error_code":        "000000"            hexadezimal; 000000 = kein Fehler
  },
  "record":       { "ping": "Check alive" }, Form hängt vom record_type ab
  "parsed":       { },                       nur Antworten: ein Fehlercode je Element
  "participants": [ ],                       steuert die ausgehende Kommunikation
  "references":   [ ]                        eine Antwort trägt hier den head der Anfrage
}

Ids tragen ihren Namensraum

Jede Id beginnt mit einem zwei Zeichen langen Präfix, das sagt, welche Art von Bezeichner folgt.

PräfixBedeutungPräfixBedeutung
E:EVSEID / EMAIDG:GUID
D:Domänen-NamensraumH:Hash
P:privater NamensraumN:numerische Paginierung
I:internC:Vertrags- / Tarif-Id
V:VIDX:undefiniert

Ein Node, der sich seine Id selbst erzeugt, verwendet den privaten Namensraum; deshalb heißen die Labor-Nodes P:NODE-9BED6853.

Modultypen

origin_type und target_type sind drei Großbuchstaben, die die Art des Moduls benennen: TMH Trusted Management Host, TEH Trusted Exchange Host, TAH Trusted Archive Host, MSP eMobility Service Provider, CPO Charge Point Operator, CSU Charge Switch Unit, EMU Energy Management Unit und so fort. Seinen eigenen kündigt ein Node mit --node-type an.

Wie eine Anfrage und ihre Antwort zueinanderfinden

Zu jeder Anfrage gehört genau eine Antwort. Der Antwortende kopiert den vollständigen head der Anfrage nach references[0], und beide Nodes berechnen daraus denselben Schlüssel: Record-Typ, Richtung, Zeitstempel, Paginierung, Herkunft und Ziel. So können die Frame-Listen auf beiden Seiten ↳ answered und ↳ answers an earlier request sagen, ohne dass ein Node eine Sitzung führen müsste. Antworten Sie aus dem Posteingang, übergibt der Node zusätzlich die Frame-Id ausdrücklich, damit die Zuordnung nicht davon abhängt, dass die Zeitstempel unversehrt bleiben.

Signieren

Ein ausgehender Frame ist ein kompaktes JWS über die Bytes der Nutzlast, mit dem Header {"alg":"ES384","kid":"…","typ":"JWT"}. Zwei Einzelheiten sind es wert, gekannt zu werden, weil interoperable Implementierungen üblicherweise genau dort scheitern:

  • R‖S in fester Breite. JWS will die ECDSA-Signatur als zwei 48-Byte- Hälften (IEEE P1363), genau das, was die Web Crypto des Browsers erzeugt. Gos ecdsa.SignASN1 erzeugt stattdessen DER und darf nicht verwendet werden; etwa jede 256. Signatur hat eine kurze Koordinate, die links aufgefüllt werden muss.
  • Die Nutzlast wird so signiert, wie sie übergeben wurde. JSON.stringify erhält die Einfügereihenfolge der Schlüssel, während Gos encoding/json die Schlüssel von Maps sortiert; eine Nachricht wird zwischen Signieren und Prüfen deshalb nie neu serialisiert.

Anhang

Die 27 Endpunkte

Jeder hat ein Request- und ein Response-Schema, ausgeliefert unter /schema/1.3.0/<base>_request.json und …_response.json. Manche bringen mehr als ein Beispiel mit, und jedes Beispiel wird zu einem Record, den Sie laden können.

EndpunktWeitere Beispielvarianten
archive_get, archive_listkeine
charge_eventRequest: example_with_charge_end
charge_process, charge_startkeine
display_input, display_outputkeine
emaid_getRequest: example_via_vehicle
errorRequest: example_hardware_internal, example_hardware_request
key_getkeine
measurement_change, measurement_configuration, measurement_createkeine
module_getkeine
parameters_delete, parameters_get, parameters_setkeine
parameters_listResponse: example_measurement
pingResponse: example_with_participants, example_with_reference_jws
statuskeine
tariffs_listRequest: example_cp_in_location, example_cp_search
trigger_delete, trigger_get, trigger_set, triggers_listkeine
verification_get, verification_setkeine

Glossar

Frame
Eine TCPP-Nachricht, wie dieser Node sie gesehen hat: Token, dekodierte Nutzlast, Richtung, Peer, Prüfzustand, Transportergebnis und Zuordnungen.
Peer
Ein anderer Node: ein Name, eine Adresse und wahlweise ein öffentlicher Schlüssel.
Record
Eine gespeicherte Nutzlast in der Bibliothek des Nodes, unter records/<schema>/<name>.json.
Entwurf
Ungesendeter Formularinhalt, im Browser-Tab gehalten. Je Schema im Compose-Formular, je Frame im Antwortformular.
Ziel
Das host:port, an das ein Frame gesendet wird.
kid
Schlüssel-Id, aus dem öffentlichen Schlüssel abgeleitet und im Header jedes Tokens mitgeführt.
JWS / JWT
Die kompakte signierte Form, in der ein Frame reist. Hier immer ES384.

Über die Abbildungen

Jede Abbildung in diesem Handbuch stammt aus einem laufenden Zwei-Node-Labor, keine ist gezeichnet; was Sie hier sehen, ist also, was die Anwendung tut. Aufgenommen werden sie von einem Skript, das neben der Quelle des Handbuchs liegt und einen Browser ohne Anzeige durch dieselben Schritte führt, die ein Bediener geht; sie werden neu aufgenommen, wann immer sich die Oberfläche ändert. Die Anwendung ist englisch, deshalb zeigen auch die Abbildungen dieser Ausgabe die englische Oberfläche.